[view_compiler] DexBuilder: add support for string literals and null pointers
Bug: 111895153 Change-Id: If6bdd9a280f6feed175be4da57733f3442e6dd14
This commit is contained in:
@@ -49,6 +49,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
|
||||
case Instruction::Op::kReturn:
|
||||
out << "kReturn";
|
||||
return out;
|
||||
case Instruction::Op::kReturnObject:
|
||||
out << "kReturnObject";
|
||||
return out;
|
||||
case Instruction::Op::kMove:
|
||||
out << "kMove";
|
||||
return out;
|
||||
@@ -137,6 +140,9 @@ ir::String* DexBuilder::GetOrAddString(const std::string& string) {
|
||||
entry = Alloc<ir::String>();
|
||||
// +1 for null terminator
|
||||
entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
|
||||
::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex();
|
||||
dex_file_->strings_map[new_index] = entry;
|
||||
entry->orig_index = new_index;
|
||||
string_data_.push_back(std::move(buffer));
|
||||
}
|
||||
return entry;
|
||||
@@ -240,8 +246,9 @@ void MethodBuilder::AddInstruction(Instruction instruction) {
|
||||
|
||||
void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
|
||||
|
||||
void MethodBuilder::BuildReturn(Value src) {
|
||||
AddInstruction(Instruction::OpWithArgs(Op::kReturn, /*destination=*/{}, src));
|
||||
void MethodBuilder::BuildReturn(Value src, bool is_object) {
|
||||
AddInstruction(Instruction::OpWithArgs(
|
||||
is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src));
|
||||
}
|
||||
|
||||
void MethodBuilder::BuildConst4(Value target, int value) {
|
||||
@@ -249,6 +256,11 @@ void MethodBuilder::BuildConst4(Value target, int value) {
|
||||
AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
|
||||
}
|
||||
|
||||
void MethodBuilder::BuildConstString(Value target, const std::string& value) {
|
||||
const ir::String* const dex_string = dex_->GetOrAddString(value);
|
||||
AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index)));
|
||||
}
|
||||
|
||||
void MethodBuilder::EncodeInstructions() {
|
||||
buffer_.clear();
|
||||
for (const auto& instruction : instructions_) {
|
||||
@@ -259,7 +271,9 @@ void MethodBuilder::EncodeInstructions() {
|
||||
void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
|
||||
switch (instruction.opcode()) {
|
||||
case Instruction::Op::kReturn:
|
||||
return EncodeReturn(instruction);
|
||||
return EncodeReturn(instruction, ::art::Instruction::RETURN);
|
||||
case Instruction::Op::kReturnObject:
|
||||
return EncodeReturn(instruction, ::art::Instruction::RETURN_OBJECT);
|
||||
case Instruction::Op::kMove:
|
||||
return EncodeMove(instruction);
|
||||
case Instruction::Op::kInvokeVirtual:
|
||||
@@ -271,15 +285,14 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
|
||||
}
|
||||
}
|
||||
|
||||
void MethodBuilder::EncodeReturn(const Instruction& instruction) {
|
||||
DCHECK_EQ(Instruction::Op::kReturn, instruction.opcode());
|
||||
void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
|
||||
DCHECK(!instruction.dest().has_value());
|
||||
if (instruction.args().size() == 0) {
|
||||
buffer_.push_back(art::Instruction::RETURN_VOID);
|
||||
} else {
|
||||
DCHECK(instruction.args().size() == 1);
|
||||
DCHECK_EQ(1, instruction.args().size());
|
||||
size_t source = RegisterValue(instruction.args()[0]);
|
||||
buffer_.push_back(art::Instruction::RETURN | source << 8);
|
||||
buffer_.push_back(opcode | source << 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,6 +310,12 @@ void MethodBuilder::EncodeMove(const Instruction& instruction) {
|
||||
DCHECK_LT(source.value(), 16);
|
||||
buffer_.push_back(art::Instruction::CONST_4 | (source.value() << 12) |
|
||||
(RegisterValue(*instruction.dest()) << 8));
|
||||
} else if (source.is_string()) {
|
||||
constexpr size_t kMaxRegisters = 256;
|
||||
DCHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
|
||||
DCHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string
|
||||
buffer_.push_back(::art::Instruction::CONST_STRING | (RegisterValue(*instruction.dest()) << 8));
|
||||
buffer_.push_back(source.value());
|
||||
} else {
|
||||
UNIMPLEMENTED(FATAL);
|
||||
}
|
||||
|
||||
@@ -110,18 +110,20 @@ class Value {
|
||||
static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
|
||||
static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
|
||||
static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
|
||||
static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
|
||||
static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
|
||||
|
||||
bool is_register() const { return kind_ == Kind::kLocalRegister; }
|
||||
bool is_parameter() const { return kind_ == Kind::kParameter; }
|
||||
bool is_variable() const { return is_register() || is_parameter(); }
|
||||
bool is_immediate() const { return kind_ == Kind::kImmediate; }
|
||||
bool is_string() const { return kind_ == Kind::kString; }
|
||||
bool is_label() const { return kind_ == Kind::kLabel; }
|
||||
|
||||
size_t value() const { return value_; }
|
||||
|
||||
private:
|
||||
enum class Kind { kLocalRegister, kParameter, kImmediate, kLabel };
|
||||
enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel };
|
||||
|
||||
const size_t value_;
|
||||
const Kind kind_;
|
||||
@@ -137,7 +139,7 @@ class Instruction {
|
||||
public:
|
||||
// The operation performed by this instruction. These are virtual instructions that do not
|
||||
// correspond exactly to DEX instructions.
|
||||
enum class Op { kReturn, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
|
||||
enum class Op { kReturn, kReturnObject, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
|
||||
|
||||
////////////////////////
|
||||
// Named Constructors //
|
||||
@@ -210,16 +212,22 @@ class MethodBuilder {
|
||||
|
||||
// return-void
|
||||
void BuildReturn();
|
||||
void BuildReturn(Value src);
|
||||
void BuildReturn(Value src, bool is_object = false);
|
||||
// const/4
|
||||
void BuildConst4(Value target, int value);
|
||||
void BuildConstString(Value target, const std::string& value);
|
||||
|
||||
// TODO: add builders for more instructions
|
||||
|
||||
private:
|
||||
void EncodeInstructions();
|
||||
void EncodeInstruction(const Instruction& instruction);
|
||||
void EncodeReturn(const Instruction& instruction);
|
||||
|
||||
// Encodes a return instruction. For instructions with no return value, the opcode field is
|
||||
// ignored. Otherwise, this specifies which return instruction will be used (return,
|
||||
// return-object, etc.)
|
||||
void EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode);
|
||||
|
||||
void EncodeMove(const Instruction& instruction);
|
||||
void EncodeInvokeVirtual(const Instruction& instruction);
|
||||
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
|
||||
|
||||
@@ -82,4 +82,38 @@ public class DexBuilderTest {
|
||||
Method method = clazz.getMethod("backwardsBranch");
|
||||
Assert.assertEquals(2, method.invoke(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnNull() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("returnNull");
|
||||
Assert.assertEquals(null, method.invoke(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void makeString() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("makeString");
|
||||
Assert.assertEquals("Hello, World!", method.invoke(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnStringIfZeroAB() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("returnStringIfZeroAB", int.class);
|
||||
Assert.assertEquals("a", method.invoke(null, 0));
|
||||
Assert.assertEquals("b", method.invoke(null, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnStringIfZeroBA() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("returnStringIfZeroBA", int.class);
|
||||
Assert.assertEquals("b", method.invoke(null, 0));
|
||||
Assert.assertEquals("a", method.invoke(null, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,71 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
}(backwardsBranch);
|
||||
backwardsBranch.Encode();
|
||||
|
||||
// Test that we can make a null value. Basically:
|
||||
//
|
||||
// public static String returnNull() { return null; }
|
||||
MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
|
||||
[](MethodBuilder& method) {
|
||||
Value zero = method.MakeRegister();
|
||||
method.BuildConst4(zero, 0);
|
||||
method.BuildReturn(zero, /*is_object=*/true);
|
||||
}(returnNull);
|
||||
returnNull.Encode();
|
||||
|
||||
// Test that we can make String literals. Basically:
|
||||
//
|
||||
// public static String makeString() { return "Hello, World!"; }
|
||||
MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
|
||||
[](MethodBuilder& method) {
|
||||
Value string = method.MakeRegister();
|
||||
method.BuildConstString(string, "Hello, World!");
|
||||
method.BuildReturn(string, /*is_object=*/true);
|
||||
}(makeString);
|
||||
makeString.Encode();
|
||||
|
||||
// Make sure strings are sorted correctly.
|
||||
//
|
||||
// int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } }
|
||||
MethodBuilder returnStringIfZeroAB{
|
||||
cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value resultIfZero{method.MakeRegister()};
|
||||
Value else_target{method.MakeLabel()};
|
||||
method.AddInstruction(Instruction::OpWithArgs(
|
||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||
// else branch
|
||||
method.BuildConstString(resultIfZero, "b");
|
||||
method.AddInstruction(
|
||||
Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
|
||||
// then branch
|
||||
method.AddInstruction(
|
||||
Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
|
||||
method.BuildConstString(resultIfZero, "a");
|
||||
method.AddInstruction(
|
||||
Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
|
||||
method.Encode();
|
||||
}(returnStringIfZeroAB);
|
||||
// int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } }
|
||||
MethodBuilder returnStringIfZeroBA{
|
||||
cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value resultIfZero{method.MakeRegister()};
|
||||
Value else_target{method.MakeLabel()};
|
||||
method.AddInstruction(Instruction::OpWithArgs(
|
||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||
// else branch
|
||||
method.BuildConstString(resultIfZero, "a");
|
||||
method.AddInstruction(
|
||||
Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
|
||||
// then branch
|
||||
method.AddInstruction(
|
||||
Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
|
||||
method.BuildConstString(resultIfZero, "b");
|
||||
method.AddInstruction(
|
||||
Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
|
||||
method.Encode();
|
||||
}(returnStringIfZeroBA);
|
||||
|
||||
slicer::MemView image{dex_file.CreateImage()};
|
||||
std::ofstream out_file(outdir + "/simple.dex");
|
||||
out_file.write(image.ptr<const char>(), image.size());
|
||||
|
||||
Reference in New Issue
Block a user