[viewcompiler] Add support for static field put to DexBuilder

Bug: 111895153
Change-Id: I12b38fa520790debec545d7d1f6b3522a65ce03b
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 66bc698..09f9c04 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -105,6 +105,9 @@
     case Instruction::Op::kGetStaticField:
       out << "kGetStaticField";
       return out;
+    case Instruction::Op::kSetStaticField:
+      out << "kSetStaticField";
+      return out;
   }
 }
 
@@ -380,6 +383,7 @@
     case Instruction::Op::kCheckCast:
       return EncodeCast(instruction);
     case Instruction::Op::kGetStaticField:
+    case Instruction::Op::kSetStaticField:
       return EncodeStaticFieldOp(instruction);
   }
 }
@@ -536,13 +540,32 @@
 }
 
 void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) {
-  CHECK_EQ(Instruction::Op::kGetStaticField, instruction.opcode());
-  CHECK(instruction.dest().has_value());
-  CHECK(instruction.dest()->is_variable());
-  CHECK_EQ(0, instruction.args().size());
+  switch (instruction.opcode()) {
+    case Instruction::Op::kGetStaticField: {
+      CHECK(instruction.dest().has_value());
+      CHECK(instruction.dest()->is_variable());
+      CHECK_EQ(0, instruction.args().size());
 
-  Encode21c(
-      ::art::Instruction::SGET, RegisterValue(*instruction.dest()), instruction.index_argument());
+      Encode21c(::art::Instruction::SGET,
+                RegisterValue(*instruction.dest()),
+                instruction.index_argument());
+      break;
+    }
+    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,
+                RegisterValue(args[0]),
+                instruction.index_argument());
+      break;
+    }
+    default: {
+      LOG(FATAL) << "Unsupported static field operation";
+    }
+  }
 }
 
 size_t MethodBuilder::RegisterValue(const Value& value) const {
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index a7ccb4a..3f9ac43 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -163,6 +163,7 @@
     kNew,
     kReturn,
     kReturnObject,
+    kSetStaticField
   };
 
   ////////////////////////
@@ -237,6 +238,11 @@
     return Instruction{Op::kGetStaticField, field_id, dest};
   }
 
+  static inline Instruction SetStaticField(size_t field_id, Value value) {
+    return Instruction{Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
+  }
+
+
   ///////////////
   // Accessors //
   ///////////////
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 df11bfa..3138e71 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
@@ -180,4 +180,14 @@
     TestClass.staticInteger = 5;
     Assert.assertEquals(5, method.invoke(null));
   }
+
+  @Test
+  public void setStaticField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("setStaticField");
+    TestClass.staticInteger = 5;
+    method.invoke(null);
+    Assert.assertEquals(7, TestClass.staticInteger);
+  }
 }
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 5227f80..fee5e72 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -297,6 +297,22 @@
     method.Encode();
   }(readStaticField);
 
+  // Set a static field
+  // void setStaticField() { TestClass.staticInteger = 7; }
+  MethodBuilder setStaticField{
+      cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
+                               "staticInteger",
+                               TypeDescriptor::Int());
+    Value number{method.MakeRegister()};
+    method.BuildConst4(number, 7);
+    method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
+    method.BuildReturn();
+    method.Encode();
+  }(setStaticField);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());