Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5617abe authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Refactor DexViewBuilder"

parents 37f8bfb2 5c6a1a51
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -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{&register_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{&register_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;
+40 −14
Original line number Diff line number Diff line
@@ -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());
+106 −105
Original line number Diff line number Diff line
@@ -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()}} {}

void DexViewBuilder::Start() {
  dex::DexBuilder* const dex = method_->dex_file();

  // 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_));
          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_));
}

  // Resources res = context.getResources();
  auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
  auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
void DexViewBuilder::BuildGetResources(Value dest) {
  // dest = context.getResources();
  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"),
      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",
      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_));
      Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
  method_->AddInstruction(
      Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
}

  // xml.next(); // start document
  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
void DexViewBuilder::BuildXmlNext() {
  // xml_.next();
  method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
}

void DexViewBuilder::Start() {
  BuildGetLayoutInflater(/*dest=*/inflater_);
  BuildGetResources(/*dest=*/xml_);
  BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
  BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/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_];
}

void DexViewBuilder::ReleaseRegister() { top_register_--; }
LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }

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

+18 −13
Original line number Diff line number Diff line
@@ -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_;
};
+18 −18

File changed.

Preview size limit exceeded, changes collapsed.