Merge "Refactor DexViewBuilder" am: f5617abe09 am: 0ea5d04aff
am: 75f7f68128
Change-Id: I53afd6879f86b710f272bfa9ee8d45ec908029ce
This commit is contained in:
@@ -161,7 +161,7 @@ void WriteTestDexFile(const string& filename) {
|
|||||||
|
|
||||||
MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
|
MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
|
||||||
|
|
||||||
Value result = method.MakeRegister();
|
LiveRegister result = method.AllocRegister();
|
||||||
|
|
||||||
MethodDeclData string_length =
|
MethodDeclData string_length =
|
||||||
dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
|
dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
|
||||||
@@ -314,7 +314,7 @@ ir::EncodedMethod* MethodBuilder::Encode() {
|
|||||||
CHECK(decl_->prototype != nullptr);
|
CHECK(decl_->prototype != nullptr);
|
||||||
size_t const num_args =
|
size_t const num_args =
|
||||||
decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
|
decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
|
||||||
code->registers = num_registers_ + num_args + kMaxScratchRegisters;
|
code->registers = NumRegisters() + num_args + kMaxScratchRegisters;
|
||||||
code->ins_count = num_args;
|
code->ins_count = num_args;
|
||||||
EncodeInstructions();
|
EncodeInstructions();
|
||||||
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
|
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
|
||||||
@@ -327,7 +327,20 @@ ir::EncodedMethod* MethodBuilder::Encode() {
|
|||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value MethodBuilder::MakeRegister() { return Value::Local(num_registers_++); }
|
LiveRegister MethodBuilder::AllocRegister() {
|
||||||
|
// Find a free register
|
||||||
|
for (size_t i = 0; i < register_liveness_.size(); ++i) {
|
||||||
|
if (!register_liveness_[i]) {
|
||||||
|
register_liveness_[i] = true;
|
||||||
|
return LiveRegister{®ister_liveness_, i};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, all the registers are in use, so we have to allocate a new
|
||||||
|
// one.
|
||||||
|
register_liveness_.push_back(true);
|
||||||
|
return LiveRegister{®ister_liveness_, register_liveness_.size() - 1};
|
||||||
|
}
|
||||||
|
|
||||||
Value MethodBuilder::MakeLabel() {
|
Value MethodBuilder::MakeLabel() {
|
||||||
labels_.push_back({});
|
labels_.push_back({});
|
||||||
@@ -600,7 +613,7 @@ size_t MethodBuilder::RegisterValue(const Value& value) const {
|
|||||||
if (value.is_register()) {
|
if (value.is_register()) {
|
||||||
return value.value();
|
return value.value();
|
||||||
} else if (value.is_parameter()) {
|
} else if (value.is_parameter()) {
|
||||||
return value.value() + num_registers_ + kMaxScratchRegisters;
|
return value.value() + NumRegisters() + kMaxScratchRegisters;
|
||||||
}
|
}
|
||||||
CHECK(false && "Must be either a parameter or a register");
|
CHECK(false && "Must be either a parameter or a register");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -140,6 +140,29 @@ class Value {
|
|||||||
constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
|
constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Represents an allocated register returned by MethodBuilder::AllocRegister
|
||||||
|
class LiveRegister {
|
||||||
|
friend class MethodBuilder;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} {
|
||||||
|
other.index_ = {};
|
||||||
|
};
|
||||||
|
~LiveRegister() {
|
||||||
|
if (index_.has_value()) {
|
||||||
|
(*liveness_)[*index_] = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
operator const Value() const { return Value::Local(*index_); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {}
|
||||||
|
|
||||||
|
std::vector<bool>* const liveness_;
|
||||||
|
std::optional<size_t> index_;
|
||||||
|
};
|
||||||
|
|
||||||
// A virtual instruction. We convert these to real instructions in MethodBuilder::Encode.
|
// A virtual instruction. We convert these to real instructions in MethodBuilder::Encode.
|
||||||
// Virtual instructions are needed to keep track of information that is not known until all of the
|
// Virtual instructions are needed to keep track of information that is not known until all of the
|
||||||
// code is generated. This information includes things like how many local registers are created and
|
// code is generated. This information includes things like how many local registers are created and
|
||||||
@@ -178,7 +201,8 @@ class Instruction {
|
|||||||
}
|
}
|
||||||
// For most instructions, which take some number of arguments and have an optional return value.
|
// For most instructions, which take some number of arguments and have an optional return value.
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
|
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest,
|
||||||
|
const T&... args) {
|
||||||
return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
|
return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,14 +223,14 @@ class Instruction {
|
|||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeVirtualObject(size_t index_argument,
|
static inline Instruction InvokeVirtualObject(size_t index_argument,
|
||||||
std::optional<const Value> dest, Value this_arg,
|
std::optional<const Value> dest, Value this_arg,
|
||||||
T... args) {
|
const 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...};
|
||||||
}
|
}
|
||||||
// For direct calls (basically, constructors).
|
// For direct calls (basically, constructors).
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
|
static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
|
||||||
Value this_arg, T... args) {
|
Value this_arg, const T&... args) {
|
||||||
return Instruction{
|
return Instruction{
|
||||||
Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
|
Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
|
||||||
}
|
}
|
||||||
@@ -234,7 +258,7 @@ class Instruction {
|
|||||||
// 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) {
|
const T&... args) {
|
||||||
return Instruction{
|
return Instruction{
|
||||||
Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
|
Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
|
||||||
}
|
}
|
||||||
@@ -277,7 +301,7 @@ class Instruction {
|
|||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
inline 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, const T&... args)
|
||||||
: opcode_{opcode},
|
: opcode_{opcode},
|
||||||
index_argument_{index_argument},
|
index_argument_{index_argument},
|
||||||
result_is_object_{result_is_object},
|
result_is_object_{result_is_object},
|
||||||
@@ -309,10 +333,8 @@ class MethodBuilder {
|
|||||||
// Encode the method into DEX format.
|
// Encode the method into DEX format.
|
||||||
ir::EncodedMethod* Encode();
|
ir::EncodedMethod* Encode();
|
||||||
|
|
||||||
// Create a new register to be used to storing values. Note that these are not SSA registers, like
|
// Create a new register to be used to storing values.
|
||||||
// might be expected in similar code generators. This does no liveness tracking or anything, so
|
LiveRegister AllocRegister();
|
||||||
// it's up to the caller to reuse registers as appropriate.
|
|
||||||
Value MakeRegister();
|
|
||||||
|
|
||||||
Value MakeLabel();
|
Value MakeLabel();
|
||||||
|
|
||||||
@@ -329,7 +351,7 @@ class MethodBuilder {
|
|||||||
void BuildConst4(Value target, int value);
|
void BuildConst4(Value target, int value);
|
||||||
void BuildConstString(Value target, const std::string& value);
|
void BuildConstString(Value target, const std::string& value);
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args);
|
void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args);
|
||||||
|
|
||||||
// TODO: add builders for more instructions
|
// TODO: add builders for more instructions
|
||||||
|
|
||||||
@@ -427,7 +449,7 @@ class MethodBuilder {
|
|||||||
static_assert(num_regs <= kMaxScratchRegisters);
|
static_assert(num_regs <= kMaxScratchRegisters);
|
||||||
std::array<Value, num_regs> regs;
|
std::array<Value, num_regs> regs;
|
||||||
for (size_t i = 0; i < num_regs; ++i) {
|
for (size_t i = 0; i < num_regs; ++i) {
|
||||||
regs[i] = std::move(Value::Local(num_registers_ + i));
|
regs[i] = std::move(Value::Local(NumRegisters() + i));
|
||||||
}
|
}
|
||||||
return regs;
|
return regs;
|
||||||
}
|
}
|
||||||
@@ -457,8 +479,9 @@ class MethodBuilder {
|
|||||||
// around to make legal DEX code.
|
// around to make legal DEX code.
|
||||||
static constexpr size_t kMaxScratchRegisters = 5;
|
static constexpr size_t kMaxScratchRegisters = 5;
|
||||||
|
|
||||||
// How many registers we've allocated
|
size_t NumRegisters() const {
|
||||||
size_t num_registers_{0};
|
return register_liveness_.size();
|
||||||
|
}
|
||||||
|
|
||||||
// Stores information needed to back-patch a label once it is bound. We need to know the start of
|
// Stores information needed to back-patch a label once it is bound. We need to know the start of
|
||||||
// the instruction that refers to the label, and the offset to where the actual label value should
|
// the instruction that refers to the label, and the offset to where the actual label value should
|
||||||
@@ -478,6 +501,8 @@ class MethodBuilder {
|
|||||||
// During encoding, keep track of the largest number of arguments needed, so we can use it for our
|
// During encoding, keep track of the largest number of arguments needed, so we can use it for our
|
||||||
// outs count
|
// outs count
|
||||||
size_t max_args_{0};
|
size_t max_args_{0};
|
||||||
|
|
||||||
|
std::vector<bool> register_liveness_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A helper to build class definitions.
|
// A helper to build class definitions.
|
||||||
@@ -576,7 +601,8 @@ class DexBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) {
|
void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor,
|
||||||
|
const T&... args) {
|
||||||
MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
|
MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
|
||||||
// allocate the object
|
// allocate the object
|
||||||
ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
|
ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
|
||||||
|
|||||||
@@ -22,76 +22,94 @@
|
|||||||
namespace startop {
|
namespace startop {
|
||||||
|
|
||||||
using android::base::StringPrintf;
|
using android::base::StringPrintf;
|
||||||
|
using dex::Instruction;
|
||||||
|
using dex::LiveRegister;
|
||||||
|
using dex::Prototype;
|
||||||
|
using dex::TypeDescriptor;
|
||||||
|
using dex::Value;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// TODO: these are a bunch of static initializers, which we should avoid. See if
|
||||||
|
// we can make them constexpr.
|
||||||
|
const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet");
|
||||||
|
const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context");
|
||||||
|
const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater");
|
||||||
|
const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources");
|
||||||
|
const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String");
|
||||||
|
const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View");
|
||||||
|
const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup");
|
||||||
|
const TypeDescriptor kXmlResourceParser =
|
||||||
|
TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
|
||||||
|
} // namespace
|
||||||
|
|
||||||
DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
|
DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
|
||||||
: method_{method},
|
: method_{method},
|
||||||
context_{dex::Value::Parameter(0)},
|
context_{Value::Parameter(0)},
|
||||||
resid_{dex::Value::Parameter(1)},
|
resid_{Value::Parameter(1)},
|
||||||
inflater_{method->MakeRegister()},
|
inflater_{method->AllocRegister()},
|
||||||
xml_{method->MakeRegister()},
|
xml_{method->AllocRegister()},
|
||||||
attrs_{method->MakeRegister()},
|
attrs_{method->AllocRegister()},
|
||||||
classname_tmp_{method->MakeRegister()},
|
classname_tmp_{method->AllocRegister()},
|
||||||
xml_next_{method->dex_file()->GetOrDeclareMethod(
|
xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next",
|
||||||
dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
|
Prototype{TypeDescriptor::Int()})},
|
||||||
dex::Prototype{dex::TypeDescriptor::Int()})},
|
|
||||||
try_create_view_{method->dex_file()->GetOrDeclareMethod(
|
try_create_view_{method->dex_file()->GetOrDeclareMethod(
|
||||||
dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
|
kLayoutInflater, "tryCreateView",
|
||||||
dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
|
Prototype{kView, kView, kString, kContext, kAttributeSet})},
|
||||||
dex::TypeDescriptor::FromClassname("android.view.View"),
|
|
||||||
dex::TypeDescriptor::FromClassname("java.lang.String"),
|
|
||||||
dex::TypeDescriptor::FromClassname("android.content.Context"),
|
|
||||||
dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
|
|
||||||
generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
|
generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
|
||||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
|
kViewGroup, "generateLayoutParams",
|
||||||
dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
|
Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
|
||||||
dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
|
kAttributeSet})},
|
||||||
add_view_{method->dex_file()->GetOrDeclareMethod(
|
add_view_{method->dex_file()->GetOrDeclareMethod(
|
||||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
|
kViewGroup, "addView",
|
||||||
dex::Prototype{
|
Prototype{TypeDescriptor::Void(),
|
||||||
dex::TypeDescriptor::Void(),
|
kView,
|
||||||
dex::TypeDescriptor::FromClassname("android.view.View"),
|
TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
|
||||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
|
|
||||||
// The register stack starts with one register, which will be null for the root view.
|
void DexViewBuilder::BuildGetLayoutInflater(Value dest) {
|
||||||
register_stack_{{method->MakeRegister()}} {}
|
// dest = LayoutInflater.from(context);
|
||||||
|
auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod(
|
||||||
|
kLayoutInflater, "from", Prototype{kLayoutInflater, kContext});
|
||||||
|
method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DexViewBuilder::BuildGetResources(Value dest) {
|
||||||
|
// dest = context.getResources();
|
||||||
|
auto get_resources =
|
||||||
|
method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources});
|
||||||
|
method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) {
|
||||||
|
// dest = resources.getLayout(resid);
|
||||||
|
auto get_layout = method_->dex_file()->GetOrDeclareMethod(
|
||||||
|
kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()});
|
||||||
|
method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest,
|
||||||
|
dex::Value layout_resource) {
|
||||||
|
// dest = Xml.asAttributeSet(layout_resource);
|
||||||
|
auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod(
|
||||||
|
TypeDescriptor::FromClassname("android.util.Xml"),
|
||||||
|
"asAttributeSet",
|
||||||
|
Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
|
||||||
|
method_->AddInstruction(
|
||||||
|
Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DexViewBuilder::BuildXmlNext() {
|
||||||
|
// xml_.next();
|
||||||
|
method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
||||||
|
}
|
||||||
|
|
||||||
void DexViewBuilder::Start() {
|
void DexViewBuilder::Start() {
|
||||||
dex::DexBuilder* const dex = method_->dex_file();
|
BuildGetLayoutInflater(/*dest=*/inflater_);
|
||||||
|
BuildGetResources(/*dest=*/xml_);
|
||||||
|
BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
|
||||||
|
BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
|
||||||
|
|
||||||
// LayoutInflater inflater = LayoutInflater.from(context);
|
// Advance past start document tag
|
||||||
auto layout_inflater_from = dex->GetOrDeclareMethod(
|
BuildXmlNext();
|
||||||
dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
|
|
||||||
"from",
|
|
||||||
dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
|
|
||||||
dex::TypeDescriptor::FromClassname("android.content.Context")});
|
|
||||||
method_->AddInstruction(
|
|
||||||
dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_));
|
|
||||||
|
|
||||||
// Resources res = context.getResources();
|
|
||||||
auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
|
|
||||||
auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
|
|
||||||
auto get_resources =
|
|
||||||
dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type});
|
|
||||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_));
|
|
||||||
|
|
||||||
// XmlResourceParser xml = res.getLayout(resid);
|
|
||||||
auto xml_resource_parser_type =
|
|
||||||
dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
|
|
||||||
auto get_layout =
|
|
||||||
dex->GetOrDeclareMethod(resources_type,
|
|
||||||
"getLayout",
|
|
||||||
dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()});
|
|
||||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_));
|
|
||||||
|
|
||||||
// AttributeSet attrs = Xml.asAttributeSet(xml);
|
|
||||||
auto as_attribute_set = dex->GetOrDeclareMethod(
|
|
||||||
dex::TypeDescriptor::FromClassname("android.util.Xml"),
|
|
||||||
"asAttributeSet",
|
|
||||||
dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"),
|
|
||||||
dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
|
|
||||||
method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_));
|
|
||||||
|
|
||||||
// xml.next(); // start document
|
|
||||||
method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DexViewBuilder::Finish() {}
|
void DexViewBuilder::Finish() {}
|
||||||
@@ -107,58 +125,57 @@ std::string ResolveName(const std::string& name) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) {
|
||||||
|
// dest = inflater_.tryCreateView(parent, classname, context_, attrs_);
|
||||||
|
method_->AddInstruction(Instruction::InvokeVirtualObject(
|
||||||
|
try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_));
|
||||||
|
}
|
||||||
|
|
||||||
void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
|
void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
|
||||||
bool const is_root_view = view_stack_.empty();
|
bool const is_root_view = view_stack_.empty();
|
||||||
|
|
||||||
// xml.next(); // start tag
|
// Advance to start tag
|
||||||
method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
BuildXmlNext();
|
||||||
|
|
||||||
dex::Value view = AcquireRegister();
|
LiveRegister view = AcquireRegister();
|
||||||
// try to create the view using the factories
|
// try to create the view using the factories
|
||||||
method_->BuildConstString(classname_tmp_,
|
method_->BuildConstString(classname_tmp_,
|
||||||
name); // TODO: the need to fully qualify the classname
|
name); // TODO: the need to fully qualify the classname
|
||||||
if (is_root_view) {
|
if (is_root_view) {
|
||||||
dex::Value null = AcquireRegister();
|
LiveRegister null = AcquireRegister();
|
||||||
method_->BuildConst4(null, 0);
|
method_->BuildConst4(null, 0);
|
||||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
|
BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
|
||||||
try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
|
|
||||||
ReleaseRegister();
|
|
||||||
} else {
|
} else {
|
||||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
|
BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
|
||||||
try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
|
|
||||||
}
|
}
|
||||||
auto label = method_->MakeLabel();
|
auto label = method_->MakeLabel();
|
||||||
// branch if not null
|
// branch if not null
|
||||||
method_->AddInstruction(
|
method_->AddInstruction(
|
||||||
dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
|
Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
|
||||||
|
|
||||||
// If null, create the class directly.
|
// If null, create the class directly.
|
||||||
method_->BuildNew(view,
|
method_->BuildNew(view,
|
||||||
dex::TypeDescriptor::FromClassname(ResolveName(name)),
|
TypeDescriptor::FromClassname(ResolveName(name)),
|
||||||
dex::Prototype{dex::TypeDescriptor::Void(),
|
Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
|
||||||
dex::TypeDescriptor::FromClassname("android.content.Context"),
|
|
||||||
dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
|
|
||||||
context_,
|
context_,
|
||||||
attrs_);
|
attrs_);
|
||||||
|
|
||||||
method_->AddInstruction(
|
method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
|
||||||
dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
|
|
||||||
|
|
||||||
if (is_viewgroup) {
|
if (is_viewgroup) {
|
||||||
// Cast to a ViewGroup so we can add children later.
|
// Cast to a ViewGroup so we can add children later.
|
||||||
const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
|
const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor());
|
||||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
|
method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index)));
|
||||||
method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_root_view) {
|
if (!is_root_view) {
|
||||||
// layout_params = parent.generateLayoutParams(attrs);
|
// layout_params = parent.generateLayoutParams(attrs);
|
||||||
dex::Value layout_params{AcquireRegister()};
|
LiveRegister layout_params{AcquireRegister()};
|
||||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
|
method_->AddInstruction(Instruction::InvokeVirtualObject(
|
||||||
generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
|
generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
|
||||||
view_stack_.push_back({view, layout_params});
|
view_stack_.push_back({std::move(view), std::move(layout_params)});
|
||||||
} else {
|
} else {
|
||||||
view_stack_.push_back({view, {}});
|
view_stack_.push_back({std::move(view), {}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,40 +184,24 @@ void DexViewBuilder::FinishView() {
|
|||||||
method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
|
method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
|
||||||
} else {
|
} else {
|
||||||
// parent.add(view, layout_params)
|
// parent.add(view, layout_params)
|
||||||
method_->AddInstruction(dex::Instruction::InvokeVirtual(
|
method_->AddInstruction(Instruction::InvokeVirtual(
|
||||||
add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
|
add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
|
||||||
// xml.next(); // end tag
|
// xml.next(); // end tag
|
||||||
method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
||||||
}
|
}
|
||||||
PopViewStack();
|
PopViewStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
dex::Value DexViewBuilder::AcquireRegister() {
|
LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
|
||||||
top_register_++;
|
|
||||||
if (register_stack_.size() == top_register_) {
|
|
||||||
register_stack_.push_back(method_->MakeRegister());
|
|
||||||
}
|
|
||||||
return register_stack_[top_register_];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DexViewBuilder::ReleaseRegister() { top_register_--; }
|
Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
|
||||||
|
Value DexViewBuilder::GetCurrentLayoutParams() const {
|
||||||
dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
|
|
||||||
dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
|
|
||||||
return view_stack_.back().layout_params.value();
|
return view_stack_.back().layout_params.value();
|
||||||
}
|
}
|
||||||
dex::Value DexViewBuilder::GetParentView() const {
|
Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
|
||||||
return view_stack_[view_stack_.size() - 2].view;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DexViewBuilder::PopViewStack() {
|
void DexViewBuilder::PopViewStack() {
|
||||||
const auto& top = view_stack_.back();
|
|
||||||
// release the layout params if we have them
|
|
||||||
if (top.layout_params.has_value()) {
|
|
||||||
ReleaseRegister();
|
|
||||||
}
|
|
||||||
// Unconditionally release the view register.
|
// Unconditionally release the view register.
|
||||||
ReleaseRegister();
|
|
||||||
view_stack_.pop_back();
|
view_stack_.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,36 +79,41 @@ class DexViewBuilder {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Accessors for the stack of views that are under construction.
|
// Accessors for the stack of views that are under construction.
|
||||||
dex::Value AcquireRegister();
|
dex::LiveRegister AcquireRegister();
|
||||||
void ReleaseRegister();
|
|
||||||
dex::Value GetCurrentView() const;
|
dex::Value GetCurrentView() const;
|
||||||
dex::Value GetCurrentLayoutParams() const;
|
dex::Value GetCurrentLayoutParams() const;
|
||||||
dex::Value GetParentView() const;
|
dex::Value GetParentView() const;
|
||||||
void PopViewStack();
|
void PopViewStack();
|
||||||
|
|
||||||
|
// Methods to simplify building different code fragments.
|
||||||
|
void BuildGetLayoutInflater(dex::Value dest);
|
||||||
|
void BuildGetResources(dex::Value dest);
|
||||||
|
void BuildGetLayoutResource(dex::Value dest, dex::Value resources, dex::Value resid);
|
||||||
|
void BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource);
|
||||||
|
void BuildXmlNext();
|
||||||
|
void BuildTryCreateView(dex::Value dest, dex::Value parent, dex::Value classname);
|
||||||
|
|
||||||
dex::MethodBuilder* method_;
|
dex::MethodBuilder* method_;
|
||||||
|
|
||||||
// Registers used for code generation
|
// Parameters to the generated method
|
||||||
dex::Value const context_;
|
dex::Value const context_;
|
||||||
dex::Value const resid_;
|
dex::Value const resid_;
|
||||||
const dex::Value inflater_;
|
|
||||||
const dex::Value xml_;
|
// Registers used for code generation
|
||||||
const dex::Value attrs_;
|
const dex::LiveRegister inflater_;
|
||||||
const dex::Value classname_tmp_;
|
const dex::LiveRegister xml_;
|
||||||
|
const dex::LiveRegister attrs_;
|
||||||
|
const dex::LiveRegister classname_tmp_;
|
||||||
|
|
||||||
const dex::MethodDeclData xml_next_;
|
const dex::MethodDeclData xml_next_;
|
||||||
const dex::MethodDeclData try_create_view_;
|
const dex::MethodDeclData try_create_view_;
|
||||||
const dex::MethodDeclData generate_layout_params_;
|
const dex::MethodDeclData generate_layout_params_;
|
||||||
const dex::MethodDeclData add_view_;
|
const dex::MethodDeclData add_view_;
|
||||||
|
|
||||||
// used for keeping track of which registers are in use
|
|
||||||
size_t top_register_{0};
|
|
||||||
std::vector<dex::Value> register_stack_;
|
|
||||||
|
|
||||||
// Keep track of the views currently in progress.
|
// Keep track of the views currently in progress.
|
||||||
struct ViewEntry {
|
struct ViewEntry {
|
||||||
dex::Value view;
|
dex::LiveRegister view;
|
||||||
std::optional<dex::Value> layout_params;
|
std::optional<dex::LiveRegister> layout_params;
|
||||||
};
|
};
|
||||||
std::vector<ViewEntry> view_stack_;
|
std::vector<ViewEntry> view_stack_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
// int return5() { return 5; }
|
// int return5() { return 5; }
|
||||||
auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
|
auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
|
||||||
{
|
{
|
||||||
Value r{return5.MakeRegister()};
|
LiveRegister r{return5.AllocRegister()};
|
||||||
return5.BuildConst4(r, 5);
|
return5.BuildConst4(r, 5);
|
||||||
return5.BuildReturn(r);
|
return5.BuildReturn(r);
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,9 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
|
auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
|
||||||
auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
|
auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
Value five{method.MakeRegister()};
|
LiveRegister five{method.AllocRegister()};
|
||||||
method.BuildConst4(five, 5);
|
method.BuildConst4(five, 5);
|
||||||
Value object{method.MakeRegister()};
|
LiveRegister object{method.AllocRegister()};
|
||||||
method.BuildNew(
|
method.BuildNew(
|
||||||
object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
|
object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
|
||||||
method.BuildReturn(object, /*is_object=*/true);
|
method.BuildReturn(object, /*is_object=*/true);
|
||||||
@@ -80,7 +80,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
auto returnStringLength{
|
auto returnStringLength{
|
||||||
cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
|
cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
|
||||||
{
|
{
|
||||||
Value result = returnStringLength.MakeRegister();
|
LiveRegister result = returnStringLength.AllocRegister();
|
||||||
returnStringLength.AddInstruction(
|
returnStringLength.AddInstruction(
|
||||||
Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
|
Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
|
||||||
returnStringLength.BuildReturn(result);
|
returnStringLength.BuildReturn(result);
|
||||||
@@ -91,7 +91,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
MethodBuilder returnIfZero{cbuilder.CreateMethod(
|
MethodBuilder returnIfZero{cbuilder.CreateMethod(
|
||||||
"returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
"returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||||
{
|
{
|
||||||
Value resultIfZero{returnIfZero.MakeRegister()};
|
LiveRegister resultIfZero{returnIfZero.AllocRegister()};
|
||||||
Value else_target{returnIfZero.MakeLabel()};
|
Value else_target{returnIfZero.MakeLabel()};
|
||||||
returnIfZero.AddInstruction(Instruction::OpWithArgs(
|
returnIfZero.AddInstruction(Instruction::OpWithArgs(
|
||||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||||
@@ -112,7 +112,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
|
MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
|
||||||
"returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
"returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||||
{
|
{
|
||||||
Value resultIfNotZero{returnIfNotZero.MakeRegister()};
|
LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()};
|
||||||
Value else_target{returnIfNotZero.MakeLabel()};
|
Value else_target{returnIfNotZero.MakeLabel()};
|
||||||
returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
|
returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
|
||||||
Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||||
@@ -148,8 +148,8 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
MethodBuilder backwardsBranch{
|
MethodBuilder backwardsBranch{
|
||||||
cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})};
|
cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})};
|
||||||
[](MethodBuilder& method) {
|
[](MethodBuilder& method) {
|
||||||
Value zero = method.MakeRegister();
|
LiveRegister zero = method.AllocRegister();
|
||||||
Value result = method.MakeRegister();
|
LiveRegister result = method.AllocRegister();
|
||||||
Value labelA = method.MakeLabel();
|
Value labelA = method.MakeLabel();
|
||||||
Value labelB = method.MakeLabel();
|
Value labelB = method.MakeLabel();
|
||||||
method.BuildConst4(zero, 0);
|
method.BuildConst4(zero, 0);
|
||||||
@@ -177,7 +177,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
// public static String returnNull() { return null; }
|
// public static String returnNull() { return null; }
|
||||||
MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
|
MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
|
||||||
[](MethodBuilder& method) {
|
[](MethodBuilder& method) {
|
||||||
Value zero = method.MakeRegister();
|
LiveRegister zero = method.AllocRegister();
|
||||||
method.BuildConst4(zero, 0);
|
method.BuildConst4(zero, 0);
|
||||||
method.BuildReturn(zero, /*is_object=*/true);
|
method.BuildReturn(zero, /*is_object=*/true);
|
||||||
}(returnNull);
|
}(returnNull);
|
||||||
@@ -188,7 +188,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
// public static String makeString() { return "Hello, World!"; }
|
// public static String makeString() { return "Hello, World!"; }
|
||||||
MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
|
MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
|
||||||
[](MethodBuilder& method) {
|
[](MethodBuilder& method) {
|
||||||
Value string = method.MakeRegister();
|
LiveRegister string = method.AllocRegister();
|
||||||
method.BuildConstString(string, "Hello, World!");
|
method.BuildConstString(string, "Hello, World!");
|
||||||
method.BuildReturn(string, /*is_object=*/true);
|
method.BuildReturn(string, /*is_object=*/true);
|
||||||
}(makeString);
|
}(makeString);
|
||||||
@@ -200,7 +200,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
MethodBuilder returnStringIfZeroAB{
|
MethodBuilder returnStringIfZeroAB{
|
||||||
cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
|
cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
Value resultIfZero{method.MakeRegister()};
|
LiveRegister resultIfZero{method.AllocRegister()};
|
||||||
Value else_target{method.MakeLabel()};
|
Value else_target{method.MakeLabel()};
|
||||||
method.AddInstruction(Instruction::OpWithArgs(
|
method.AddInstruction(Instruction::OpWithArgs(
|
||||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||||
@@ -220,7 +220,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
MethodBuilder returnStringIfZeroBA{
|
MethodBuilder returnStringIfZeroBA{
|
||||||
cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
|
cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
Value resultIfZero{method.MakeRegister()};
|
LiveRegister resultIfZero{method.AllocRegister()};
|
||||||
Value else_target{method.MakeLabel()};
|
Value else_target{method.MakeLabel()};
|
||||||
method.AddInstruction(Instruction::OpWithArgs(
|
method.AddInstruction(Instruction::OpWithArgs(
|
||||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||||
@@ -244,7 +244,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
cbuilder.CreateMethod("invokeStaticReturnObject",
|
cbuilder.CreateMethod("invokeStaticReturnObject",
|
||||||
Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
Value result{method.MakeRegister()};
|
LiveRegister result{method.AllocRegister()};
|
||||||
MethodDeclData to_string{dex_file.GetOrDeclareMethod(
|
MethodDeclData to_string{dex_file.GetOrDeclareMethod(
|
||||||
TypeDescriptor::FromClassname("java.lang.Integer"),
|
TypeDescriptor::FromClassname("java.lang.Integer"),
|
||||||
"toString",
|
"toString",
|
||||||
@@ -260,7 +260,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
|
MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
|
||||||
"invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
|
"invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
|
||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
Value result{method.MakeRegister()};
|
LiveRegister result{method.AllocRegister()};
|
||||||
MethodDeclData substring{dex_file.GetOrDeclareMethod(
|
MethodDeclData substring{dex_file.GetOrDeclareMethod(
|
||||||
string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
|
string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
|
||||||
method.AddInstruction(Instruction::InvokeVirtualObject(
|
method.AddInstruction(Instruction::InvokeVirtualObject(
|
||||||
@@ -291,7 +291,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
const ir::FieldDecl* field =
|
const ir::FieldDecl* field =
|
||||||
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
||||||
Value result{method.MakeRegister()};
|
LiveRegister result{method.AllocRegister()};
|
||||||
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);
|
||||||
method.Encode();
|
method.Encode();
|
||||||
@@ -304,7 +304,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
const ir::FieldDecl* field =
|
const ir::FieldDecl* field =
|
||||||
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
||||||
Value number{method.MakeRegister()};
|
LiveRegister number{method.AllocRegister()};
|
||||||
method.BuildConst4(number, 7);
|
method.BuildConst4(number, 7);
|
||||||
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
|
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
|
||||||
method.BuildReturn();
|
method.BuildReturn();
|
||||||
@@ -318,7 +318,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
const ir::FieldDecl* field =
|
const ir::FieldDecl* field =
|
||||||
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
|
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
|
||||||
Value result{method.MakeRegister()};
|
LiveRegister result{method.AllocRegister()};
|
||||||
method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
|
method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
|
||||||
method.BuildReturn(result, /*is_object=*/false);
|
method.BuildReturn(result, /*is_object=*/false);
|
||||||
method.Encode();
|
method.Encode();
|
||||||
@@ -331,7 +331,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
[&](MethodBuilder& method) {
|
[&](MethodBuilder& method) {
|
||||||
const ir::FieldDecl* field =
|
const ir::FieldDecl* field =
|
||||||
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
|
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
|
||||||
Value number{method.MakeRegister()};
|
LiveRegister number{method.AllocRegister()};
|
||||||
method.BuildConst4(number, 7);
|
method.BuildConst4(number, 7);
|
||||||
method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
|
method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
|
||||||
method.BuildReturn();
|
method.BuildReturn();
|
||||||
|
|||||||
Reference in New Issue
Block a user