1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef DEX_BUILDER_H_
17 #define DEX_BUILDER_H_
18 
19 #include <array>
20 #include <forward_list>
21 #include <map>
22 #include <optional>
23 #include <string>
24 #include <unordered_map>
25 #include <vector>
26 
27 #include "android-base/logging.h"
28 
29 #include "slicer/dex_bytecode.h"
30 #include "slicer/dex_ir.h"
31 #include "slicer/writer.h"
32 
33 namespace startop {
34 namespace dex {
35 
36 // TODO: remove this once the dex generation code is complete.
37 void WriteTestDexFile(const std::string& filename);
38 
39 //////////////////////////
40 // Forward declarations //
41 //////////////////////////
42 class DexBuilder;
43 
44 // Our custom allocator for dex::Writer
45 //
46 // This keeps track of all allocations and ensures they are freed when
47 // TrackingAllocator is destroyed. Pointers to memory allocated by this
48 // allocator must not outlive the allocator.
49 class TrackingAllocator : public ::dex::Writer::Allocator {
50  public:
51   virtual void* Allocate(size_t size);
52   virtual void Free(void* ptr);
53 
54  private:
55   std::unordered_map<void*, std::unique_ptr<uint8_t[]>> allocations_;
56 };
57 
58 // Represents a DEX type descriptor.
59 //
60 // TODO: add a way to create a descriptor for a reference of a class type.
61 class TypeDescriptor {
62  public:
63   // Named constructors for base type descriptors.
64   static const TypeDescriptor Int();
65   static const TypeDescriptor Void();
66 
67   // Creates a type descriptor from a fully-qualified class name. For example, it turns the class
68   // name java.lang.Object into the descriptor Ljava/lang/Object.
69   static TypeDescriptor FromClassname(const std::string& name);
70 
71   // Return the full descriptor, such as I or Ljava/lang/Object
descriptor()72   const std::string& descriptor() const { return descriptor_; }
73   // Return the shorty descriptor, such as I or L
short_descriptor()74   std::string short_descriptor() const { return descriptor().substr(0, 1); }
75 
is_object()76   bool is_object() const { return short_descriptor() == "L"; }
77 
78   bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; }
79 
80  private:
TypeDescriptor(std::string descriptor)81   explicit TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
82 
83   const std::string descriptor_;
84 };
85 
86 // Defines a function signature. For example, Prototype{TypeDescriptor::VOID, TypeDescriptor::Int}
87 // represents the function type (Int) -> Void.
88 class Prototype {
89  public:
90   template <typename... TypeDescriptors>
Prototype(TypeDescriptor return_type,TypeDescriptors...param_types)91   explicit Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
92       : return_type_{return_type}, param_types_{param_types...} {}
93 
94   // Encode this prototype into the dex file.
95   ir::Proto* Encode(DexBuilder* dex) const;
96 
97   // Get the shorty descriptor, such as VII for (Int, Int) -> Void
98   std::string Shorty() const;
99 
100   const TypeDescriptor& ArgType(size_t index) const;
101 
102   bool operator<(const Prototype& rhs) const {
103     return std::make_tuple(return_type_, param_types_) <
104            std::make_tuple(rhs.return_type_, rhs.param_types_);
105   }
106 
107  private:
108   const TypeDescriptor return_type_;
109   const std::vector<TypeDescriptor> param_types_;
110 };
111 
112 // Represents a DEX register or constant. We separate regular registers and parameters
113 // because we will not know the real parameter id until after all instructions
114 // have been generated.
115 class Value {
116  public:
Local(size_t id)117   static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
Parameter(size_t id)118   static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
Immediate(size_t value)119   static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
String(size_t value)120   static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
Label(size_t id)121   static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
Type(size_t id)122   static constexpr Value Type(size_t id) { return Value{id, Kind::kType}; }
123 
is_register()124   bool is_register() const { return kind_ == Kind::kLocalRegister; }
is_parameter()125   bool is_parameter() const { return kind_ == Kind::kParameter; }
is_variable()126   bool is_variable() const { return is_register() || is_parameter(); }
is_immediate()127   bool is_immediate() const { return kind_ == Kind::kImmediate; }
is_string()128   bool is_string() const { return kind_ == Kind::kString; }
is_label()129   bool is_label() const { return kind_ == Kind::kLabel; }
is_type()130   bool is_type() const { return kind_ == Kind::kType; }
131 
value()132   size_t value() const { return value_; }
133 
Value()134   constexpr Value() : value_{0}, kind_{Kind::kInvalid} {}
135 
136  private:
137   enum class Kind { kInvalid, kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
138 
139   size_t value_;
140   Kind kind_;
141 
Value(size_t value,Kind kind)142   constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
143 };
144 
145 // Represents an allocated register returned by MethodBuilder::AllocRegister
146 class LiveRegister {
147   friend class MethodBuilder;
148 
149  public:
LiveRegister(LiveRegister && other)150   LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} {
151     other.index_ = {};
152   };
~LiveRegister()153   ~LiveRegister() {
154     if (index_.has_value()) {
155       (*liveness_)[*index_] = false;
156     }
157   };
158 
Value()159   operator const Value() const { return Value::Local(*index_); }
160 
161  private:
LiveRegister(std::vector<bool> * liveness,size_t index)162   LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {}
163 
164   std::vector<bool>* const liveness_;
165   std::optional<size_t> index_;
166 };
167 
168 // A virtual instruction. We convert these to real instructions in MethodBuilder::Encode.
169 // Virtual instructions are needed to keep track of information that is not known until all of the
170 // code is generated. This information includes things like how many local registers are created and
171 // branch target locations.
172 class Instruction {
173  public:
174   // The operation performed by this instruction. These are virtual instructions that do not
175   // correspond exactly to DEX instructions.
176   enum class Op {
177     kBindLabel,
178     kBranchEqz,
179     kBranchNEqz,
180     kCheckCast,
181     kGetInstanceField,
182     kGetStaticField,
183     kInvokeDirect,
184     kInvokeInterface,
185     kInvokeStatic,
186     kInvokeVirtual,
187     kMove,
188     kMoveObject,
189     kNew,
190     kReturn,
191     kReturnObject,
192     kSetInstanceField,
193     kSetStaticField
194   };
195 
196   ////////////////////////
197   // Named Constructors //
198   ////////////////////////
199 
200   // For instructions with no return value and no arguments.
OpNoArgs(Op opcode)201   static inline Instruction OpNoArgs(Op opcode) {
202     return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}};
203   }
204   // For most instructions, which take some number of arguments and have an optional return value.
205   template <typename... T>
OpWithArgs(Op opcode,std::optional<const Value> dest,const T &...args)206   static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest,
207                                        const T&... args) {
208     return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
209   }
210 
211   // A cast instruction. Basically, `(type)val`
Cast(Value val,Value type)212   static inline Instruction Cast(Value val, Value type) {
213     CHECK(type.is_type());
214     return OpWithArgs(Op::kCheckCast, val, type);
215   }
216 
217   // For method calls.
218   template <typename... T>
InvokeVirtual(size_t index_argument,std::optional<const Value> dest,Value this_arg,T...args)219   static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest,
220                                           Value this_arg, T... args) {
221     return Instruction{
222         Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
223   }
224   // Returns an object
225   template <typename... T>
InvokeVirtualObject(size_t index_argument,std::optional<const Value> dest,Value this_arg,const T &...args)226   static inline Instruction InvokeVirtualObject(size_t index_argument,
227                                                 std::optional<const Value> dest, Value this_arg,
228                                                 const T&... args) {
229     return Instruction{
230         Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
231   }
232   // For direct calls (basically, constructors).
233   template <typename... T>
InvokeDirect(size_t index_argument,std::optional<const Value> dest,Value this_arg,const T &...args)234   static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
235                                          Value this_arg, const T&... args) {
236     return Instruction{
237         Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
238   }
239   // Returns an object
240   template <typename... T>
InvokeDirectObject(size_t index_argument,std::optional<const Value> dest,Value this_arg,T...args)241   static inline Instruction InvokeDirectObject(size_t index_argument,
242                                                std::optional<const Value> dest, Value this_arg,
243                                                T... args) {
244     return Instruction{
245         Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
246   }
247   // For static calls.
248   template <typename... T>
InvokeStatic(size_t index_argument,std::optional<const Value> dest,T...args)249   static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
250                                          T... args) {
251     return Instruction{
252         Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
253   }
254   // Returns an object
255   template <typename... T>
InvokeStaticObject(size_t index_argument,std::optional<const Value> dest,T...args)256   static inline Instruction InvokeStaticObject(size_t index_argument,
257                                                std::optional<const Value> dest, T... args) {
258     return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
259   }
260   // For static calls.
261   template <typename... T>
InvokeInterface(size_t index_argument,std::optional<const Value> dest,const T &...args)262   static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
263                                             const T&... args) {
264     return Instruction{
265         Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
266   }
267 
GetStaticField(size_t field_id,Value dest)268   static inline Instruction GetStaticField(size_t field_id, Value dest) {
269     return Instruction{Op::kGetStaticField, field_id, dest};
270   }
271 
SetStaticField(size_t field_id,Value value)272   static inline Instruction SetStaticField(size_t field_id, Value value) {
273     return Instruction{
274         Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
275   }
276 
GetField(size_t field_id,Value dest,Value object)277   static inline Instruction GetField(size_t field_id, Value dest, Value object) {
278     return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object};
279   }
280 
SetField(size_t field_id,Value object,Value value)281   static inline Instruction SetField(size_t field_id, Value object, Value value) {
282     return Instruction{
283         Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value};
284   }
285 
286   ///////////////
287   // Accessors //
288   ///////////////
289 
opcode()290   Op opcode() const { return opcode_; }
index_argument()291   size_t index_argument() const { return index_argument_; }
result_is_object()292   bool result_is_object() const { return result_is_object_; }
dest()293   const std::optional<const Value>& dest() const { return dest_; }
args()294   const std::vector<const Value>& args() const { return args_; }
295 
296  private:
Instruction(Op opcode,size_t index_argument,std::optional<const Value> dest)297   inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
298       : opcode_{opcode},
299         index_argument_{index_argument},
300         result_is_object_{false},
301         dest_{dest},
302         args_{} {}
303 
304   template <typename... T>
Instruction(Op opcode,size_t index_argument,bool result_is_object,std::optional<const Value> dest,const T &...args)305   inline Instruction(Op opcode, size_t index_argument, bool result_is_object,
306                      std::optional<const Value> dest, const T&... args)
307       : opcode_{opcode},
308         index_argument_{index_argument},
309         result_is_object_{result_is_object},
310         dest_{dest},
311         args_{args...} {}
312 
313   const Op opcode_;
314   // The index of the method to invoke, for kInvokeVirtual and similar opcodes.
315   const size_t index_argument_{0};
316   const bool result_is_object_;
317   const std::optional<const Value> dest_;
318   const std::vector<const Value> args_;
319 };
320 
321 // Needed for CHECK_EQ, DCHECK_EQ, etc.
322 std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode);
323 
324 // Keeps track of information needed to manipulate or call a method.
325 struct MethodDeclData {
326   size_t id;
327   ir::MethodDecl* decl;
328 };
329 
330 // Tools to help build methods and their bodies.
331 class MethodBuilder {
332  public:
333   MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl);
334 
335   // Encode the method into DEX format.
336   ir::EncodedMethod* Encode();
337 
338   // Create a new register to be used to storing values.
339   LiveRegister AllocRegister();
340 
341   Value MakeLabel();
342 
343   /////////////////////////////////
344   // Instruction builder methods //
345   /////////////////////////////////
346 
347   void AddInstruction(Instruction instruction);
348 
349   // return-void
350   void BuildReturn();
351   void BuildReturn(Value src, bool is_object = false);
352   // const/4
353   void BuildConst4(Value target, int value);
354   void BuildConstString(Value target, const std::string& value);
355   template <typename... T>
356   void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args);
357 
358   // TODO: add builders for more instructions
359 
dex_file()360   DexBuilder* dex_file() const { return dex_; }
361 
362  private:
363   void EncodeInstructions();
364   void EncodeInstruction(const Instruction& instruction);
365 
366   // Encodes a return instruction. For instructions with no return value, the opcode field is
367   // ignored. Otherwise, this specifies which return instruction will be used (return,
368   // return-object, etc.)
369   void EncodeReturn(const Instruction& instruction, ::dex::Opcode opcode);
370 
371   void EncodeMove(const Instruction& instruction);
372   void EncodeInvoke(const Instruction& instruction, ::dex::Opcode opcode);
373   void EncodeBranch(::dex::Opcode op, const Instruction& instruction);
374   void EncodeNew(const Instruction& instruction);
375   void EncodeCast(const Instruction& instruction);
376   void EncodeFieldOp(const Instruction& instruction);
377 
378   // Low-level instruction format encoding. See
379   // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
380   // formats.
381 
ToBits(::dex::Opcode opcode)382   inline uint8_t ToBits(::dex::Opcode opcode) {
383     static_assert(sizeof(uint8_t) == sizeof(::dex::Opcode));
384     return static_cast<uint8_t>(opcode);
385   }
386 
Encode10x(::dex::Opcode opcode)387   inline void Encode10x(::dex::Opcode opcode) {
388     // 00|op
389     static_assert(sizeof(uint8_t) == sizeof(::dex::Opcode));
390     buffer_.push_back(ToBits(opcode));
391   }
392 
Encode11x(::dex::Opcode opcode,uint8_t a)393   inline void Encode11x(::dex::Opcode opcode, uint8_t a) {
394     // aa|op
395     buffer_.push_back((a << 8) | ToBits(opcode));
396   }
397 
Encode11n(::dex::Opcode opcode,uint8_t a,int8_t b)398   inline void Encode11n(::dex::Opcode opcode, uint8_t a, int8_t b) {
399     // b|a|op
400 
401     // Make sure the fields are in bounds (4 bits for a, 4 bits for b).
402     CHECK_LT(a, 16);
403     CHECK_LE(-8, b);
404     CHECK_LT(b, 8);
405 
406     buffer_.push_back(((b & 0xf) << 12) | (a << 8) | ToBits(opcode));
407   }
408 
Encode21c(::dex::Opcode opcode,uint8_t a,uint16_t b)409   inline void Encode21c(::dex::Opcode opcode, uint8_t a, uint16_t b) {
410     // aa|op|bbbb
411     buffer_.push_back((a << 8) | ToBits(opcode));
412     buffer_.push_back(b);
413   }
414 
Encode22c(::dex::Opcode opcode,uint8_t a,uint8_t b,uint16_t c)415   inline void Encode22c(::dex::Opcode opcode, uint8_t a, uint8_t b, uint16_t c) {
416     // b|a|op|bbbb
417     CHECK(IsShortRegister(a));
418     CHECK(IsShortRegister(b));
419     buffer_.push_back((b << 12) | (a << 8) | ToBits(opcode));
420     buffer_.push_back(c);
421   }
422 
Encode32x(::dex::Opcode opcode,uint16_t a,uint16_t b)423   inline void Encode32x(::dex::Opcode opcode, uint16_t a, uint16_t b) {
424     buffer_.push_back(ToBits(opcode));
425     buffer_.push_back(a);
426     buffer_.push_back(b);
427   }
428 
Encode35c(::dex::Opcode opcode,size_t a,uint16_t b,uint8_t c,uint8_t d,uint8_t e,uint8_t f,uint8_t g)429   inline void Encode35c(::dex::Opcode opcode, size_t a, uint16_t b, uint8_t c, uint8_t d,
430                         uint8_t e, uint8_t f, uint8_t g) {
431     // a|g|op|bbbb|f|e|d|c
432 
433     CHECK_LE(a, 5);
434     CHECK(IsShortRegister(c));
435     CHECK(IsShortRegister(d));
436     CHECK(IsShortRegister(e));
437     CHECK(IsShortRegister(f));
438     CHECK(IsShortRegister(g));
439     buffer_.push_back((a << 12) | (g << 8) | ToBits(opcode));
440     buffer_.push_back(b);
441     buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c);
442   }
443 
Encode3rc(::dex::Opcode opcode,size_t a,uint16_t b,uint16_t c)444   inline void Encode3rc(::dex::Opcode opcode, size_t a, uint16_t b, uint16_t c) {
445     CHECK_LE(a, 255);
446     buffer_.push_back((a << 8) | ToBits(opcode));
447     buffer_.push_back(b);
448     buffer_.push_back(c);
449   }
450 
IsShortRegister(size_t register_value)451   static constexpr bool IsShortRegister(size_t register_value) { return register_value < 16; }
452 
453   // Returns an array of num_regs scratch registers. These are guaranteed to be
454   // contiguous, so they are suitable for the invoke-*/range instructions.
455   template <int num_regs>
GetScratchRegisters()456   std::array<Value, num_regs> GetScratchRegisters() const {
457     static_assert(num_regs <= kMaxScratchRegisters);
458     std::array<Value, num_regs> regs;
459     for (size_t i = 0; i < num_regs; ++i) {
460       regs[i] = std::move(Value::Local(NumRegisters() + i));
461     }
462     return regs;
463   }
464 
465   // Converts a register or parameter to its DEX register number.
466   size_t RegisterValue(const Value& value) const;
467 
468   // Sets a label's address to the current position in the instruction buffer. If there are any
469   // forward references to the label, this function will back-patch them.
470   void BindLabel(const Value& label);
471 
472   // Returns the offset of the label relative to the given instruction offset. If the label is not
473   // bound, a reference will be saved and it will automatically be patched when the label is bound.
474   ::dex::u2 LabelValue(const Value& label, size_t instruction_offset, size_t field_offset);
475 
476   DexBuilder* dex_;
477   ir::Class* class_;
478   ir::MethodDecl* decl_;
479 
480   // A list of the instructions we will eventually encode.
481   std::vector<Instruction> instructions_;
482 
483   // A buffer to hold instructions that have been encoded.
484   std::vector<::dex::u2> buffer_;
485 
486   // We create some scratch registers for when we have to shuffle registers
487   // around to make legal DEX code.
488   static constexpr size_t kMaxScratchRegisters = 5;
489 
NumRegisters()490   size_t NumRegisters() const {
491     return register_liveness_.size();
492   }
493 
494   // Stores information needed to back-patch a label once it is bound. We need to know the start of
495   // the instruction that refers to the label, and the offset to where the actual label value should
496   // go.
497   struct LabelReference {
498     size_t instruction_offset;
499     size_t field_offset;
500   };
501 
502   struct LabelData {
503     std::optional<size_t> bound_address;
504     std::forward_list<LabelReference> references;
505   };
506 
507   std::vector<LabelData> labels_;
508 
509   // During encoding, keep track of the largest number of arguments needed, so we can use it for our
510   // outs count
511   size_t max_args_{0};
512 
513   std::vector<bool> register_liveness_;
514 };
515 
516 // A helper to build class definitions.
517 class ClassBuilder {
518  public:
519   ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def);
520 
521   void set_source_file(const std::string& source);
522 
523   // Create a method with the given name and prototype. The returned MethodBuilder can be used to
524   // fill in the method body.
525   MethodBuilder CreateMethod(const std::string& name, Prototype prototype);
526 
527  private:
528   DexBuilder* const parent_;
529   const TypeDescriptor type_descriptor_;
530   ir::Class* const class_;
531 };
532 
533 // Builds Dex files from scratch.
534 class DexBuilder {
535  public:
536   DexBuilder();
537 
538   // Create an in-memory image of the DEX file that can either be loaded directly or written to a
539   // file.
540   slicer::MemView CreateImage();
541 
542   template <typename T>
Alloc()543   T* Alloc() {
544     return dex_file_->Alloc<T>();
545   }
546 
547   // Find the ir::String that matches the given string, creating it if it does not exist.
548   ir::String* GetOrAddString(const std::string& string);
549   // Create a new class of the given name.
550   ClassBuilder MakeClass(const std::string& name);
551 
552   // Add a type for the given descriptor, or return the existing one if it already exists.
553   // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
554   // imported classes.
555   ir::Type* GetOrAddType(const std::string& descriptor);
GetOrAddType(TypeDescriptor descriptor)556   inline ir::Type* GetOrAddType(TypeDescriptor descriptor) {
557     return GetOrAddType(descriptor.descriptor());
558   }
559 
560   ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type);
561 
562   // Returns the method id for the method, creating it if it has not been created yet.
563   const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
564                                            Prototype prototype);
565 
566   std::optional<const Prototype> GetPrototypeByMethodId(size_t method_id) const;
567 
568  private:
569   // Looks up the ir::Proto* corresponding to this given prototype, or creates one if it does not
570   // exist.
571   ir::Proto* GetOrEncodeProto(Prototype prototype);
572 
573   std::shared_ptr<ir::DexFile> dex_file_;
574 
575   // allocator_ is needed to be able to encode the image.
576   TrackingAllocator allocator_;
577 
578   // We'll need to allocate buffers for all of the encoded strings we create. This is where we store
579   // all of them.
580   std::vector<std::unique_ptr<uint8_t[]>> string_data_;
581 
582   // Keep track of what types we've defined so we can look them up later.
583   std::unordered_map<std::string, ir::Type*> types_by_descriptor_;
584 
585   struct MethodDescriptor {
586     TypeDescriptor type;
587     std::string name;
588     Prototype prototype;
589 
590     inline bool operator<(const MethodDescriptor& rhs) const {
591       return std::make_tuple(type, name, prototype) <
592              std::make_tuple(rhs.type, rhs.name, rhs.prototype);
593     }
594   };
595 
596   // Maps method declarations to their method index. This is needed to encode references to them.
597   // When we go to actually write the DEX file, slicer will re-assign these after correctly sorting
598   // the methods list.
599   std::map<MethodDescriptor, MethodDeclData> method_id_map_;
600 
601   // Keep track of what strings we've defined so we can look them up later.
602   std::unordered_map<std::string, ir::String*> strings_;
603 
604   // Keep track of already-encoded protos.
605   std::map<Prototype, ir::Proto*> proto_map_;
606 
607   // Keep track of fields that have been declared
608   std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_;
609 };
610 
611 template <typename... T>
BuildNew(Value target,TypeDescriptor type,Prototype constructor,const T &...args)612 void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor,
613                              const T&... args) {
614   MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
615   // allocate the object
616   ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
617   AddInstruction(
618       Instruction::OpWithArgs(Instruction::Op::kNew, target, Value::Type(type_def->orig_index)));
619   // call the constructor
620   AddInstruction(Instruction::InvokeDirect(constructor_data.id, /*dest=*/{}, target, args...));
621 };
622 
623 }  // namespace dex
624 }  // namespace startop
625 
626 #endif  // DEX_BUILDER_H_
627