Merge "[view compiler] Add DexBuilder support for getting and setting instance fields" am: c0b1a32260
am: 615fd40982
Change-Id: I50917072f916b9ae9fb52840791cbc7c65b6bce7
This commit is contained in:
@@ -108,6 +108,12 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
|
|||||||
case Instruction::Op::kSetStaticField:
|
case Instruction::Op::kSetStaticField:
|
||||||
out << "kSetStaticField";
|
out << "kSetStaticField";
|
||||||
return out;
|
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->parent = GetOrAddType(parent);
|
||||||
field->name = GetOrAddString(name);
|
field->name = GetOrAddString(name);
|
||||||
field->type = GetOrAddType(type);
|
field->type = GetOrAddType(type);
|
||||||
|
field->orig_index = dex_file_->fields_indexes.AllocateIndex();
|
||||||
dex_file_->fields_map[field->orig_index] = field;
|
dex_file_->fields_map[field->orig_index] = field;
|
||||||
field_decls_by_key_[key] = field;
|
field_decls_by_key_[key] = field;
|
||||||
return field;
|
return field;
|
||||||
@@ -384,7 +391,9 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
|
|||||||
return EncodeCast(instruction);
|
return EncodeCast(instruction);
|
||||||
case Instruction::Op::kGetStaticField:
|
case Instruction::Op::kGetStaticField:
|
||||||
case Instruction::Op::kSetStaticField:
|
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());
|
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()) {
|
switch (instruction.opcode()) {
|
||||||
case Instruction::Op::kGetStaticField: {
|
case Instruction::Op::kGetStaticField: {
|
||||||
CHECK(instruction.dest().has_value());
|
CHECK(instruction.dest().has_value());
|
||||||
@@ -553,18 +563,36 @@ void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) {
|
|||||||
}
|
}
|
||||||
case Instruction::Op::kSetStaticField: {
|
case Instruction::Op::kSetStaticField: {
|
||||||
CHECK(!instruction.dest().has_value());
|
CHECK(!instruction.dest().has_value());
|
||||||
const auto& args = instruction.args();
|
|
||||||
CHECK_EQ(1, args.size());
|
CHECK_EQ(1, args.size());
|
||||||
CHECK(args[0].is_variable());
|
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]),
|
RegisterValue(args[0]),
|
||||||
instruction.index_argument());
|
instruction.index_argument());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
case Instruction::Op::kSetInstanceField: {
|
||||||
LOG(FATAL) << "Unsupported static field operation";
|
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"; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class Instruction {
|
|||||||
kBranchEqz,
|
kBranchEqz,
|
||||||
kBranchNEqz,
|
kBranchNEqz,
|
||||||
kCheckCast,
|
kCheckCast,
|
||||||
|
kGetInstanceField,
|
||||||
kGetStaticField,
|
kGetStaticField,
|
||||||
kInvokeDirect,
|
kInvokeDirect,
|
||||||
kInvokeInterface,
|
kInvokeInterface,
|
||||||
@@ -163,6 +164,7 @@ class Instruction {
|
|||||||
kNew,
|
kNew,
|
||||||
kReturn,
|
kReturn,
|
||||||
kReturnObject,
|
kReturnObject,
|
||||||
|
kSetInstanceField,
|
||||||
kSetStaticField
|
kSetStaticField
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -195,8 +197,9 @@ class Instruction {
|
|||||||
}
|
}
|
||||||
// Returns an object
|
// Returns an object
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest,
|
static inline Instruction InvokeVirtualObject(size_t index_argument,
|
||||||
Value this_arg, T... args) {
|
std::optional<const Value> dest, Value this_arg,
|
||||||
|
T... args) {
|
||||||
return Instruction{
|
return Instruction{
|
||||||
Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
|
Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
|
||||||
}
|
}
|
||||||
@@ -209,8 +212,9 @@ class Instruction {
|
|||||||
}
|
}
|
||||||
// Returns an object
|
// Returns an object
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeDirectObject(size_t index_argument, std::optional<const Value> dest,
|
static inline Instruction InvokeDirectObject(size_t index_argument,
|
||||||
Value this_arg, T... args) {
|
std::optional<const Value> dest, Value this_arg,
|
||||||
|
T... args) {
|
||||||
return Instruction{
|
return Instruction{
|
||||||
Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
|
Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
|
||||||
}
|
}
|
||||||
@@ -218,20 +222,21 @@ class Instruction {
|
|||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
|
static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
|
||||||
T... args) {
|
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
|
// Returns an object
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeStaticObject(size_t index_argument, std::optional<const Value> dest,
|
static inline Instruction InvokeStaticObject(size_t index_argument,
|
||||||
T... args) {
|
std::optional<const Value> dest, T... args) {
|
||||||
return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
|
return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
|
||||||
}
|
}
|
||||||
// For static calls.
|
// For static calls.
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
|
static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
|
||||||
T... args) {
|
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) {
|
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) {
|
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 //
|
// Accessors //
|
||||||
@@ -255,10 +269,14 @@ class Instruction {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
|
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>
|
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)
|
std::optional<const Value> dest, T... args)
|
||||||
: opcode_{opcode},
|
: opcode_{opcode},
|
||||||
index_argument_{index_argument},
|
index_argument_{index_argument},
|
||||||
@@ -331,7 +349,7 @@ class MethodBuilder {
|
|||||||
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
|
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
|
||||||
void EncodeNew(const Instruction& instruction);
|
void EncodeNew(const Instruction& instruction);
|
||||||
void EncodeCast(const Instruction& instruction);
|
void EncodeCast(const Instruction& instruction);
|
||||||
void EncodeStaticFieldOp(const Instruction& instruction);
|
void EncodeFieldOp(const Instruction& instruction);
|
||||||
|
|
||||||
// Low-level instruction format encoding. See
|
// Low-level instruction format encoding. See
|
||||||
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
|
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
|
||||||
@@ -364,6 +382,14 @@ class MethodBuilder {
|
|||||||
buffer_.push_back(b);
|
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) {
|
inline void Encode32x(art::Instruction::Code opcode, uint16_t a, uint16_t b) {
|
||||||
buffer_.push_back(opcode);
|
buffer_.push_back(opcode);
|
||||||
buffer_.push_back(a);
|
buffer_.push_back(a);
|
||||||
|
|||||||
@@ -186,4 +186,25 @@ public final class DexBuilderTest {
|
|||||||
method.invoke(null);
|
method.invoke(null);
|
||||||
Assert.assertEquals(7, TestClass.staticInteger);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,15 +282,15 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
method.Encode();
|
method.Encode();
|
||||||
}(castObjectToString);
|
}(castObjectToString);
|
||||||
|
|
||||||
|
TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass");
|
||||||
|
|
||||||
// Read a static field
|
// Read a static field
|
||||||
// integer readStaticField() { return TestClass.staticInteger; }
|
// int readStaticField() { return TestClass.staticInteger; }
|
||||||
MethodBuilder readStaticField{
|
MethodBuilder readStaticField{
|
||||||
cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
|
cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
const ir::FieldDecl* field =
|
const ir::FieldDecl* field =
|
||||||
dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
|
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
||||||
"staticInteger",
|
|
||||||
TypeDescriptor::Int());
|
|
||||||
Value result{method.MakeRegister()};
|
Value result{method.MakeRegister()};
|
||||||
method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
|
method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
|
||||||
method.BuildReturn(result, /*is_object=*/false);
|
method.BuildReturn(result, /*is_object=*/false);
|
||||||
@@ -303,9 +303,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
|
cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
const ir::FieldDecl* field =
|
const ir::FieldDecl* field =
|
||||||
dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
|
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
||||||
"staticInteger",
|
|
||||||
TypeDescriptor::Int());
|
|
||||||
Value number{method.MakeRegister()};
|
Value number{method.MakeRegister()};
|
||||||
method.BuildConst4(number, 7);
|
method.BuildConst4(number, 7);
|
||||||
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
|
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
|
||||||
@@ -313,6 +311,33 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
method.Encode();
|
method.Encode();
|
||||||
}(setStaticField);
|
}(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()};
|
slicer::MemView image{dex_file.CreateImage()};
|
||||||
std::ofstream out_file(outdir + "/simple.dex");
|
std::ofstream out_file(outdir + "/simple.dex");
|
||||||
out_file.write(image.ptr<const char>(), image.size());
|
out_file.write(image.ptr<const char>(), image.size());
|
||||||
|
|||||||
Reference in New Issue
Block a user