Trim <clinit> some more

* Move 0/null to default value as well
* Remove empty <clinit> methods
* Calculate fixed results for getting the name of the class itself inside <clinit>

Change-Id: I4cc5ed873af51dafcba6fdcf02b64027942e73a0
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 d09d395..0e64e9d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -187,6 +187,14 @@
     return getClassInitializer() != null;
   }
 
+  public boolean hasTrivialClassInitializer() {
+    DexEncodedMethod clinit = getClassInitializer();
+    return clinit != null
+        && clinit.getCode() != null
+        && clinit.getCode().asDexCode().isEmptyVoidMethod();
+  }
+
+
   public boolean hasNonTrivialClassInitializer() {
     DexEncodedMethod clinit = getClassInitializer();
     if (clinit == null || clinit.getCode() == null) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 339f4c0..c41113d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -85,6 +85,8 @@
   public final DexString getClassMethodName = createString("getClass");
   public final DexString ordinalMethodName = createString("ordinal");
   public final DexString desiredAssertionStatusMethodName = createString("desiredAssertionStatus");
+  public final DexString getNameName = createString("getName");
+  public final DexString getSimpleNameName = createString("getSimpleName");
   public final DexString assertionsDisabled = createString("$assertionsDisabled");
 
   public final DexString stringDescriptor = createString("Ljava/lang/String;");
@@ -208,10 +210,15 @@
   public class ClassMethods {
 
     public DexMethod desiredAssertionStatus;
+    public DexMethod getName;
+    public DexMethod getSimpleName;
 
     private ClassMethods() {
       desiredAssertionStatus = createMethod(classDescriptor,
           desiredAssertionStatusMethodName, booleanDescriptor, DexString.EMPTY_ARRAY);
+      getName = createMethod(classDescriptor, getNameName, stringDescriptor, DexString.EMPTY_ARRAY);
+      getSimpleName = createMethod(classDescriptor,
+          getSimpleNameName, stringDescriptor, DexString.EMPTY_ARRAY);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 60a7319..1bdc56c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -152,6 +152,18 @@
     directMethods[directMethods.length - 1] = staticMethod;
   }
 
+  public void removeStaticMethod(DexEncodedMethod staticMethod) {
+    assert staticMethod.accessFlags.isStatic();
+    DexEncodedMethod[] newDirectMethods = new DexEncodedMethod[directMethods.length - 1];
+    int toIndex = 0;
+    for (int fromIndex = 0; fromIndex < directMethods.length; fromIndex++) {
+      if (directMethods[fromIndex] != staticMethod) {
+        newDirectMethods[toIndex++] = directMethods[fromIndex];
+      }
+    }
+    directMethods = newDirectMethods;
+  }
+
   @Override
   public DexProgramClass get() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 2ffb5a1..bf93530 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -426,6 +426,10 @@
     return isConstant() && getConstInstruction().isConstNumber();
   }
 
+  public boolean isConstString() {
+    return isConstant() && getConstInstruction().isConstString();
+  }
+
   public boolean isConstant() {
     return definition.isOutConstant() && getLocalInfo() == null;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 3cd40f7..165a014 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -238,7 +238,11 @@
     for (DexProgramClass clazz : classes) {
       futures.add(executor.submit(() -> clazz.forEachMethod(this::convertMethodToDex)));
     }
+
     ThreadUtils.awaitFutures(futures);
+
+    // Get rid of <clinit> methods with no code.
+    removeEmptyClassInitializers();
   }
 
   private void convertMethodToDex(DexEncodedMethod method) {
@@ -284,6 +288,9 @@
     }, executorService);
     timing.end();
 
+    // Get rid of <clinit> methods with no code.
+    removeEmptyClassInitializers();
+
     // Build a new application with jumbo string info.
     Builder builder = new Builder(application);
     builder.setHighestSortingString(highestSortingString);
@@ -325,6 +332,16 @@
     return builder.build();
   }
 
+  private void removeEmptyClassInitializers() {
+    application.classes().forEach(this::removeEmptyClassInitializer);
+  }
+
+  private void removeEmptyClassInitializer(DexProgramClass clazz) {
+    if (clazz.hasTrivialClassInitializer()) {
+      clazz.removeStaticMethod(clazz.getClassInitializer());
+    }
+  }
+
   private void clearDexMethodCompilationState() {
     application.classes().forEach(this::clearDexMethodCompilationState);
   }
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 eab1dfa..92e7580 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
@@ -619,12 +619,31 @@
     }
   }
 
+  // Check if the static put is a constant derived from the class holding the method.
+  // This checks for java.lang.Class.getName and java.lang.Class.getSimpleName.
+  private boolean isClassNameConstant(DexEncodedMethod method, StaticPut put) {
+    if (put.getField().type != dexItemFactory.stringType) {
+      return false;
+    }
+    if (put.inValue().definition != null && put.inValue().definition.isInvokeVirtual()) {
+      InvokeVirtual invoke = put.inValue().definition.asInvokeVirtual();
+      if ((invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName
+          || invoke.getInvokedMethod() == dexItemFactory.classMethods.getName)
+          && invoke.inValues().get(0).definition.isConstClass()
+          && invoke.inValues().get(0).definition.asConstClass().getValue()
+          == method.method.getHolder()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public void collectClassInitializerDefaults(DexEncodedMethod method, IRCode code) {
     if (!method.isClassInitializer()) {
       return;
     }
 
-    // Collect all static put which are dominated by the exit block, and not dominated by a
+    // Collect relevant static put which are dominated by the exit block, and not dominated by a
     // static get.
     // This does not check for instructions that can throw. However, as classes which throw in the
     // class initializer are never visible to the program (see Java Virtual Machine Specification
@@ -644,14 +663,21 @@
         if (current.isStaticPut()) {
           StaticPut put = current.asStaticPut();
           DexField field = put.getField();
-          if (!(field.type.isPrimitiveType()
-              || field.type == dexItemFactory.stringType)
-              || field.getHolder() != method.method.getHolder()) {
-            continue;
-          }
-          if (put.inValue().isConstant()) {
-            // Collect put as a potential default value.
-            puts.put(put.getField(), put);
+          if (field.getHolder() == method.method.getHolder()) {
+            if (put.inValue().isConstant()) {
+              if ((field.type.isClassType() || field.type.isArrayType())
+                  && put.inValue().getConstInstruction().isConstNumber() &&
+                  put.inValue().getConstInstruction().asConstNumber().isZero()) {
+                // Collect put of zero as a potential default value.
+                puts.put(put.getField(), put);
+              } else if (field.type.isPrimitiveType() || field.type == dexItemFactory.stringType) {
+                // Collect put as a potential default value.
+                puts.put(put.getField(), put);
+              }
+            } else if (isClassNameConstant(method, put)) {
+              // Collect put of class name constant as a potential default value.
+              puts.put(put.getField(), put);
+            }
           }
         }
         if (current.isStaticGet()) {
@@ -670,12 +696,32 @@
         DexField field = put.getField();
         DexEncodedField encodedField = appInfo.definitionFor(field);
         if (field.type == dexItemFactory.stringType) {
-          if (put.inValue().getConstInstruction().isConstNumber()) {
-            assert put.inValue().getConstInstruction().asConstNumber().isZero();
+          if (put.inValue().isConstant()) {
+            if (put.inValue().getConstInstruction().isConstNumber()) {
+              assert put.inValue().getConstInstruction().asConstNumber().isZero();
+              encodedField.staticValue = DexValueNull.NULL;
+            } else {
+              ConstString cnst = put.inValue().getConstInstruction().asConstString();
+              encodedField.staticValue = new DexValueString(cnst.getValue());
+            }
+          } else {
+            InvokeVirtual invoke = put.inValue().definition.asInvokeVirtual();
+            String name = method.method.getHolder().toSourceString();
+            if (invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName) {
+              String simpleName = name.substring(name.lastIndexOf('.') + 1);
+              encodedField.staticValue =
+                  new DexValueString(dexItemFactory.createString(simpleName));
+            } else {
+              assert invoke.getInvokedMethod() == dexItemFactory.classMethods.getName;
+              encodedField.staticValue = new DexValueString(dexItemFactory.createString(name));
+            }
+          }
+        } else if (field.type.isClassType() || field.type.isArrayType()) {
+          if (put.inValue().getConstInstruction().isConstNumber()
+              && put.inValue().getConstInstruction().asConstNumber().isZero()) {
             encodedField.staticValue = DexValueNull.NULL;
           } else {
-            ConstString cnst = put.inValue().getConstInstruction().asConstString();
-            encodedField.staticValue = new DexValueString(cnst.getValue());
+            throw new Unreachable("Unexpected default value for field type " + field.type + ".");
           }
         } else {
           ConstNumber cnst = put.inValue().getConstInstruction().asConstNumber();
@@ -696,22 +742,45 @@
           } else if (field.type == dexItemFactory.charType) {
             encodedField.staticValue = DexValueChar.create((char) cnst.getIntValue());
           } else {
-             throw new Unreachable("Unexpected field type.");
+            throw new Unreachable("Unexpected field type " + field.type + ".");
           }
         }
       }
 
       // Remove the static put instructions now replaced by static filed initial values.
+      List<Instruction> toRemove = new ArrayList<>();
       for (BasicBlock block : dominatorTree.dominatorBlocks(exit)) {
         InstructionListIterator iterator = block.listIterator();
         while (iterator.hasNext()) {
           Instruction current = iterator.next();
           if (current.isStaticPut() && puts.values().contains(current.asStaticPut())) {
             iterator.remove();
+            // Collect, for removal, the instruction that created the value for the static put,
+            // if all users are gone. This is done even if these instructions can throw as for
+            // the current patterns matched these exceptions are not detectable.
+            StaticPut put = current.asStaticPut();
+            if (put.inValue().uniqueUsers().size() == 0) {
+              if (put.inValue().isConstString()) {
+                toRemove.add(put.inValue().definition);
+              } else if (put.inValue().definition.isInvokeVirtual()) {
+                toRemove.add(put.inValue().definition);
+              }
+            }
           }
         }
       }
 
+      // Remove the instructions collected for removal.
+      if (toRemove.size() > 0) {
+        for (BasicBlock block : dominatorTree.dominatorBlocks(exit)) {
+          InstructionListIterator iterator = block.listIterator();
+          while (iterator.hasNext()) {
+            if (toRemove.contains(iterator.next())) {
+              iterator.remove();
+            }
+          }
+        }
+      }
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValues.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValues.java
deleted file mode 100644
index 085b069..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValues.java
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.rewrite.staticvalues;
-
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.smali.SmaliTestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
-import com.android.tools.r8.utils.InternalOptions;
-import org.junit.Test;
-
-public class StaticValues extends SmaliTestBase {
-
-  @Test
-  public void testAllTypes() {
-    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
-    builder.addStaticField("booleanField", "Z");
-    builder.addStaticField("byteField", "B");
-    builder.addStaticField("shortField", "S");
-    builder.addStaticField("intField", "I");
-    builder.addStaticField("longField", "J");
-    builder.addStaticField("floatField", "F");
-    builder.addStaticField("doubleField", "D");
-    builder.addStaticField("charField", "C");
-    builder.addStaticField("stringField", "Ljava/lang/String;");
-
-    builder.addStaticInitializer(
-        2,
-        "const               v0, 1",
-        "sput-byte           v0, LTest;->booleanField:Z",
-        "sput-byte           v0, LTest;->byteField:B",
-        "const               v0, 2",
-        "sput-short          v0, LTest;->shortField:S",
-        "const               v0, 3",
-        "sput                v0, LTest;->intField:I",
-        "const-wide          v0, 4",
-        "sput-wide           v0, LTest;->longField:J",
-        "const               v0, 0x40a00000",  // 5.0.
-        "sput                v0, LTest;->floatField:F",
-        "const-wide          v0, 0x4018000000000000L",  // 6.0.
-        "sput-wide           v0, LTest;->doubleField:D",
-        "const               v0, 0x37",  // ASCII 7.
-        "sput-char           v0, LTest;->charField:C",
-        "const-string        v0, \"8\"",
-        "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
-        "return-void"
-    );
-    builder.addMainMethod(
-        3,
-        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
-        "sget-boolean        v1, LTest;->booleanField:Z",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Z)V",
-        "sget-byte           v1, LTest;->byteField:B",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "sget-short          v1, LTest;->shortField:S",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "sget                v1, LTest;->intField:I",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "sget-wide           v1, LTest;->longField:J",
-        "invoke-virtual      { v0, v1, v2 }, Ljava/io/PrintStream;->println(J)V",
-        "sget                v1, LTest;->floatField:F",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(F)V",
-        "sget-wide           v1, LTest;->doubleField:D",
-        "invoke-virtual      { v0, v1, v2 }, Ljava/io/PrintStream;->println(D)V",
-        "sget-char           v1, LTest;->charField:C",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(C)V",
-        "sget-object         v1, LTest;->stringField:Ljava/lang/String;",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
-        "return-void"
-    );
-
-    InternalOptions options = new InternalOptions();
-    DexApplication originalApplication = buildApplication(builder, options);
-    DexApplication processedApplication = processApplication(originalApplication, options);
-
-    DexInspector inspector = new DexInspector(processedApplication);
-    MethodSubject clinit = inspector.clazz("Test").clinit();
-    // The const-string and return-void instructions are left.
-    assertEquals(2, clinit.getMethod().getCode().asDexCode().instructions.length);
-
-    String result = runArt(processedApplication, options);
-
-    assertEquals("true\n1\n2\n3\n4\n5.0\n6.0\n7\n8\n", result);
-  }
-
-  @Test
-  public void getBeforePut() {
-    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
-    builder.addStaticField("field1", "I", "1");
-    builder.addStaticField("field2", "I", "2");
-
-    builder.addStaticInitializer(
-        1,
-        "sget                v0, LTest;->field1:I",
-        "sput                v0, LTest;->field2:I",
-        "const               v0, 0",
-        "sput                v0, LTest;->field1:I",
-        "return-void"
-    );
-    builder.addMainMethod(
-        2,
-        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
-        "sget                v1, LTest;->field1:I",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "sget                v1, LTest;->field2:I",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "return-void"
-    );
-
-    InternalOptions options = new InternalOptions();
-    DexApplication originalApplication = buildApplication(builder, options);
-    DexApplication processedApplication = processApplication(originalApplication, options);
-
-    DexInspector inspector = new DexInspector(processedApplication);
-    MethodSubject clinit = inspector.clazz("Test").clinit();
-    // Nothing changed in the class initializer.
-    assertEquals(5, clinit.getMethod().getCode().asDexCode().instructions.length);
-
-    String result = runArt(processedApplication, options);
-
-    assertEquals("0\n1\n", result);
-  }
-
-  @Test
-  public void nullString() {
-    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
-    builder.addStaticField("stringField", "Ljava/lang/String;", "Hello");
-
-    builder.addStaticInitializer(
-        1,
-        "const               v0, 0",
-        "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
-        "return-void"
-    );
-    builder.addMainMethod(
-        2,
-        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
-        "sget-object         v1, LTest;->stringField:Ljava/lang/String;",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
-        "return-void"
-    );
-
-    InternalOptions options = new InternalOptions();
-    DexApplication originalApplication = buildApplication(builder, options);
-    DexApplication processedApplication = processApplication(originalApplication, options);
-
-    DexInspector inspector = new DexInspector(processedApplication);
-    MethodSubject clinit = inspector.clazz("Test").clinit();
-    // The return-void instruction is left.
-    assertEquals(1, clinit.getMethod().getCode().asDexCode().instructions.length);
-
-    String result = runArt(processedApplication, options);
-
-    assertEquals("null\n", result);
-  }
-
-  @Test
-  public void fieldOnOtherClass() {
-    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
-    builder.addStaticInitializer(
-        1,
-        "const               v0, 2",
-        "sput                v0, LOther;->field:I",
-        "return-void"
-    );
-    builder.addMainMethod(
-        2,
-        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
-        "sget                v1, LOther;->field:I",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "return-void"
-    );
-
-    builder.addClass("Other");
-    builder.addStaticField("field", "I", "1");
-
-    InternalOptions options = new InternalOptions();
-    DexApplication originalApplication = buildApplication(builder, options);
-    DexApplication processedApplication = processApplication(originalApplication, options);
-
-    DexInspector inspector = new DexInspector(processedApplication);
-    MethodSubject clinit = inspector.clazz("Test").clinit();
-    // Nothing changed in the class initializer.
-    assertEquals(3, clinit.getMethod().getCode().asDexCode().instructions.length);
-
-    String result = runArt(processedApplication, options);
-
-    assertEquals("2\n", result);
-  }
-
-}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
new file mode 100644
index 0000000..195c3aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -0,0 +1,359 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.rewrite.staticvalues;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import org.junit.Test;
+
+public class StaticValuesTest extends SmaliTestBase {
+
+  @Test
+  public void testAllTypes() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticField("booleanField", "Z");
+    builder.addStaticField("byteField", "B");
+    builder.addStaticField("shortField", "S");
+    builder.addStaticField("intField", "I");
+    builder.addStaticField("longField", "J");
+    builder.addStaticField("floatField", "F");
+    builder.addStaticField("doubleField", "D");
+    builder.addStaticField("charField", "C");
+    builder.addStaticField("stringField", "Ljava/lang/String;");
+
+    builder.addStaticInitializer(
+        2,
+        "const               v0, 1",
+        "sput-byte           v0, LTest;->booleanField:Z",
+        "sput-byte           v0, LTest;->byteField:B",
+        "const               v0, 2",
+        "sput-short          v0, LTest;->shortField:S",
+        "const               v0, 3",
+        "sput                v0, LTest;->intField:I",
+        "const-wide          v0, 4",
+        "sput-wide           v0, LTest;->longField:J",
+        "const               v0, 0x40a00000",  // 5.0.
+        "sput                v0, LTest;->floatField:F",
+        "const-wide          v0, 0x4018000000000000L",  // 6.0.
+        "sput-wide           v0, LTest;->doubleField:D",
+        "const               v0, 0x37",  // ASCII 7.
+        "sput-char           v0, LTest;->charField:C",
+        "const-string        v0, \"8\"",
+        "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget-boolean        v1, LTest;->booleanField:Z",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Z)V",
+        "sget-byte           v1, LTest;->byteField:B",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "sget-short          v1, LTest;->shortField:S",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "sget                v1, LTest;->intField:I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "sget-wide           v1, LTest;->longField:J",
+        "invoke-virtual      { v0, v1, v2 }, Ljava/io/PrintStream;->println(J)V",
+        "sget                v1, LTest;->floatField:F",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(F)V",
+        "sget-wide           v1, LTest;->doubleField:D",
+        "invoke-virtual      { v0, v1, v2 }, Ljava/io/PrintStream;->println(D)V",
+        "sget-char           v1, LTest;->charField:C",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(C)V",
+        "sget-object         v1, LTest;->stringField:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    assertFalse(inspector.clazz("Test").clinit().isPresent());
+
+    String result = runArt(processedApplication, options);
+
+    assertEquals("true\n1\n2\n3\n4\n5.0\n6.0\n7\n8\n", result);
+  }
+
+  @Test
+  public void getBeforePut() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticField("field1", "I", "1");
+    builder.addStaticField("field2", "I", "2");
+
+    builder.addStaticInitializer(
+        1,
+        "sget                v0, LTest;->field1:I",
+        "sput                v0, LTest;->field2:I",
+        "const               v0, 0",
+        "sput                v0, LTest;->field1:I",
+        "return-void"
+    );
+    builder.addMainMethod(
+        2,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget                v1, LTest;->field1:I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "sget                v1, LTest;->field2:I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    MethodSubject clinit = inspector.clazz("Test").clinit();
+    // Nothing changed in the class initializer.
+    assertEquals(5, clinit.getMethod().getCode().asDexCode().instructions.length);
+
+    String result = runArt(processedApplication, options);
+
+    assertEquals("0\n1\n", result);
+  }
+
+  @Test
+  public void testNull() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticField("stringField", "Ljava/lang/String;", "Hello");
+    builder.addStaticField("arrayField", "[I");
+    builder.addStaticField("arrayField2", "[[[[I");
+
+    builder.addStaticInitializer(
+        2,
+        "const               v0, 0",
+        "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
+        "sput-object         v0, LTest;->arrayField:[I",
+        "sput-object         v0, LTest;->arrayField2:[[[[I",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget-object         v1, LTest;->stringField:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, LTest;->arrayField:[I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V",
+        "sget-object         v1, LTest;->arrayField2:[[[[I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V",
+        "return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    assertFalse(inspector.clazz("Test").clinit().isPresent());
+
+    String result = runArt(processedApplication, options);
+
+    assertEquals("null\nnull\nnull\n", result);
+  }
+
+  @Test
+  public void testString() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticField("stringField1", "Ljava/lang/String;", "Hello");
+    builder.addStaticField("stringField2", "Ljava/lang/String;", "Hello");
+    builder.addStaticField("stringField3", "Ljava/lang/String;", "Hello");
+
+    builder.addStaticInitializer(
+        2,
+        "const-string        v0, \"Value1\"",
+        "sput-object         v0, LTest;->stringField1:Ljava/lang/String;",
+        "const-string        v0, \"Value2\"",
+        "sput-object         v0, LTest;->stringField2:Ljava/lang/String;",
+        "sput-object         v0, LTest;->stringField3:Ljava/lang/String;",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget-object         v1, LTest;->stringField1:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, LTest;->stringField2:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, LTest;->stringField3:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    assertFalse(inspector.clazz("Test").clinit().isPresent());
+
+    String result = runArt(processedApplication, options);
+
+    assertEquals("Value1\nValue2\nValue2\n", result);
+  }
+
+  @Test
+  public void testInitializationToOwnClassName() {
+    String className = "org.example.Test";
+    SmaliBuilder builder = new SmaliBuilder(className);
+
+    builder.addStaticField("name1", "Ljava/lang/String;");
+    builder.addStaticField("name2", "Ljava/lang/String;");
+    builder.addStaticField("name3", "Ljava/lang/String;");
+    builder.addStaticField("simpleName1", "Ljava/lang/String;");
+    builder.addStaticField("simpleName2", "Ljava/lang/String;");
+    builder.addStaticField("simpleName3", "Ljava/lang/String;");
+
+    String descriptor = builder.getCurrentClassDescriptor();
+
+    builder.addStaticInitializer(
+        3,
+        "const-class         v0, " + descriptor,
+        "invoke-virtual      { v0 }, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;",
+        "move-result-object  v0",
+        "sput-object         v0, " + descriptor + "->simpleName1:Ljava/lang/String;",
+        "const-class         v0, " + descriptor,
+        "invoke-virtual      { v0 }, Ljava/lang/Class;->getName()Ljava/lang/String;",
+        "move-result-object  v0",
+        "sput-object         v0, " + descriptor + "->name1:Ljava/lang/String;",
+        "const-class         v0, " + descriptor,
+        "invoke-virtual      { v0 }, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;",
+        "move-result-object  v1",
+        "invoke-virtual      { v0 }, Ljava/lang/Class;->getName()Ljava/lang/String;",
+        "move-result-object  v2",
+        "sput-object         v1, " + descriptor + "->simpleName2:Ljava/lang/String;",
+        "sput-object         v1, " + descriptor + "->simpleName3:Ljava/lang/String;",
+        "sput-object         v2, " + descriptor + "->name2:Ljava/lang/String;",
+        "sput-object         v2, " + descriptor + "->name3:Ljava/lang/String;",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget-object         v1, " + descriptor + "->simpleName1:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, " + descriptor + "->name1:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, " + descriptor + "->simpleName2:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, " + descriptor + "->name2:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, " + descriptor + "->simpleName3:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, " + descriptor + "->name3:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    assertTrue(inspector.clazz(className).isPresent());
+    assertFalse(inspector.clazz(className).clinit().isPresent());
+
+    String result = runArt(processedApplication, options, className);
+
+    assertEquals(
+        "Test\n" + className + "\nTest\n" + className + "\nTest\n"  + className + "\n", result);
+  }
+
+  @Test
+  public void testInitializationToOtherClassName() {
+    String className = "org.example.Test";
+    SmaliBuilder builder = new SmaliBuilder(className);
+
+    builder.addStaticField("simpleName", "Ljava/lang/String;");
+    builder.addStaticField("name", "Ljava/lang/String;");
+
+    String descriptor = builder.getCurrentClassDescriptor();
+
+    builder.addStaticInitializer(
+        3,
+        "const-class         v0, Lorg/example/Test2;",
+        "invoke-virtual      { v0 }, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;",
+        "move-result-object  v0",
+        "sput-object         v0, " + descriptor + "->simpleName:Ljava/lang/String;",
+        "const-class         v0, Lorg/example/Test2;",
+        "invoke-virtual      { v0 }, Ljava/lang/Class;->getName()Ljava/lang/String;",
+        "move-result-object  v0",
+        "sput-object         v0, " + descriptor + "->name:Ljava/lang/String;",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget-object         v1, " + descriptor + "->simpleName:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "sget-object         v1, " + descriptor + "->name:Ljava/lang/String;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "return-void"
+    );
+
+    builder.addClass("org.example.Test2");
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    assertTrue(inspector.clazz(className).isPresent());
+    assertTrue(inspector.clazz(className).clinit().isPresent());
+
+    String result = runArt(processedApplication, options, className);
+
+    assertEquals("Test2\norg.example.Test2\n", result);
+  }
+
+  @Test
+  public void fieldOnOtherClass() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticInitializer(
+        1,
+        "const               v0, 2",
+        "sput                v0, LOther;->field:I",
+        "return-void"
+    );
+    builder.addMainMethod(
+        2,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget                v1, LOther;->field:I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "return-void"
+    );
+
+    builder.addClass("Other");
+    builder.addStaticField("field", "I", "1");
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexInspector inspector = new DexInspector(processedApplication);
+    MethodSubject clinit = inspector.clazz("Test").clinit();
+    // Nothing changed in the class initializer.
+    assertEquals(3, clinit.getMethod().getCode().asDexCode().instructions.length);
+
+    String result = runArt(processedApplication, options);
+
+    assertEquals("2\n", result);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 4cb6736..03df65c 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -126,9 +126,7 @@
 
   private static void inspectShaking1(PrintUsageInspector inspector) {
     assertTrue(inspector.clazz("shaking1.Unused").isPresent());
-    assertTrue(inspector.clazz("shaking1.Used").isPresent());
-    ClassSubject used = inspector.clazz("shaking1.Used").get();
-    assertTrue(used.method("void", "<clinit>", ImmutableList.of()));
+    assertFalse(inspector.clazz("shaking1.Used").isPresent());
   }
 
   private static void inspectShaking2(PrintUsageInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 4c1fc69..51b6ab5 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -584,9 +584,12 @@
 
   private static void checkSameStructure(FoundMethodSubject refMethod, ClassSubject clazz) {
     MethodSignature signature = refMethod.getOriginalSignature();
-    Assert.assertTrue("Missing Method: " + clazz.getDexClass().toSourceString() + "."
-            + signature.toString(),
-        clazz.method(signature).isPresent());
+    // Don't check for existence of class initializers, as the code optimization can remove them.
+    if (!refMethod.isClassInitializer()) {
+      Assert.assertTrue("Missing Method: " + clazz.getDexClass().toSourceString() + "."
+              + signature.toString(),
+          clazz.method(signature).isPresent());
+    }
   }
 
   private static void checkSameStructure(FoundFieldSubject refField, ClassSubject clazz) {
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index ab9bf96..4253395 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -518,12 +518,16 @@
   }
 
   public String runArt(DexApplication application, InternalOptions options) {
+    return runArt(application, options, DEFAULT_MAIN_CLASS_NAME);
+  }
+
+  public String runArt(DexApplication application, InternalOptions options, String mainClass) {
     try {
       AndroidApp app = writeDex(application, options);
       Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
       // TODO(sgjesse): Pass in a unique temp directory for each run.
       app.writeToZip(out, OutputMode.Indexed);
-      return ToolHelper.runArtNoVerificationErrors(out.toString(), DEFAULT_MAIN_CLASS_NAME);
+      return ToolHelper.runArtNoVerificationErrors(out.toString(), mainClass);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 16c791d..0ad240c 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -521,6 +521,8 @@
 
     public abstract boolean isBridge();
 
+    public abstract boolean isClassInitializer();
+
     public abstract DexEncodedMethod getMethod();
 
     public Iterator<InstructionSubject> iterateInstructions() {
@@ -578,6 +580,11 @@
     }
 
     @Override
+    public boolean isClassInitializer() {
+      return false;
+    }
+
+    @Override
     public DexEncodedMethod getMethod() {
       return null;
     }
@@ -644,6 +651,11 @@
     }
 
     @Override
+    public boolean isClassInitializer() {
+      return dexMethod.isClassInitializer();
+    }
+
+    @Override
     public DexEncodedMethod getMethod() {
       return dexMethod;
     }