Loading startop/view_compiler/dex_builder.cc +34 −6 Original line number Diff line number Diff line Loading @@ -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; } } Loading Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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()); Loading @@ -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"; } } } Loading startop/view_compiler/dex_builder.h +39 −13 Original line number Diff line number Diff line Loading @@ -153,6 +153,7 @@ class Instruction { kBranchEqz, kBranchNEqz, kCheckCast, kGetInstanceField, kGetStaticField, kInvokeDirect, kInvokeInterface, Loading @@ -163,6 +164,7 @@ class Instruction { kNew, kReturn, kReturnObject, kSetInstanceField, kSetStaticField }; Loading Loading @@ -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...}; } Loading @@ -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...}; } Loading @@ -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) { Loading @@ -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 // Loading @@ -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}, Loading Loading @@ -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 Loading Loading @@ -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); Loading startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +21 −0 Original line number Diff line number Diff line Loading @@ -190,4 +190,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); } } startop/view_compiler/dex_testcase_generator.cc +32 −7 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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)); Loading @@ -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()); Loading Loading
startop/view_compiler/dex_builder.cc +34 −6 Original line number Diff line number Diff line Loading @@ -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; } } Loading Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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()); Loading @@ -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"; } } } Loading
startop/view_compiler/dex_builder.h +39 −13 Original line number Diff line number Diff line Loading @@ -153,6 +153,7 @@ class Instruction { kBranchEqz, kBranchNEqz, kCheckCast, kGetInstanceField, kGetStaticField, kInvokeDirect, kInvokeInterface, Loading @@ -163,6 +164,7 @@ class Instruction { kNew, kReturn, kReturnObject, kSetInstanceField, kSetStaticField }; Loading Loading @@ -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...}; } Loading @@ -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...}; } Loading @@ -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) { Loading @@ -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 // Loading @@ -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}, Loading Loading @@ -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 Loading Loading @@ -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); Loading
startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +21 −0 Original line number Diff line number Diff line Loading @@ -190,4 +190,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); } }
startop/view_compiler/dex_testcase_generator.cc +32 −7 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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)); Loading @@ -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()); Loading