Version 1.5.44

Cherry pick: Reland "Synthesize null-throwing body for abstract methods on non-abstract classes"
CL: https://r8-review.googlesource.com/c/r8/+/39220

Cherry pick: Synthesize throwing body when merging abstract method to non-abstract class
CL: https://r8-review.googlesource.com/c/r8/+/39222

Cherry pick: Disable value propagation for constructors
CL: https://r8-review.googlesource.com/c/r8/+/39223

Cherry pick: Remove redundant debug positions before numbering instructions
CL: https://r8-review.googlesource.com/c/r8/+/39300

Cherry-pick: Fix arg type of null in outline candidates.
CL: https://r8-review.googlesource.com/c/r8/+/39200

Bug: 132953944, 134462736
Change-Id: Ief4886f697b22c91765a1175b7cc39c235f358c6
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5c0e9f7..daf3d43 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -718,7 +718,7 @@
       }
 
       // Validity checks.
-      assert application.classes().stream().allMatch(DexClass::isValid);
+      assert application.classes().stream().allMatch(clazz -> clazz.isValid(options));
       assert appView.rootSet().verifyKeptItemsAreKept(application, appView.appInfo());
       assert appView
           .graphLense()
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 668e101..1041a82 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.5.43";
+  public static final String LABEL = "1.5.44";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 3ef485f..df86e96 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -600,8 +600,12 @@
     return fields;
   }
 
-  private DexEncodedMethod[] readMethods(int size, DexMethodAnnotation[] annotations,
-      DexParameterAnnotation[] parameters, boolean skipCodes) {
+  private DexEncodedMethod[] readMethods(
+      int size,
+      DexMethodAnnotation[] annotations,
+      DexParameterAnnotation[] parameters,
+      boolean skipCodes,
+      boolean ensureNonAbstract) {
     DexEncodedMethod[] methods = new DexEncodedMethod[size];
     int methodIndex = 0;
     MemberAnnotationIterator<DexMethod, DexAnnotationSet> annotationIterator =
@@ -618,8 +622,21 @@
         code = codes.get(codeOff);
       }
       DexMethod method = indexedItems.getMethod(methodIndex);
-      methods[i] = new DexEncodedMethod(method, accessFlags, annotationIterator.getNextFor(method),
-          parameterAnnotationsIterator.getNextFor(method), code);
+      DexEncodedMethod encodedMethod =
+          new DexEncodedMethod(
+              method,
+              accessFlags,
+              annotationIterator.getNextFor(method),
+              parameterAnnotationsIterator.getNextFor(method),
+              code);
+      if (accessFlags.isAbstract() && ensureNonAbstract) {
+        accessFlags.unsetAbstract();
+        encodedMethod =
+            options.isGeneratingClassFiles()
+                ? encodedMethod.toEmptyThrowingMethodCf()
+                : encodedMethod.toEmptyThrowingMethodDex();
+      }
+      methods[i] = encodedMethod;
     }
     return methods;
   }
@@ -692,13 +709,17 @@
                 directMethodsSize,
                 annotationsDirectory.methods,
                 annotationsDirectory.parameters,
-                classKind != ClassKind.PROGRAM);
+                classKind != ClassKind.PROGRAM,
+                options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()
+                    && !flags.isAbstract());
         virtualMethods =
             readMethods(
                 virtualMethodsSize,
                 annotationsDirectory.methods,
                 annotationsDirectory.parameters,
-                classKind != ClassKind.PROGRAM);
+                classKind != ClassKind.PROGRAM,
+                options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()
+                    && !flags.isAbstract());
       }
 
       AttributesAndAnnotations attrs =
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 a301563..b62bcaa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -262,6 +262,19 @@
     return true;
   }
 
+  private boolean verifyNoAbstractMethodsOnNonAbstractClasses(
+      Iterable<DexEncodedMethod> methods, InternalOptions options) {
+    if (options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()) {
+      if (!isAbstract()) {
+        for (DexEncodedMethod method : methods) {
+          assert !method.isAbstract()
+              : "Non-abstract method on abstract class: `" + method.method.toSourceString() + "`";
+        }
+      }
+    }
+    return true;
+  }
+
   private boolean verifyNoDuplicateMethods() {
     Set<DexMethod> unique = Sets.newIdentityHashSet();
     for (DexEncodedMethod method : methods()) {
@@ -572,7 +585,10 @@
     return null;
   }
 
-  // Tells whether this is an interface.
+  public boolean isAbstract() {
+    return accessFlags.isAbstract();
+  }
+
   public boolean isInterface() {
     return accessFlags.isInterface();
   }
@@ -847,9 +863,9 @@
     return getKotlinInfo() != null;
   }
 
-  public boolean isValid() {
-    assert !isInterface()
-        || Arrays.stream(virtualMethods).noneMatch(method -> method.accessFlags.isFinal());
+  public boolean isValid(InternalOptions options) {
+    assert verifyNoAbstractMethodsOnNonAbstractClasses(virtualMethods(), options);
+    assert !isInterface() || virtualMethods().stream().noneMatch(DexEncodedMethod::isFinal);
     assert verifyCorrectnessOfFieldHolders(fields());
     assert verifyNoDuplicateFields();
     assert verifyCorrectnessOfMethodHolders(methods());
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 97fd72c..d2f8e2d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -227,6 +227,14 @@
     return compilationState != CompilationState.NOT_PROCESSED;
   }
 
+  public boolean isAbstract() {
+    return accessFlags.isAbstract();
+  }
+
+  public boolean isFinal() {
+    return accessFlags.isFinal();
+  }
+
   public boolean isInitializer() {
     checkIfObsolete();
     return isInstanceInitializer() || isClassInitializer();
@@ -566,6 +574,12 @@
     return generateCodeFromTemplate(1, 0, insn);
   }
 
+  public DexEncodedMethod toEmptyThrowingMethod(InternalOptions options) {
+    return options.isGeneratingClassFiles()
+        ? toEmptyThrowingMethodCf()
+        : toEmptyThrowingMethodDex();
+  }
+
   public DexEncodedMethod toEmptyThrowingMethodDex() {
     checkIfObsolete();
     assert !shouldNotHaveCode();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index a6e556d..7803adc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -182,13 +182,13 @@
       // large for the if encoding.
       rewriteIfs();
 
-      // Reset the state of the builder to start from scratch.
-      reset();
-
       // Remove redundant debug position instructions. They would otherwise materialize as
       // unnecessary nops.
       removeRedundantDebugPositions(ir);
 
+      // Reset the state of the builder to start from scratch.
+      reset();
+
       // Populate the builder info objects.
       numberOfInstructions = 0;
 
@@ -667,10 +667,12 @@
 
   // Helper used by the info objects.
   private Info getInfo(com.android.tools.r8.ir.code.Instruction instruction) {
+    assert instruction.getNumber() >= 0;
     return instructionToInfo[instructionNumberToIndex(instruction.getNumber())];
   }
 
   private void setInfo(com.android.tools.r8.ir.code.Instruction instruction, Info info) {
+    assert instruction.getNumber() >= 0;
     if (!(info instanceof FallThroughInfo)) {
       previousNonFallthroughInfo = info;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 615b4c1..3e5ff2f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -219,6 +219,13 @@
       return;
     }
     DexEncodedMethod definition = current.lookupSingleTarget(appView, callingContext);
+    if (definition != null && definition.isInstanceInitializer()) {
+      // Member value propagation does not apply to constructors. Removing a call to a constructor
+      // that is marked as having no side effects could lead to verification errors, due to
+      // uninitialized instances being used.
+      return;
+    }
+
     ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
     if (lookup == null) {
       // Since -assumenosideeffects rules are always applied to all overriding methods, we can
@@ -233,8 +240,10 @@
       if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS && outValueNullOrNotUsed) {
         // Remove invoke if marked as having no side effects and the return value is not used.
         iterator.removeOrReplaceByDebugLocalRead();
-        invokeReplaced = true;
-      } else if (!outValueNullOrNotUsed) {
+        return;
+      }
+
+      if (!outValueNullOrNotUsed) {
         // Check to see if a constant value can be assumed.
         invokeReplaced =
             tryConstantReplacementFromProguard(
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 81236f8..33c8c96 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
@@ -956,7 +956,7 @@
         // For values of lattice type java.lang.Object and only one interface use the interface as
         // the type of the outline argument. If there are several interfaces these interfaces don't
         // have a common super interface nor are they implemented by a common superclass so the
-        // argument type of the ouline will be java.lang.Object.
+        // argument type of the outline will be java.lang.Object.
         if (valueClassTypeLatticeElement.getClassType() == objectType
             && valueClassTypeLatticeElement.getInterfaces().size() == 1) {
           return valueClassTypeLatticeElement.getInterfaces().iterator().next();
@@ -966,8 +966,8 @@
       } else if (valueLatticeElement.isArrayType()) {
         return value.getTypeLattice().asArrayTypeLatticeElement().getArrayType(itemFactory);
       } else if (valueLatticeElement.isNullType()) {
-        // For values which are always null use java.lang.Object as the outline argument type.
-        return objectType;
+        // For values which are always null use the actual type at the call site.
+        return argumentTypeFromInvoke(invoke, argumentIndex);
       } else {
         assert valueLatticeElement.isPrimitive();
         assert valueLatticeElement.asPrimitiveTypeLatticeElement().hasDexType();
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 25c61d8..0bc0284 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -925,6 +925,12 @@
             // Update the holder of [virtualMethod] using renameMethod().
             DexEncodedMethod resultingVirtualMethod =
                 renameMethod(virtualMethod, availableMethodSignatures, Rename.NEVER);
+            if (appView.options().canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()
+                && !target.isAbstract()) {
+              resultingVirtualMethod.accessFlags.unsetAbstract();
+              resultingVirtualMethod =
+                  resultingVirtualMethod.toEmptyThrowingMethod(appView.options());
+            }
             deferredRenamings.map(virtualMethod.method, resultingVirtualMethod.method);
             deferredRenamings.recordMove(virtualMethod.method, resultingVirtualMethod.method);
             add(virtualMethods, resultingVirtualMethod, MethodSignatureEquivalence.get());
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 5901ee9..951feef 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1106,4 +1106,12 @@
   public boolean canHaveDalvikCatchHandlerVerificationBug() {
     return minApiLevel < AndroidApiLevel.L.getLevel();
   }
+
+  // Having an invoke instruction that targets an abstract method on a non-abstract class will fail
+  // with a verification error.
+  //
+  // See b/132953944.
+  public boolean canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() {
+    return minApiLevel < AndroidApiLevel.L.getLevel();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 3dfac08..148f1b1 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -118,6 +118,8 @@
   private static Map<String, AndroidApiLevel> needMinSdkVersion =
       new ImmutableMap.Builder<String, AndroidApiLevel>()
           .put("004-JniTest", AndroidApiLevel.N)
+          // Has a non-abstract class with an abstract method.
+          .put("800-smali", AndroidApiLevel.L)
           // Android O
           .put("952-invoke-custom", AndroidApiLevel.O)
           .put("952-invoke-custom-kinds", AndroidApiLevel.O)
@@ -1071,13 +1073,20 @@
           // When running R8 on dex input (D8, DX) this test non-deterministically fails
           // with a compiler exception, due to invoke-virtual on an interface, or it completes but
           // the output when run on Art is not as expected. b/65233869
-          .put("162-method-resolution",
+          .put(
+              "162-method-resolution",
               TestCondition.match(
-                  TestCondition.tools(DexTool.DX, DexTool.NONE),
-                  TestCondition.R8_COMPILER))
+                  TestCondition.tools(DexTool.DX, DexTool.NONE), TestCondition.R8_COMPILER))
           // Produces wrong output when compiled in release mode, which we cannot express.
-          .put("015-switch",
-              TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_0_4)))
+          .put("015-switch", TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_0_4)))
+          // To prevent "Dex file with version '37' cannot be used with min sdk level '21'", which
+          // would otherwise happen because D8 passes through the DEX code from DX.
+          .put(
+              "800-smali",
+              TestCondition.match(
+                  TestCondition.tools(DexTool.DX),
+                  TestCondition.D8_COMPILER,
+                  TestCondition.runtimesFrom(DexVm.Version.V5_1_1)))
           .build();
 
   public static List<String> requireInliningToBeDisabled =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NoConstructorPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NoConstructorPropagationTest.java
new file mode 100644
index 0000000..904342a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NoConstructorPropagationTest.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2019, 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.ir.optimize.membervaluepropagation;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NoConstructorPropagationTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().build();
+  }
+
+  public NoConstructorPropagationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(NoConstructorPropagationTest.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules(
+            "-assumenosideeffects class " + Greeter.class.getTypeName() + " {",
+            "  <init>(...);",
+            "}")
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getRuntime())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new Greeter().greet();
+    }
+  }
+
+  @NeverClassInline
+  static class Greeter {
+
+    @NeverInline
+    public void greet() {
+      System.out.println("Hello world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
new file mode 100644
index 0000000..177bc91
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2019, 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.ir.optimize.outliner.classtypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B134462736 extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().build();
+  }
+
+  public B134462736(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private void validateOutlining(CodeInspector inspector) {
+    ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+    MethodSubject outline0Method =
+        outlineClass.method(
+            "void",
+            "outline0",
+            ImmutableList.of(
+                StringBuilder.class.getTypeName(),
+                String.class.getTypeName(),
+                String.class.getTypeName(),
+                TestClass.class.getTypeName(),
+                String.class.getTypeName()));
+    assertThat(outline0Method, isPresent());
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+    assertThat(
+        classSubject.uniqueMethodWithName("method1"), CodeMatchers.invokesMethod(outline0Method));
+    assertThat(
+        classSubject.uniqueMethodWithName("method2"), CodeMatchers.invokesMethod(outline0Method));
+  }
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("12 null", "12 null");
+    testForR8(parameters.getBackend())
+        .enableInliningAnnotations()
+        .addInnerClasses(B134462736.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getRuntime())
+        .noMinification()
+        .addOptionsModification(
+            options -> {
+              options.outline.threshold = 2;
+              options.outline.minSize = 2;
+            })
+        .compile()
+        .inspect(this::validateOutlining)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(expectedOutput);
+  }
+
+  public static class TestClass {
+    @NeverInline
+    public void consumer(String arg1, String arg2) {
+      System.out.println(arg1 + " " + arg2);
+    }
+
+    @NeverInline
+    public void method1(StringBuilder builder, String arg1, String arg2) {
+      builder.append(arg1);
+      builder.append(arg2);
+      consumer(builder.toString(), null);
+    }
+
+    @NeverInline
+    public void method2(StringBuilder builder, String arg1, String arg2) {
+      builder.append(arg1);
+      builder.append(arg2);
+      consumer(builder.toString(), null);
+    }
+
+    public static void main(String[] args) {
+      TestClass instance = new TestClass();
+      instance.method1(new StringBuilder(), "1", "2");
+      instance.method2(new StringBuilder(), "1", "2");
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
index dc980d7..e9368e9 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -85,13 +85,23 @@
 
   public class ClassBuilder extends Builder {
 
+    private boolean isAbstract = false;
+
     ClassBuilder(String name, String superName, List<String> implementedInterfaces) {
       super(name, superName, implementedInterfaces);
     }
 
+    public ClassBuilder setAbstract() {
+      isAbstract = true;
+      return this;
+    }
+
     public String toString() {
       StringBuilder builder = new StringBuilder();
       builder.append(".class public ");
+      if (isAbstract) {
+        builder.append("abstract ");
+      }
       builder.append(DescriptorUtils.javaTypeToDescriptor(name));
       builder.append("\n");
       appendSuper(builder);
@@ -164,10 +174,12 @@
     addClass(name, superName, ImmutableList.of());
   }
 
-  public void addClass(String name, String superName, List<String> implementedInterfaces) {
+  public ClassBuilder addClass(String name, String superName, List<String> implementedInterfaces) {
     assert !classes.containsKey(name);
     currentClassName = name;
-    classes.put(name, new ClassBuilder(name, superName, implementedInterfaces));
+    ClassBuilder classBuilder = new ClassBuilder(name, superName, implementedInterfaces);
+    classes.put(name, classBuilder);
+    return classBuilder;
   }
 
   public void addInterface(String name) {
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
index 2297ebd..bbeed31 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -380,20 +380,20 @@
   @Test
   public void implementsInterface() {
     SmaliBuilder builder = new SmaliBuilder();
-    builder.addClass("Test", "java.lang.Object", ImmutableList.of("java.util.List"));
+    builder.addClass("Test", "java.lang.Object", ImmutableList.of("java.util.List")).setAbstract();
     builder.addAbstractMethod("int", "test", ImmutableList.of());
     AndroidApp application = buildApplication(builder);
 
     String expected =
-        ".class public LTest;\n" +
-            "\n" +
-            ".super Ljava/lang/Object;\n" +
-            ".implements Ljava/util/List;\n" +
-            "\n" +
-            ".method public abstract test()I\n" +
-            ".end method\n" +
-            "\n" +
-            "# End of class LTest;\n";
+        ".class public abstract LTest;\n"
+            + "\n"
+            + ".super Ljava/lang/Object;\n"
+            + ".implements Ljava/util/List;\n"
+            + "\n"
+            + ".method public abstract test()I\n"
+            + ".end method\n"
+            + "\n"
+            + "# End of class LTest;\n";
 
     assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));