Refactor DexViewBuilder
The code was previously pretty messy and hard to follow. This reworks some things to make it more manageable. Among the changes: * Pull out fragments of code into their own method, so the high level steps are more apparent in methods like DexViewBuilder::Start. * Pull frequently used types into global constants in dex_layout_compiler.cc. * Rework register handling to track liveness so a strict stack discipline is no longer needed. Change-Id: Idb4b41f88c96a1ac4efb2c7b9bed05c2de0da999
This commit is contained in:
@@ -161,7 +161,7 @@ void WriteTestDexFile(const string& filename) {
|
||||
|
||||
MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
|
||||
|
||||
Value result = method.MakeRegister();
|
||||
LiveRegister result = method.AllocRegister();
|
||||
|
||||
MethodDeclData string_length =
|
||||
dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
|
||||
@@ -314,7 +314,7 @@ ir::EncodedMethod* MethodBuilder::Encode() {
|
||||
CHECK(decl_->prototype != nullptr);
|
||||
size_t const num_args =
|
||||
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;
|
||||
EncodeInstructions();
|
||||
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
|
||||
@@ -327,7 +327,20 @@ ir::EncodedMethod* MethodBuilder::Encode() {
|
||||
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() {
|
||||
labels_.push_back({});
|
||||
@@ -600,7 +613,7 @@ size_t MethodBuilder::RegisterValue(const Value& value) const {
|
||||
if (value.is_register()) {
|
||||
return value.value();
|
||||
} 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");
|
||||
return 0;
|
||||
|
||||
@@ -140,6 +140,29 @@ class Value {
|
||||
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.
|
||||
// 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
|
||||
@@ -178,7 +201,8 @@ class Instruction {
|
||||
}
|
||||
// For most instructions, which take some number of arguments and have an optional return value.
|
||||
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...};
|
||||
}
|
||||
|
||||
@@ -199,14 +223,14 @@ class Instruction {
|
||||
template <typename... T>
|
||||
static inline Instruction InvokeVirtualObject(size_t index_argument,
|
||||
std::optional<const Value> dest, Value this_arg,
|
||||
T... args) {
|
||||
const T&... args) {
|
||||
return Instruction{
|
||||
Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
|
||||
}
|
||||
// For direct calls (basically, constructors).
|
||||
template <typename... T>
|
||||
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{
|
||||
Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
|
||||
}
|
||||
@@ -234,7 +258,7 @@ class Instruction {
|
||||
// For static calls.
|
||||
template <typename... T>
|
||||
static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
|
||||
T... args) {
|
||||
const T&... args) {
|
||||
return Instruction{
|
||||
Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
|
||||
}
|
||||
@@ -277,7 +301,7 @@ class Instruction {
|
||||
|
||||
template <typename... T>
|
||||
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},
|
||||
index_argument_{index_argument},
|
||||
result_is_object_{result_is_object},
|
||||
@@ -309,10 +333,8 @@ class MethodBuilder {
|
||||
// Encode the method into DEX format.
|
||||
ir::EncodedMethod* Encode();
|
||||
|
||||
// Create a new register to be used to storing values. Note that these are not SSA registers, like
|
||||
// might be expected in similar code generators. This does no liveness tracking or anything, so
|
||||
// it's up to the caller to reuse registers as appropriate.
|
||||
Value MakeRegister();
|
||||
// Create a new register to be used to storing values.
|
||||
LiveRegister AllocRegister();
|
||||
|
||||
Value MakeLabel();
|
||||
|
||||
@@ -329,7 +351,7 @@ class MethodBuilder {
|
||||
void BuildConst4(Value target, int value);
|
||||
void BuildConstString(Value target, const std::string& value);
|
||||
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
|
||||
|
||||
@@ -427,7 +449,7 @@ class MethodBuilder {
|
||||
static_assert(num_regs <= kMaxScratchRegisters);
|
||||
std::array<Value, num_regs> regs;
|
||||
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;
|
||||
}
|
||||
@@ -457,8 +479,9 @@ class MethodBuilder {
|
||||
// around to make legal DEX code.
|
||||
static constexpr size_t kMaxScratchRegisters = 5;
|
||||
|
||||
// How many registers we've allocated
|
||||
size_t num_registers_{0};
|
||||
size_t NumRegisters() const {
|
||||
return register_liveness_.size();
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -478,6 +501,8 @@ class MethodBuilder {
|
||||
// During encoding, keep track of the largest number of arguments needed, so we can use it for our
|
||||
// outs count
|
||||
size_t max_args_{0};
|
||||
|
||||
std::vector<bool> register_liveness_;
|
||||
};
|
||||
|
||||
// A helper to build class definitions.
|
||||
@@ -576,7 +601,8 @@ class DexBuilder {
|
||||
};
|
||||
|
||||
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)};
|
||||
// allocate the object
|
||||
ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
|
||||
|
||||
@@ -22,76 +22,94 @@
|
||||
namespace startop {
|
||||
|
||||
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)
|
||||
: method_{method},
|
||||
context_{dex::Value::Parameter(0)},
|
||||
resid_{dex::Value::Parameter(1)},
|
||||
inflater_{method->MakeRegister()},
|
||||
xml_{method->MakeRegister()},
|
||||
attrs_{method->MakeRegister()},
|
||||
classname_tmp_{method->MakeRegister()},
|
||||
xml_next_{method->dex_file()->GetOrDeclareMethod(
|
||||
dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
|
||||
dex::Prototype{dex::TypeDescriptor::Int()})},
|
||||
context_{Value::Parameter(0)},
|
||||
resid_{Value::Parameter(1)},
|
||||
inflater_{method->AllocRegister()},
|
||||
xml_{method->AllocRegister()},
|
||||
attrs_{method->AllocRegister()},
|
||||
classname_tmp_{method->AllocRegister()},
|
||||
xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next",
|
||||
Prototype{TypeDescriptor::Int()})},
|
||||
try_create_view_{method->dex_file()->GetOrDeclareMethod(
|
||||
dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
|
||||
dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
|
||||
dex::TypeDescriptor::FromClassname("android.view.View"),
|
||||
dex::TypeDescriptor::FromClassname("java.lang.String"),
|
||||
dex::TypeDescriptor::FromClassname("android.content.Context"),
|
||||
dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
|
||||
kLayoutInflater, "tryCreateView",
|
||||
Prototype{kView, kView, kString, kContext, kAttributeSet})},
|
||||
generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
|
||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
|
||||
dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
|
||||
dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
|
||||
kViewGroup, "generateLayoutParams",
|
||||
Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
|
||||
kAttributeSet})},
|
||||
add_view_{method->dex_file()->GetOrDeclareMethod(
|
||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
|
||||
dex::Prototype{
|
||||
dex::TypeDescriptor::Void(),
|
||||
dex::TypeDescriptor::FromClassname("android.view.View"),
|
||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
|
||||
// The register stack starts with one register, which will be null for the root view.
|
||||
register_stack_{{method->MakeRegister()}} {}
|
||||
kViewGroup, "addView",
|
||||
Prototype{TypeDescriptor::Void(),
|
||||
kView,
|
||||
TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
|
||||
|
||||
void DexViewBuilder::BuildGetLayoutInflater(Value dest) {
|
||||
// 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() {
|
||||
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);
|
||||
auto layout_inflater_from = dex->GetOrDeclareMethod(
|
||||
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_));
|
||||
// Advance past start document tag
|
||||
BuildXmlNext();
|
||||
}
|
||||
|
||||
void DexViewBuilder::Finish() {}
|
||||
@@ -107,58 +125,57 @@ std::string ResolveName(const std::string& name) {
|
||||
}
|
||||
} // 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) {
|
||||
bool const is_root_view = view_stack_.empty();
|
||||
|
||||
// xml.next(); // start tag
|
||||
method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
||||
// Advance to start tag
|
||||
BuildXmlNext();
|
||||
|
||||
dex::Value view = AcquireRegister();
|
||||
LiveRegister view = AcquireRegister();
|
||||
// try to create the view using the factories
|
||||
method_->BuildConstString(classname_tmp_,
|
||||
name); // TODO: the need to fully qualify the classname
|
||||
if (is_root_view) {
|
||||
dex::Value null = AcquireRegister();
|
||||
LiveRegister null = AcquireRegister();
|
||||
method_->BuildConst4(null, 0);
|
||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
|
||||
try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
|
||||
ReleaseRegister();
|
||||
BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
|
||||
} else {
|
||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
|
||||
try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
|
||||
BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
|
||||
}
|
||||
auto label = method_->MakeLabel();
|
||||
// branch if not null
|
||||
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.
|
||||
method_->BuildNew(view,
|
||||
dex::TypeDescriptor::FromClassname(ResolveName(name)),
|
||||
dex::Prototype{dex::TypeDescriptor::Void(),
|
||||
dex::TypeDescriptor::FromClassname("android.content.Context"),
|
||||
dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
|
||||
TypeDescriptor::FromClassname(ResolveName(name)),
|
||||
Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
|
||||
context_,
|
||||
attrs_);
|
||||
|
||||
method_->AddInstruction(
|
||||
dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
|
||||
method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
|
||||
|
||||
if (is_viewgroup) {
|
||||
// Cast to a ViewGroup so we can add children later.
|
||||
const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
|
||||
dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
|
||||
method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
|
||||
const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor());
|
||||
method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index)));
|
||||
}
|
||||
|
||||
if (!is_root_view) {
|
||||
// layout_params = parent.generateLayoutParams(attrs);
|
||||
dex::Value layout_params{AcquireRegister()};
|
||||
method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
|
||||
LiveRegister layout_params{AcquireRegister()};
|
||||
method_->AddInstruction(Instruction::InvokeVirtualObject(
|
||||
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 {
|
||||
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);
|
||||
} else {
|
||||
// parent.add(view, layout_params)
|
||||
method_->AddInstruction(dex::Instruction::InvokeVirtual(
|
||||
method_->AddInstruction(Instruction::InvokeVirtual(
|
||||
add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
|
||||
// xml.next(); // end tag
|
||||
method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
||||
method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
||||
}
|
||||
PopViewStack();
|
||||
}
|
||||
|
||||
dex::Value DexViewBuilder::AcquireRegister() {
|
||||
top_register_++;
|
||||
if (register_stack_.size() == top_register_) {
|
||||
register_stack_.push_back(method_->MakeRegister());
|
||||
}
|
||||
return register_stack_[top_register_];
|
||||
}
|
||||
LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
|
||||
|
||||
void DexViewBuilder::ReleaseRegister() { top_register_--; }
|
||||
|
||||
dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
|
||||
dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
|
||||
Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
|
||||
Value DexViewBuilder::GetCurrentLayoutParams() const {
|
||||
return view_stack_.back().layout_params.value();
|
||||
}
|
||||
dex::Value DexViewBuilder::GetParentView() const {
|
||||
return view_stack_[view_stack_.size() - 2].view;
|
||||
}
|
||||
Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
|
||||
|
||||
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.
|
||||
ReleaseRegister();
|
||||
view_stack_.pop_back();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,36 +79,41 @@ class DexViewBuilder {
|
||||
|
||||
private:
|
||||
// Accessors for the stack of views that are under construction.
|
||||
dex::Value AcquireRegister();
|
||||
void ReleaseRegister();
|
||||
dex::LiveRegister AcquireRegister();
|
||||
dex::Value GetCurrentView() const;
|
||||
dex::Value GetCurrentLayoutParams() const;
|
||||
dex::Value GetParentView() const;
|
||||
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_;
|
||||
|
||||
// Registers used for code generation
|
||||
// Parameters to the generated method
|
||||
dex::Value const context_;
|
||||
dex::Value const resid_;
|
||||
const dex::Value inflater_;
|
||||
const dex::Value xml_;
|
||||
const dex::Value attrs_;
|
||||
const dex::Value classname_tmp_;
|
||||
|
||||
// Registers used for code generation
|
||||
const dex::LiveRegister inflater_;
|
||||
const dex::LiveRegister xml_;
|
||||
const dex::LiveRegister attrs_;
|
||||
const dex::LiveRegister classname_tmp_;
|
||||
|
||||
const dex::MethodDeclData xml_next_;
|
||||
const dex::MethodDeclData try_create_view_;
|
||||
const dex::MethodDeclData generate_layout_params_;
|
||||
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.
|
||||
struct ViewEntry {
|
||||
dex::Value view;
|
||||
std::optional<dex::Value> layout_params;
|
||||
dex::LiveRegister view;
|
||||
std::optional<dex::LiveRegister> layout_params;
|
||||
};
|
||||
std::vector<ViewEntry> view_stack_;
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
// int return5() { return 5; }
|
||||
auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
|
||||
{
|
||||
Value r{return5.MakeRegister()};
|
||||
LiveRegister r{return5.AllocRegister()};
|
||||
return5.BuildConst4(r, 5);
|
||||
return5.BuildReturn(r);
|
||||
}
|
||||
@@ -57,9 +57,9 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
|
||||
auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value five{method.MakeRegister()};
|
||||
LiveRegister five{method.AllocRegister()};
|
||||
method.BuildConst4(five, 5);
|
||||
Value object{method.MakeRegister()};
|
||||
LiveRegister object{method.AllocRegister()};
|
||||
method.BuildNew(
|
||||
object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
|
||||
method.BuildReturn(object, /*is_object=*/true);
|
||||
@@ -80,7 +80,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
auto returnStringLength{
|
||||
cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
|
||||
{
|
||||
Value result = returnStringLength.MakeRegister();
|
||||
LiveRegister result = returnStringLength.AllocRegister();
|
||||
returnStringLength.AddInstruction(
|
||||
Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
|
||||
returnStringLength.BuildReturn(result);
|
||||
@@ -91,7 +91,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
MethodBuilder returnIfZero{cbuilder.CreateMethod(
|
||||
"returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||
{
|
||||
Value resultIfZero{returnIfZero.MakeRegister()};
|
||||
LiveRegister resultIfZero{returnIfZero.AllocRegister()};
|
||||
Value else_target{returnIfZero.MakeLabel()};
|
||||
returnIfZero.AddInstruction(Instruction::OpWithArgs(
|
||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||
@@ -112,7 +112,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
|
||||
"returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||
{
|
||||
Value resultIfNotZero{returnIfNotZero.MakeRegister()};
|
||||
LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()};
|
||||
Value else_target{returnIfNotZero.MakeLabel()};
|
||||
returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
|
||||
Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||
@@ -148,8 +148,8 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
MethodBuilder backwardsBranch{
|
||||
cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})};
|
||||
[](MethodBuilder& method) {
|
||||
Value zero = method.MakeRegister();
|
||||
Value result = method.MakeRegister();
|
||||
LiveRegister zero = method.AllocRegister();
|
||||
LiveRegister result = method.AllocRegister();
|
||||
Value labelA = method.MakeLabel();
|
||||
Value labelB = method.MakeLabel();
|
||||
method.BuildConst4(zero, 0);
|
||||
@@ -177,7 +177,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
// public static String returnNull() { return null; }
|
||||
MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
|
||||
[](MethodBuilder& method) {
|
||||
Value zero = method.MakeRegister();
|
||||
LiveRegister zero = method.AllocRegister();
|
||||
method.BuildConst4(zero, 0);
|
||||
method.BuildReturn(zero, /*is_object=*/true);
|
||||
}(returnNull);
|
||||
@@ -188,7 +188,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
// public static String makeString() { return "Hello, World!"; }
|
||||
MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
|
||||
[](MethodBuilder& method) {
|
||||
Value string = method.MakeRegister();
|
||||
LiveRegister string = method.AllocRegister();
|
||||
method.BuildConstString(string, "Hello, World!");
|
||||
method.BuildReturn(string, /*is_object=*/true);
|
||||
}(makeString);
|
||||
@@ -200,7 +200,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
MethodBuilder returnStringIfZeroAB{
|
||||
cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value resultIfZero{method.MakeRegister()};
|
||||
LiveRegister resultIfZero{method.AllocRegister()};
|
||||
Value else_target{method.MakeLabel()};
|
||||
method.AddInstruction(Instruction::OpWithArgs(
|
||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||
@@ -220,7 +220,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
MethodBuilder returnStringIfZeroBA{
|
||||
cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value resultIfZero{method.MakeRegister()};
|
||||
LiveRegister resultIfZero{method.AllocRegister()};
|
||||
Value else_target{method.MakeLabel()};
|
||||
method.AddInstruction(Instruction::OpWithArgs(
|
||||
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
|
||||
@@ -244,7 +244,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
cbuilder.CreateMethod("invokeStaticReturnObject",
|
||||
Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value result{method.MakeRegister()};
|
||||
LiveRegister result{method.AllocRegister()};
|
||||
MethodDeclData to_string{dex_file.GetOrDeclareMethod(
|
||||
TypeDescriptor::FromClassname("java.lang.Integer"),
|
||||
"toString",
|
||||
@@ -260,7 +260,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
|
||||
"invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
|
||||
[&](MethodBuilder& method) {
|
||||
Value result{method.MakeRegister()};
|
||||
LiveRegister result{method.AllocRegister()};
|
||||
MethodDeclData substring{dex_file.GetOrDeclareMethod(
|
||||
string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
|
||||
method.AddInstruction(Instruction::InvokeVirtualObject(
|
||||
@@ -291,7 +291,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
[&](MethodBuilder& method) {
|
||||
const ir::FieldDecl* field =
|
||||
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.BuildReturn(result, /*is_object=*/false);
|
||||
method.Encode();
|
||||
@@ -304,7 +304,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
[&](MethodBuilder& method) {
|
||||
const ir::FieldDecl* field =
|
||||
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
|
||||
Value number{method.MakeRegister()};
|
||||
LiveRegister number{method.AllocRegister()};
|
||||
method.BuildConst4(number, 7);
|
||||
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
|
||||
method.BuildReturn();
|
||||
@@ -318,7 +318,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
[&](MethodBuilder& method) {
|
||||
const ir::FieldDecl* field =
|
||||
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.BuildReturn(result, /*is_object=*/false);
|
||||
method.Encode();
|
||||
@@ -331,7 +331,7 @@ void GenerateSimpleTestCases(const string& outdir) {
|
||||
[&](MethodBuilder& method) {
|
||||
const ir::FieldDecl* field =
|
||||
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
|
||||
Value number{method.MakeRegister()};
|
||||
LiveRegister number{method.AllocRegister()};
|
||||
method.BuildConst4(number, 7);
|
||||
method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
|
||||
method.BuildReturn();
|
||||
|
||||
Reference in New Issue
Block a user