Output fields in smali disassembler

Change-Id: If3a4a2a77596a7981919a38bc750f9f0c048b7af
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index 7c4ac49..6f68e5b 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -243,7 +243,7 @@
           new ApplicationReader(app, options, timing).read(command.proguardMap, executor);
       DexByteCodeWriter writer =
           command.useSmali()
-              ? new SmaliWriter(application, options)
+              ? new SmaliWriter(application, options, !command.noCode())
               : new AssemblyWriter(
                   application, options, command.allInfo, command.useIr(), !command.noCode());
       if (command.getOutputPath() != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 4ed66d8..a583683 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -116,10 +116,19 @@
   }
 
   @Override
-  void writeFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+  void writeInstanceFieldsHeader(DexProgramClass clazz, PrintStream ps) {
     if (writeFields) {
       ps.println("#");
-      ps.println("# Fields:");
+      ps.println("# Instance fields:");
+      ps.println("#");
+    }
+  }
+
+  @Override
+  void writeStaticFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+    if (writeFields) {
+      ps.println("#");
+      ps.println("# Static fields:");
       ps.println("#");
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
index 60ab622..8cb22f1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -89,8 +89,10 @@
 
   private void writeClass(DexProgramClass clazz, PrintStream ps) {
     writeClassHeader(clazz, ps);
-    writeFieldsHeader(clazz, ps);
-    clazz.forEachField(field -> writeField(field, ps));
+    writeStaticFieldsHeader(clazz, ps);
+    clazz.forEachFieldMatching(DexEncodedMember::isStatic, field -> writeField(field, ps));
+    writeInstanceFieldsHeader(clazz, ps);
+    clazz.forEachFieldMatching(DexEncodedMember::isInstance, field -> writeField(field, ps));
     writeFieldsFooter(clazz, ps);
     writeMethodsHeader(clazz, ps);
     clazz.forEachProgramMethod(method -> writeMethod(method, ps));
@@ -102,7 +104,11 @@
 
   abstract void writeClassHeader(DexProgramClass clazz, PrintStream ps);
 
-  void writeFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+  void writeInstanceFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+    // Do nothing.
+  }
+
+  void writeStaticFieldsHeader(DexProgramClass clazz, PrintStream ps) {
     // Do nothing.
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index ada1c8a..5cf7530 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -20,8 +20,11 @@
 import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -166,7 +169,29 @@
 
   @Override
   public String toSmaliString() {
-    return getReference().toSmaliString();
+    StringBuilder builder = new StringBuilder();
+    builder.append(".field ");
+    builder.append(accessFlags.toSmaliString());
+    builder.append(" ");
+    builder.append(getName().toSmaliString());
+    builder.append(":");
+    builder.append(getType().toSmaliString());
+    return builder.toString();
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    FieldSignature fieldSignature =
+        naming != null
+            ? naming.originalSignatureOf(getReference())
+            : FieldSignature.fromDexField(getReference());
+    StringBuilder builder = new StringBuilder();
+    builder.append(".field ");
+    builder.append(accessFlags.toSmaliString());
+    builder.append(" ");
+    builder.append(fieldSignature.getName());
+    builder.append(":");
+    builder.append(DescriptorUtils.javaTypeToDescriptor(fieldSignature.getTypeName()));
+    return builder.toString();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index e1bb25e..b68c45b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -57,6 +57,10 @@
     return this;
   }
 
+  public final boolean isInstance() {
+    return !isStatic();
+  }
+
   public final boolean isPrivate() {
     return getAccessFlags().isPrivate();
   }
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 6bd54aa..ff92837 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -594,10 +594,6 @@
     return (accessFlags.isPrivate() || accessFlags.isConstructor()) && !accessFlags.isStatic();
   }
 
-  public boolean isInstance() {
-    return !isStatic();
-  }
-
   @Override
   public boolean isStatic() {
     checkIfObsolete();
@@ -896,6 +892,10 @@
   }
 
   public String toSmaliString(ClassNameMapper naming) {
+    return toSmaliString(naming, true);
+  }
+
+  public String toSmaliString(ClassNameMapper naming, boolean writeCode) {
     checkIfObsolete();
     StringBuilder builder = new StringBuilder();
     builder.append(".method ");
@@ -904,7 +904,7 @@
     builder.append(getReference().name.toSmaliString());
     builder.append(getReference().proto.toSmaliString());
     builder.append("\n");
-    if (code != null) {
+    if (writeCode && hasCode()) {
       DexCode dexCode = code.asDexCode();
       builder.append("    .registers ");
       builder.append(dexCode.registerSize);
diff --git a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
index e676697..f98ef60 100644
--- a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
@@ -16,9 +16,11 @@
 
 public class SmaliWriter extends DexByteCodeWriter {
 
-  public SmaliWriter(DexApplication application,
-      InternalOptions options) {
+  private final boolean writeCode;
+
+  public SmaliWriter(DexApplication application, InternalOptions options, boolean writeCode) {
     super(application, options);
+    this.writeCode = writeCode;
   }
 
   /** Return smali source for the application code. */
@@ -27,7 +29,7 @@
     try (PrintStream ps = new PrintStream(os)) {
       DexApplication dexApplication =
           new ApplicationReader(application, options, Timing.empty()).read();
-      SmaliWriter writer = new SmaliWriter(dexApplication, options);
+      SmaliWriter writer = new SmaliWriter(dexApplication, options, true);
       writer.write(ps);
     } catch (IOException e) {
       throw new CompilationError("Failed to generate smali sting", e);
@@ -46,17 +48,25 @@
     ps.append(clazz.accessFlags.toSmaliString());
     ps.append(" ");
     ps.append(clazz.type.toSmaliString());
-    ps.append("\n\n");
+    ps.append('\n');
+
     if (clazz.type != application.dexItemFactory.objectType) {
       ps.append(".super ");
       ps.append(clazz.superType.toSmaliString());
-      ps.append("\n");
+      ps.append('\n');
+    }
+    ps.append('\n');
+
+    if (!clazz.getInterfaces().isEmpty()) {
+      ps.append("# interfaces").append('\n');
       for (DexType iface : clazz.interfaces.values) {
         ps.append(".implements ");
         ps.append(iface.toSmaliString());
-        ps.append("\n");
+        ps.append('\n');
       }
+      ps.append('\n');
     }
+    ps.append('\n');
   }
 
   @Override
@@ -64,17 +74,35 @@
     ps.append("# End of class ");
     ps.append(clazz.type.toSmaliString());
     ps.append("\n");
+    ps.append("\n");
   }
 
   @Override
   void writeMethod(ProgramMethod method, PrintStream ps) {
     ps.append("\n");
-    ps.append(method.getDefinition().toSmaliString(application.getProguardMap()));
+    ps.append(method.getDefinition().toSmaliString(application.getProguardMap(), writeCode));
     ps.append("\n");
   }
 
   @Override
+  void writeInstanceFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+    if (clazz.hasStaticFields()) {
+      ps.append('\n');
+    }
+    if (clazz.hasInstanceFields()) {
+      ps.append("# instance fields").append('\n');
+    }
+  }
+
+  @Override
+  void writeStaticFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+    if (clazz.hasStaticFields()) {
+      ps.append("# static fields").append('\n');
+    }
+  }
+
+  @Override
   void writeField(DexEncodedField field, PrintStream ps) {
-    // Not yet implemented.
+    ps.append(field.toSmaliString(application.getProguardMap())).append('\n').append('\n');
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index bf0a8dd..1d024b3 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -187,6 +187,14 @@
           field.type.toSourceString());
     }
 
+    public String getName() {
+      return name;
+    }
+
+    public String getTypeName() {
+      return type;
+    }
+
     public DexField toDexField(DexItemFactory factory, DexType clazz) {
       return factory.createField(
           clazz,