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