[view compiler] Add DexBuilder support for getting and setting instance fields

Bug: 111895153
Change-Id: I5fa2936501c79e30a66f3863b76229ec83433928
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 09f9c04..499c42e 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -108,6 +108,12 @@
     case Instruction::Op::kSetStaticField:
       out << "kSetStaticField";
       return out;
+    case Instruction::Op::kGetInstanceField:
+      out << "kGetInstanceField";
+      return out;
+    case Instruction::Op::kSetInstanceField:
+      out << "kSetInstanceField";
+      return out;
   }
 }
 
@@ -246,6 +252,7 @@
   field->parent = GetOrAddType(parent);
   field->name = GetOrAddString(name);
   field->type = GetOrAddType(type);
+  field->orig_index = dex_file_->fields_indexes.AllocateIndex();
   dex_file_->fields_map[field->orig_index] = field;
   field_decls_by_key_[key] = field;
   return field;
@@ -384,7 +391,9 @@
       return EncodeCast(instruction);
     case Instruction::Op::kGetStaticField:
     case Instruction::Op::kSetStaticField:
-      return EncodeStaticFieldOp(instruction);
+    case Instruction::Op::kGetInstanceField:
+    case Instruction::Op::kSetInstanceField:
+      return EncodeFieldOp(instruction);
   }
 }
 
@@ -539,7 +548,8 @@
   Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
 }
 
-void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) {
+void MethodBuilder::EncodeFieldOp(const Instruction& instruction) {
+  const auto& args = instruction.args();
   switch (instruction.opcode()) {
     case Instruction::Op::kGetStaticField: {
       CHECK(instruction.dest().has_value());
@@ -553,18 +563,36 @@
     }
     case Instruction::Op::kSetStaticField: {
       CHECK(!instruction.dest().has_value());
-      const auto& args = instruction.args();
       CHECK_EQ(1, args.size());
       CHECK(args[0].is_variable());
 
-      Encode21c(::art::Instruction::SPUT,
+      Encode21c(::art::Instruction::SPUT, RegisterValue(args[0]), instruction.index_argument());
+      break;
+    }
+    case Instruction::Op::kGetInstanceField: {
+      CHECK(instruction.dest().has_value());
+      CHECK(instruction.dest()->is_variable());
+      CHECK_EQ(1, instruction.args().size());
+
+      Encode22c(::art::Instruction::IGET,
+                RegisterValue(*instruction.dest()),
                 RegisterValue(args[0]),
                 instruction.index_argument());
       break;
     }
-    default: {
-      LOG(FATAL) << "Unsupported static field operation";
+    case Instruction::Op::kSetInstanceField: {
+      CHECK(!instruction.dest().has_value());
+      CHECK_EQ(2, args.size());
+      CHECK(args[0].is_variable());
+      CHECK(args[1].is_variable());
+
+      Encode22c(::art::Instruction::IPUT,
+                RegisterValue(args[1]),
+                RegisterValue(args[0]),
+                instruction.index_argument());
+      break;
     }
+    default: { LOG(FATAL) << "Unsupported field operation"; }
   }
 }
 
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 3f9ac43..292d659 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -153,6 +153,7 @@
     kBranchEqz,
     kBranchNEqz,
     kCheckCast,
+    kGetInstanceField,
     kGetStaticField,
     kInvokeDirect,
     kInvokeInterface,
@@ -163,6 +164,7 @@
     kNew,
     kReturn,
     kReturnObject,
+    kSetInstanceField,
     kSetStaticField
   };
 
@@ -195,8 +197,9 @@
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest,
-                                                Value this_arg, T... args) {
+  static inline Instruction InvokeVirtualObject(size_t index_argument,
+                                                std::optional<const Value> dest, Value this_arg,
+                                                T... args) {
     return Instruction{
         Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
   }
@@ -209,8 +212,9 @@
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeDirectObject(size_t index_argument, std::optional<const Value> dest,
-                                               Value this_arg, T... args) {
+  static inline Instruction InvokeDirectObject(size_t index_argument,
+                                               std::optional<const Value> dest, Value this_arg,
+                                               T... args) {
     return Instruction{
         Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
   }
@@ -218,20 +222,21 @@
   template <typename... T>
   static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
                                          T... args) {
-    return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
+    return Instruction{
+        Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeStaticObject(size_t index_argument, std::optional<const Value> dest,
-                                               T... args) {
+  static inline Instruction InvokeStaticObject(size_t index_argument,
+                                               std::optional<const Value> dest, T... args) {
     return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
   }
   // For static calls.
   template <typename... T>
   static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
                                             T... args) {
-    return Instruction{Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
-
+    return Instruction{
+        Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
   }
 
   static inline Instruction GetStaticField(size_t field_id, Value dest) {
@@ -239,9 +244,18 @@
   }
 
   static inline Instruction SetStaticField(size_t field_id, Value value) {
-    return Instruction{Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
+    return Instruction{
+        Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
   }
 
+  static inline Instruction GetField(size_t field_id, Value dest, Value object) {
+    return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object};
+  }
+
+  static inline Instruction SetField(size_t field_id, Value object, Value value) {
+    return Instruction{
+        Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value};
+  }
 
   ///////////////
   // Accessors //
@@ -255,10 +269,14 @@
 
  private:
   inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
-      : opcode_{opcode}, index_argument_{index_argument}, result_is_object_{false}, dest_{dest}, args_{} {}
+      : opcode_{opcode},
+        index_argument_{index_argument},
+        result_is_object_{false},
+        dest_{dest},
+        args_{} {}
 
   template <typename... T>
-  inline constexpr Instruction(Op opcode, size_t index_argument, bool result_is_object,
+  inline Instruction(Op opcode, size_t index_argument, bool result_is_object,
                                std::optional<const Value> dest, T... args)
       : opcode_{opcode},
         index_argument_{index_argument},
@@ -331,7 +349,7 @@
   void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
   void EncodeNew(const Instruction& instruction);
   void EncodeCast(const Instruction& instruction);
-  void EncodeStaticFieldOp(const Instruction& instruction);
+  void EncodeFieldOp(const Instruction& instruction);
 
   // Low-level instruction format encoding. See
   // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
@@ -364,6 +382,14 @@
     buffer_.push_back(b);
   }
 
+  inline void Encode22c(art::Instruction::Code opcode, uint8_t a, uint8_t b, uint16_t c) {
+    // b|a|op|bbbb
+    CHECK(IsShortRegister(a));
+    CHECK(IsShortRegister(b));
+    buffer_.push_back((b << 12) | (a << 8) | opcode);
+    buffer_.push_back(c);
+  }
+
   inline void Encode32x(art::Instruction::Code opcode, uint16_t a, uint16_t b) {
     buffer_.push_back(opcode);
     buffer_.push_back(a);
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 3138e71..93496d0 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -190,4 +190,25 @@
     method.invoke(null);
     Assert.assertEquals(7, TestClass.staticInteger);
   }
+
+  @Test
+  public void readInstanceField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("readInstanceField", TestClass.class);
+    TestClass obj = new TestClass();
+    obj.instanceField = 5;
+    Assert.assertEquals(5, method.invoke(null, obj));
+  }
+
+  @Test
+  public void setInstanceField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("setInstanceField", TestClass.class);
+    TestClass obj = new TestClass();
+    obj.instanceField = 5;
+    method.invoke(null, obj);
+    Assert.assertEquals(7, obj.instanceField);
+  }
 }
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index fee5e72..6dedf24 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -282,15 +282,15 @@
     method.Encode();
   }(castObjectToString);
 
+  TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass");
+
   // Read a static field
-  // integer readStaticField() { return TestClass.staticInteger; }
+  // int readStaticField() { return TestClass.staticInteger; }
   MethodBuilder readStaticField{
       cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
   [&](MethodBuilder& method) {
     const ir::FieldDecl* field =
-        dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
-                               "staticInteger",
-                               TypeDescriptor::Int());
+        dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
     Value result{method.MakeRegister()};
     method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
     method.BuildReturn(result, /*is_object=*/false);
@@ -303,9 +303,7 @@
       cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
   [&](MethodBuilder& method) {
     const ir::FieldDecl* field =
-        dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
-                               "staticInteger",
-                               TypeDescriptor::Int());
+        dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
     Value number{method.MakeRegister()};
     method.BuildConst4(number, 7);
     method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
@@ -313,6 +311,33 @@
     method.Encode();
   }(setStaticField);
 
+  // Read an instance field
+  // int readInstanceField(TestClass obj) { return obj.instanceField; }
+  MethodBuilder readInstanceField{
+      cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
+    Value result{method.MakeRegister()};
+    method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
+    method.BuildReturn(result, /*is_object=*/false);
+    method.Encode();
+  }(readInstanceField);
+
+  // Set an instance field
+  // void setInstanceField(TestClass obj) { obj.instanceField = 7; }
+  MethodBuilder setInstanceField{
+      cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
+    Value number{method.MakeRegister()};
+    method.BuildConst4(number, 7);
+    method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
+    method.BuildReturn();
+    method.Encode();
+  }(setInstanceField);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());