[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());