Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 615fd409 authored by Eric Holk's avatar Eric Holk Committed by android-build-merger
Browse files

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

am: c0b1a322

Change-Id: I9efc2af44faa17b40de1b3630ecfb54ca0b15f6d
parents 7c50667b c0b1a322
Loading
Loading
Loading
Loading
+34 −6
Original line number Diff line number Diff line
@@ -108,6 +108,12 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
    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 @@ ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::strin
  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 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
      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 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) {
  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 @@ void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) {
    }
    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"; }
  }
}

+39 −13
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ class Instruction {
    kBranchEqz,
    kBranchNEqz,
    kCheckCast,
    kGetInstanceField,
    kGetStaticField,
    kInvokeDirect,
    kInvokeInterface,
@@ -163,6 +164,7 @@ class Instruction {
    kNew,
    kReturn,
    kReturnObject,
    kSetInstanceField,
    kSetStaticField
  };

@@ -195,8 +197,9 @@ class Instruction {
  }
  // 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 @@ class Instruction {
  }
  // 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 @@ class Instruction {
  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 @@ class Instruction {
  }

  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 @@ class Instruction {

 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 @@ class MethodBuilder {
  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 @@ class MethodBuilder {
    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);
+21 −0
Original line number Diff line number Diff line
@@ -186,4 +186,25 @@ public final class DexBuilderTest {
    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);
  }
}
+32 −7
Original line number Diff line number Diff line
@@ -282,15 +282,15 @@ void GenerateSimpleTestCases(const string& outdir) {
    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 @@ void GenerateSimpleTestCases(const string& outdir) {
      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 @@ void GenerateSimpleTestCases(const string& outdir) {
    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());