Rewrite objects argument of newMessageInfo()

Bug: 112437944
Change-Id: Ib50cb97678993ade99d30ae3476d347d8e1694e8
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 c0eee4f..0fbeef9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -550,6 +550,19 @@
     return lookupTarget(instanceFields, field);
   }
 
+  public DexField lookupUniqueInstanceFieldWithName(DexString name) {
+    DexField field = null;
+    for (DexEncodedField encodedField : instanceFields()) {
+      if (encodedField.field.name == name) {
+        if (field != null) {
+          return null;
+        }
+        field = encodedField.field;
+      }
+    }
+    return field;
+  }
+
   /**
    * Find field in this class matching field.
    */
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index c7eaa02..7f33fbb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -6,27 +6,24 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-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.DexString;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldTypeFactory;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
 import com.android.tools.r8.ir.code.ConstString;
-import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.naming.dexitembasedstring.FieldNameComputationInfo;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import java.util.ArrayList;
 import java.util.List;
 
 public class GeneratedMessageLiteShrinker {
@@ -35,9 +32,11 @@
   private final RawMessageInfoDecoder decoder;
   private final RawMessageInfoEncoder encoder;
   private final ProtoReferences references;
-  private final TypeLatticeElement stringType;
   private final ThrowingInfo throwingInfo;
 
+  private final TypeLatticeElement objectArrayType;
+  private final TypeLatticeElement stringType;
+
   private final ProtoFieldTypeFactory factory = new ProtoFieldTypeFactory();
 
   public GeneratedMessageLiteShrinker(AppView<AppInfoWithLiveness> appView) {
@@ -45,8 +44,13 @@
     this.decoder = new RawMessageInfoDecoder(factory);
     this.encoder = new RawMessageInfoEncoder(appView.dexItemFactory());
     this.references = appView.protoShrinker().references;
-    this.stringType = TypeLatticeElement.stringClassType(appView, Nullability.definitelyNotNull());
     this.throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
+
+    // Types.
+    this.objectArrayType =
+        TypeLatticeElement.fromDexType(
+            appView.dexItemFactory().objectArrayType, Nullability.definitelyNotNull(), appView);
+    this.stringType = TypeLatticeElement.stringClassType(appView, Nullability.definitelyNotNull());
   }
 
   public void run(DexEncodedMethod method, IRCode code) {
@@ -64,8 +68,8 @@
    * newMessageInfo is still pending.
    */
   private void rewriteDynamicMethod(DexEncodedMethod method, IRCode code) {
-    DexClass clazz = appView.definitionFor(method.method.holder);
-    if (clazz == null || !clazz.isProgramClass()) {
+    DexClass context = appView.definitionFor(method.method.holder);
+    if (context == null || !context.isProgramClass()) {
       return;
     }
 
@@ -90,46 +94,15 @@
       Value infoValue = newMessageInfoInvoke.inValues().get(1).getAliasedValue();
       Value objectsValue = newMessageInfoInvoke.inValues().get(2).getAliasedValue();
 
-      // TODO(b/112437944): If we regenerate the arguments to newMessageInfo() entirely, then we can
-      //  simply generate DexItemBasedConstString instructions at that point. That way the block
-      //  below will not be needed.
-      {
-        List<ConstString> identifierNameStringCandidates = new ArrayList<>();
-        for (Instruction user : objectsValue.uniqueUsers()) {
-          if (user.isArrayPut()) {
-            Value rewritingCandidate = user.asArrayPut().value().getAliasedValue();
-            if (!rewritingCandidate.isPhi() && rewritingCandidate.definition.isConstString()) {
-              identifierNameStringCandidates.add(rewritingCandidate.definition.asConstString());
-            }
-          }
-        }
-
-        boolean changed = false;
-        for (ConstString rewritingCandidate : identifierNameStringCandidates) {
-          DexString fieldName = rewritingCandidate.getValue();
-          DexField field = uniqueInstanceFieldWithName(clazz, fieldName, code.origin);
-          if (field == null) {
-            continue;
-          }
-          Value newValue = code.createValue(stringType);
-          rewritingCandidate.replace(
-              new DexItemBasedConstString(
-                  newValue, field, FieldNameComputationInfo.forFieldName(), throwingInfo));
-          changed = true;
-        }
-
-        if (changed) {
-          method.getMutableOptimizationInfo().markUseIdentifierNameString();
-        }
-      }
-
       // Decode the arguments passed to newMessageInfo().
-      ProtoMessageInfo protoMessageInfo = decoder.run(infoValue, objectsValue);
+      ProtoMessageInfo protoMessageInfo = decoder.run(infoValue, objectsValue, context);
       if (protoMessageInfo != null) {
         // Rewrite the arguments to newMessageInfo().
-        infoValue.definition.replace(
-            new ConstString(
-                code.createValue(stringType), encoder.encodeInfo(protoMessageInfo), throwingInfo));
+        rewriteArgumentsToNewMessageInfo(
+            method, code, newMessageInfoInvoke, infoValue, protoMessageInfo);
+
+        // TODO(b/112437944): Need to ensure that the definition of the original `objects` value is
+        //  removed by dead code elimination.
       } else {
         // We should generally be able to decode the arguments passed to newMessageInfo().
         assert false;
@@ -137,23 +110,63 @@
     }
   }
 
-  private DexField uniqueInstanceFieldWithName(DexClass clazz, DexString name, Origin origin) {
-    DexField field = null;
-    for (DexEncodedField encodedField : clazz.instanceFields()) {
-      if (encodedField.field.name == name) {
-        if (field != null) {
-          Reporter reporter = appView.options().reporter;
-          String errorMessage =
-              "Expected to find a single instance field named \""
-                  + name.toSourceString()
-                  + "\" in `"
-                  + clazz.type.toSourceString()
-                  + "`";
-          throw reporter.fatalError(new StringDiagnostic(errorMessage, origin));
-        }
-        field = encodedField.field;
+  private void rewriteArgumentsToNewMessageInfo(
+      DexEncodedMethod method,
+      IRCode code,
+      InvokeStatic newMessageInfoInvoke,
+      Value infoValue,
+      ProtoMessageInfo protoMessageInfo) {
+    rewriteInfoArgumentToNewMessageInfo(code, infoValue, protoMessageInfo);
+    rewriteObjectsArgumentToNewMessageInfo(method, code, newMessageInfoInvoke, protoMessageInfo);
+  }
+
+  private void rewriteInfoArgumentToNewMessageInfo(
+      IRCode code, Value infoValue, ProtoMessageInfo protoMessageInfo) {
+    infoValue.definition.replace(
+        new ConstString(
+            code.createValue(stringType), encoder.encodeInfo(protoMessageInfo), throwingInfo));
+  }
+
+  private void rewriteObjectsArgumentToNewMessageInfo(
+      DexEncodedMethod method,
+      IRCode code,
+      InvokeStatic newMessageInfoInvoke,
+      ProtoMessageInfo protoMessageInfo) {
+    // Position iterator immediately before the call to newMessageInfo().
+    BasicBlock block = newMessageInfoInvoke.getBlock();
+    InstructionListIterator instructionIterator = block.listIterator(newMessageInfoInvoke);
+    Instruction previous = instructionIterator.previous();
+    instructionIterator.setInsertionPosition(newMessageInfoInvoke.getPosition());
+    assert previous == newMessageInfoInvoke;
+
+    // Create the `objects` array.
+    List<ProtoObject> objects = encoder.encodeObjects(protoMessageInfo);
+    Value sizeValue =
+        instructionIterator.insertConstIntInstruction(code, appView.options(), objects.size());
+    Value newObjectsValue = code.createValue(objectArrayType);
+    instructionIterator.add(
+        new NewArrayEmpty(newObjectsValue, sizeValue, appView.dexItemFactory().objectArrayType));
+
+    // Populate the `objects` array.
+    boolean hasIntroducedIdentifierNameString = false;
+    for (int i = 0; i < objects.size(); i++) {
+      Value indexValue = instructionIterator.insertConstIntInstruction(code, appView.options(), i);
+      Instruction materializingInstruction = objects.get(i).buildIR(appView, code);
+      instructionIterator.add(materializingInstruction);
+      instructionIterator.add(
+          new ArrayPut(
+              MemberType.OBJECT, newObjectsValue, indexValue, materializingInstruction.outValue()));
+
+      if (materializingInstruction.isDexItemBasedConstString()) {
+        hasIntroducedIdentifierNameString = true;
       }
     }
-    return field;
+
+    // Pass the newly created `objects` array to newMessageInfo().
+    newMessageInfoInvoke.replaceValue(2, newObjectsValue);
+
+    if (hasIntroducedIdentifierNameString) {
+      method.getMutableOptimizationInfo().markUseIdentifierNameString();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index fdc3934..90f6e25 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -4,20 +4,35 @@
 
 package com.android.tools.r8.ir.analysis.proto;
 
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldInfo;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldObject;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldType;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldTypeFactory;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoObjectFromInvokeStatic;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoObjectFromStaticGet;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoTypeObject;
 import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.ThrowingCharIterator;
 import com.android.tools.r8.utils.ThrowingIntIterator;
 import com.android.tools.r8.utils.ThrowingIterator;
 import java.io.UTFDataFormatException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.OptionalInt;
@@ -54,7 +69,7 @@
     this.factory = factory;
   }
 
-  public ProtoMessageInfo run(Value infoValue, Value objectsValue) {
+  public ProtoMessageInfo run(Value infoValue, Value objectsValue, DexClass context) {
     try {
       ProtoMessageInfo.Builder builder = ProtoMessageInfo.builder();
       ThrowingIntIterator<InvalidRawMessageInfoException> infoIterator =
@@ -95,15 +110,19 @@
         builder.setNumberOfOneOfObjects(numberOfOneOfObjects);
         for (int i = 0; i < numberOfOneOfObjects; i++) {
           builder.addOneOfObject(
-              objectIterator.computeNextIfAbsent(this::invalidObjectsFailure),
-              objectIterator.computeNextIfAbsent(this::invalidObjectsFailure));
+              createProtoObject(
+                  objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context),
+              createProtoObject(
+                  objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context));
         }
       }
 
       if (numberOfHasBitsObjects > 0) {
         builder.setNumberOfHasBitsObjects(numberOfHasBitsObjects);
         for (int i = 0; i < numberOfHasBitsObjects; i++) {
-          builder.addHasBitsObject(objectIterator.computeNextIfAbsent(this::invalidObjectsFailure));
+          builder.addHasBitsObject(
+              createProtoObject(
+                  objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context));
         }
       }
 
@@ -124,8 +143,11 @@
         // Extract field-specific portion of "objects" array.
         int numberOfObjects = fieldType.numberOfObjects(isProto2, factory);
         try {
-          List<Value> fieldObjects = objectIterator.take(numberOfObjects);
-          builder.addField(new ProtoFieldInfo(fieldNumber, fieldType, auxData, fieldObjects));
+          List<ProtoObject> objects = new ArrayList<>(numberOfObjects);
+          for (Value value : objectIterator.take(numberOfObjects)) {
+            objects.add(createProtoObject(value, context));
+          }
+          builder.addField(new ProtoFieldInfo(fieldNumber, fieldType, auxData, objects));
         } catch (NoSuchElementException e) {
           throw new InvalidRawMessageInfoException();
         }
@@ -144,6 +166,42 @@
     }
   }
 
+  private ProtoObject createProtoObject(Value value, DexClass context)
+      throws InvalidRawMessageInfoException {
+    Value root = value.getAliasedValue();
+    if (!root.isPhi()) {
+      Instruction definition = root.definition;
+      if (definition.isConstClass()) {
+        ConstClass constClass = definition.asConstClass();
+        return new ProtoTypeObject(constClass.getValue());
+      } else if (definition.isConstString()) {
+        ConstString constString = definition.asConstString();
+        DexField field = context.lookupUniqueInstanceFieldWithName(constString.getValue());
+        if (field != null) {
+          return new ProtoFieldObject(field);
+        }
+      } else if (definition.isDexItemBasedConstString()) {
+        DexItemBasedConstString constString = definition.asDexItemBasedConstString();
+        DexReference reference = constString.getItem();
+        NameComputationInfo<?> nameComputationInfo = constString.getNameComputationInfo();
+        if (reference.isDexField()
+            && nameComputationInfo.isFieldNameComputationInfo()
+            && nameComputationInfo.asFieldNameComputationInfo().isForFieldName()) {
+          return new ProtoFieldObject(reference.asDexField());
+        }
+      } else if (definition.isInvokeStatic()) {
+        InvokeStatic invoke = definition.asInvokeStatic();
+        if (invoke.arguments().isEmpty()) {
+          return new ProtoObjectFromInvokeStatic(invoke.getInvokedMethod());
+        }
+      } else if (definition.isStaticGet()) {
+        StaticGet staticGet = definition.asStaticGet();
+        return new ProtoObjectFromStaticGet(staticGet.getField());
+      }
+    }
+    throw new InvalidRawMessageInfoException();
+  }
+
   private int invalidInfoFailure() throws InvalidRawMessageInfoException {
     throw new InvalidRawMessageInfoException();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoEncoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoEncoder.java
index 8487210..6d94b0c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoEncoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoEncoder.java
@@ -9,9 +9,13 @@
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldInfo;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldType;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
+import com.android.tools.r8.utils.Pair;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntList;
 import it.unimi.dsi.fastutil.ints.IntListIterator;
+import java.util.ArrayList;
+import java.util.List;
 
 public class RawMessageInfoEncoder {
 
@@ -23,7 +27,7 @@
 
   DexString encodeInfo(ProtoMessageInfo protoMessageInfo) {
     IntList info = new IntArrayList();
-    info.add(protoMessageInfo.flags());
+    info.add(protoMessageInfo.getFlags());
     info.add(protoMessageInfo.numberOfFields());
 
     if (protoMessageInfo.hasFields()) {
@@ -33,7 +37,7 @@
       int repeatedFieldCount = 0;
       int checkInitialized = 0;
 
-      for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.fields()) {
+      for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) {
         int fieldNumber = protoFieldInfo.getNumber();
         if (fieldNumber < minFieldNumber) {
           minFieldNumber = fieldNumber;
@@ -61,7 +65,7 @@
       info.add(repeatedFieldCount);
       info.add(checkInitialized);
 
-      for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.fields()) {
+      for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) {
         info.add(protoFieldInfo.getNumber());
         info.add(protoFieldInfo.getType().serialize());
         if (protoFieldInfo.hasAuxData()) {
@@ -92,6 +96,25 @@
     return dexItemFactory.createString(info.size() + numberOfExtraChars, result);
   }
 
+  List<ProtoObject> encodeObjects(ProtoMessageInfo protoMessageInfo) {
+    List<ProtoObject> result = new ArrayList<>();
+    if (protoMessageInfo.numberOfOneOfObjects() > 0) {
+      for (Pair<ProtoObject, ProtoObject> oneOfObject : protoMessageInfo.getOneOfObjects()) {
+        result.add(oneOfObject.getFirst());
+        result.add(oneOfObject.getSecond());
+      }
+    }
+    if (protoMessageInfo.numberOfHasBitsObjects() > 0) {
+      result.addAll(protoMessageInfo.getHasBitsObjects());
+    }
+    if (protoMessageInfo.hasFields()) {
+      for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) {
+        result.addAll(protoFieldInfo.getObjects());
+      }
+    }
+    return result;
+  }
+
   private static int countBytes(IntList info) {
     // We need an extra byte for the terminating '0'.
     int result = 1;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
index d701c02..153ea66 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.analysis.proto.schema;
 
-import com.android.tools.r8.ir.code.Value;
 import java.util.List;
 import java.util.OptionalInt;
 
@@ -14,11 +13,10 @@
   private final ProtoFieldType type;
 
   private final OptionalInt auxData;
-  // TODO(b/112437944): Create an abstract representation of the object values to ensure that this
-  //  is detached from the IR.
-  private final List<Value> objects;
+  private final List<ProtoObject> objects;
 
-  public ProtoFieldInfo(int number, ProtoFieldType type, OptionalInt auxData, List<Value> objects) {
+  public ProtoFieldInfo(
+      int number, ProtoFieldType type, OptionalInt auxData, List<ProtoObject> objects) {
     this.number = number;
     this.type = type;
     this.auxData = auxData;
@@ -38,6 +36,10 @@
     return number;
   }
 
+  public List<ProtoObject> getObjects() {
+    return objects;
+  }
+
   public ProtoFieldType getType() {
     return type;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java
new file mode 100644
index 0000000..c3f7795
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java
@@ -0,0 +1,37 @@
+// 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.analysis.proto.schema;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.naming.dexitembasedstring.FieldNameComputationInfo;
+
+public class ProtoFieldObject extends ProtoObject {
+
+  private final DexField field;
+
+  public ProtoFieldObject(DexField field) {
+    this.field = field;
+  }
+
+  @Override
+  public Instruction buildIR(AppView<?> appView, IRCode code) {
+    Value value =
+        code.createValue(
+            TypeLatticeElement.stringClassType(appView, Nullability.definitelyNotNull()));
+    return new DexItemBasedConstString(
+        value,
+        field,
+        FieldNameComputationInfo.forFieldName(),
+        ThrowingInfo.defaultForConstString(appView.options()));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
index 45bfdc0..9098d21 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.analysis.proto.schema;
 
-import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.Pair;
 import java.util.ArrayList;
 import java.util.List;
@@ -16,8 +15,8 @@
     private int flags;
 
     private List<ProtoFieldInfo> fields;
-    private List<Value> hasBitsObjects;
-    private List<Pair<Value, Value>> oneOfObjects;
+    private List<ProtoObject> hasBitsObjects;
+    private List<Pair<ProtoObject, ProtoObject>> oneOfObjects;
 
     public void setFlags(int value) {
       this.flags = value;
@@ -30,7 +29,7 @@
       fields.add(field);
     }
 
-    public void addHasBitsObject(Value hasBitsObject) {
+    public void addHasBitsObject(ProtoObject hasBitsObject) {
       if (hasBitsObjects == null) {
         hasBitsObjects = new ArrayList<>();
       }
@@ -43,7 +42,7 @@
       }
     }
 
-    public void addOneOfObject(Value first, Value second) {
+    public void addOneOfObject(ProtoObject first, ProtoObject second) {
       if (oneOfObjects == null) {
         oneOfObjects = new ArrayList<>();
       }
@@ -64,14 +63,14 @@
   private final int flags;
 
   private final List<ProtoFieldInfo> fields;
-  private final List<Value> hasBitsObjects;
-  private final List<Pair<Value, Value>> oneOfObjects;
+  private final List<ProtoObject> hasBitsObjects;
+  private final List<Pair<ProtoObject, ProtoObject>> oneOfObjects;
 
   private ProtoMessageInfo(
       int flags,
       List<ProtoFieldInfo> fields,
-      List<Value> hasBitsObjects,
-      List<Pair<Value, Value>> oneOfObjects) {
+      List<ProtoObject> hasBitsObjects,
+      List<Pair<ProtoObject, ProtoObject>> oneOfObjects) {
     this.flags = flags;
     this.fields = fields;
     this.hasBitsObjects = hasBitsObjects;
@@ -82,14 +81,22 @@
     return new ProtoMessageInfo.Builder();
   }
 
-  public List<ProtoFieldInfo> fields() {
+  public List<ProtoFieldInfo> getFields() {
     return fields;
   }
 
-  public int flags() {
+  public int getFlags() {
     return flags;
   }
 
+  public List<ProtoObject> getHasBitsObjects() {
+    return hasBitsObjects;
+  }
+
+  public List<Pair<ProtoObject, ProtoObject>> getOneOfObjects() {
+    return oneOfObjects;
+  }
+
   public boolean hasFields() {
     return fields != null && !fields.isEmpty();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java
new file mode 100644
index 0000000..ad3a916
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java
@@ -0,0 +1,14 @@
+// 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.analysis.proto.schema;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+
+public abstract class ProtoObject {
+
+  public abstract Instruction buildIR(AppView<?> appView, IRCode code);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObjectFromInvokeStatic.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObjectFromInvokeStatic.java
new file mode 100644
index 0000000..0812098
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObjectFromInvokeStatic.java
@@ -0,0 +1,33 @@
+// 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.analysis.proto.schema;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
+import com.google.common.collect.ImmutableList;
+
+public class ProtoObjectFromInvokeStatic extends ProtoObject {
+
+  private final DexMethod method;
+
+  public ProtoObjectFromInvokeStatic(DexMethod method) {
+    this.method = method;
+  }
+
+  @Override
+  public Instruction buildIR(AppView<?> appView, IRCode code) {
+    Value value =
+        code.createValue(
+            TypeLatticeElement.fromDexType(
+                method.proto.returnType, Nullability.maybeNull(), appView));
+    return new InvokeStatic(method, value, ImmutableList.of());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObjectFromStaticGet.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObjectFromStaticGet.java
new file mode 100644
index 0000000..9c14f6c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObjectFromStaticGet.java
@@ -0,0 +1,31 @@
+// 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.analysis.proto.schema;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+
+public class ProtoObjectFromStaticGet extends ProtoObject {
+
+  private final DexField field;
+
+  public ProtoObjectFromStaticGet(DexField field) {
+    this.field = field;
+  }
+
+  @Override
+  public Instruction buildIR(AppView<?> appView, IRCode code) {
+    Value value =
+        code.createValue(
+            TypeLatticeElement.fromDexType(field.type, Nullability.maybeNull(), appView));
+    return new StaticGet(value, field);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoTypeObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoTypeObject.java
new file mode 100644
index 0000000..21c37e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoTypeObject.java
@@ -0,0 +1,24 @@
+// 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.analysis.proto.schema;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+
+public class ProtoTypeObject extends ProtoObject {
+
+  private final DexType type;
+
+  public ProtoTypeObject(DexType type) {
+    this.type = type;
+  }
+
+  @Override
+  public Instruction buildIR(AppView<?> appView, IRCode code) {
+    return code.createConstClass(appView, type);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 2faa999..698592d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -97,7 +97,7 @@
   public void add(Instruction instruction) {
     instruction.setBlock(block);
     assert instruction.getBlock() == block;
-    if (position != null) {
+    if (position != null && !instruction.hasPosition()) {
       instruction.setPosition(position);
     }
     listIterator.add(instruction);
@@ -194,6 +194,15 @@
   }
 
   @Override
+  public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
+    ConstNumber constNumberInstruction = code.createIntConstant(value);
+    // Note that we only keep position info for throwing instructions in release mode.
+    constNumberInstruction.setPosition(options.debug ? current.getPosition() : Position.none());
+    add(constNumberInstruction);
+    return constNumberInstruction.outValue();
+  }
+
+  @Override
   public void replaceCurrentInstructionWithThrowNull(
       AppView<? extends AppInfoWithSubtyping> appView,
       IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 2e9986b..a1c77dc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -871,6 +871,12 @@
     return blocks.stream().max(Comparator.comparingInt(BasicBlock::getNumber)).get().getNumber();
   }
 
+  public ConstClass createConstClass(AppView<?> appView, DexType type) {
+    Value out =
+        createValue(TypeLatticeElement.fromDexType(type, Nullability.definitelyNotNull(), appView));
+    return new ConstClass(out, type);
+  }
+
   public ConstNumber createConstNull() {
     Value out = createValue(TypeLatticeElement.NULL);
     return new ConstNumber(out, 0);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index e490ebf..ad70ced 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -65,6 +65,10 @@
 
   public abstract <T> T accept(InstructionVisitor<T> visitor);
 
+  final boolean hasPosition() {
+    return position != null;
+  }
+
   public final Position getPosition() {
     assert position != null;
     return position;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 4bb3984..59a77a1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -51,6 +51,8 @@
 
   Value insertConstNullInstruction(IRCode code, InternalOptions options);
 
+  Value insertConstIntInstruction(IRCode code, InternalOptions options, int value);
+
   /**
    * Replace the current instruction with null throwing instructions.
    *
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java
index 142933f..c14da47 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java
@@ -42,6 +42,11 @@
   }
 
   @Override
+  public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
+    return currentBlockIterator.insertConstIntInstruction(code, options, value);
+  }
+
+  @Override
   public void replaceCurrentInstructionWithThrowNull(
       AppView<? extends AppInfoWithSubtyping> appView,
       IRCode code,
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java
index f9f8cc0..eeb70f3 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java
@@ -21,6 +21,10 @@
     return FIELD_NAME_INSTANCE;
   }
 
+  public boolean isForFieldName() {
+    return true;
+  }
+
   @Override
   public DexString internalComputeNameFor(
       DexField field, DexDefinitionSupplier definitions, NamingLens namingLens) {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index f5c2f27..2971cf9 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -39,6 +39,9 @@
         .addProgramFiles(PROTO2_EXAMPLES_JAR, PROTO2_PROTO_JAR, PROTOBUF_LITE_JAR)
         .addKeepMainRule("proto2.TestClass")
         .addKeepRules(
+            // TODO(b/112437944): Should rewrite constructor calls to RawMessageInfo when
+            //  newMessageInfo() is inlined.
+            "-neverinline class * { newMessageInfo(...); }",
             // TODO(b/112437944): Do not remove proto fields that are actually used in tree shaking.
             "-keepclassmembers,allowobfuscation class * extends",
             "    com.google.protobuf.GeneratedMessageLite {",
@@ -55,6 +58,7 @@
               // Because there are unused rules in lite_proguard.pgcfg.
               options.testing.allowUnusedProguardConfigurationRules = true;
             })
+        .enableProguardTestOptions()
         .minification(enableMinification)
         .setMinApi(parameters.getRuntime())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 80aae46..e83a89f 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -53,6 +53,11 @@
     }
 
     @Override
+    public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
+      throw new Unimplemented();
+    }
+
+    @Override
     public void replaceCurrentInstructionWithThrowNull(
         AppView<? extends AppInfoWithSubtyping> appView,
         IRCode code,