[view_compiler] DexBuilder: add support for string literals and null pointers

Bug: 111895153
Change-Id: If6bdd9a280f6feed175be4da57733f3442e6dd14
This commit is contained in:
Eric Holk
2018-11-08 14:16:20 -08:00
parent 24df195e78
commit 3cc4afc610
4 changed files with 137 additions and 11 deletions

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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));
}
}

View File

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