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

Commit 75f7f681 authored by Eric Holk's avatar Eric Holk Committed by android-build-merger
Browse files

Merge "Refactor DexViewBuilder" am: f5617abe

am: 0ea5d04a

Change-Id: Ic16bb1298ac85276d1119b3c9a69dc1e527edb39
parents a722ba3c 0ea5d04a
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.