• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 #include "dex_builder.h"
18 
19 #include "dex/descriptors_names.h"
20 
21 #include <fstream>
22 #include <memory>
23 
24 namespace startop {
25 namespace dex {
26 
27 using std::shared_ptr;
28 using std::string;
29 
30 using ::dex::kAccPublic;
31 using Op = Instruction::Op;
32 
33 using Opcode = ::art::Instruction::Code;
34 
Int()35 const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; };
Void()36 const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; };
37 
38 namespace {
39 // From https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
40 constexpr uint8_t kDexFileMagic[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00};
41 
42 // Strings lengths can be 32 bits long, but encoded as LEB128 this can take up to five bytes.
43 constexpr size_t kMaxEncodedStringLength{5};
44 
45 // Converts invoke-* to invoke-*/range
InvokeToInvokeRange(Opcode opcode)46 constexpr Opcode InvokeToInvokeRange(Opcode opcode) {
47   switch (opcode) {
48     case ::art::Instruction::INVOKE_VIRTUAL:
49       return ::art::Instruction::INVOKE_VIRTUAL_RANGE;
50     case ::art::Instruction::INVOKE_DIRECT:
51       return ::art::Instruction::INVOKE_DIRECT_RANGE;
52     case ::art::Instruction::INVOKE_STATIC:
53       return ::art::Instruction::INVOKE_STATIC_RANGE;
54     case ::art::Instruction::INVOKE_INTERFACE:
55       return ::art::Instruction::INVOKE_INTERFACE_RANGE;
56     default:
57       LOG(FATAL) << opcode << " is not a recognized invoke opcode.";
58       UNREACHABLE();
59   }
60 }
61 
62 }  // namespace
63 
operator <<(std::ostream & out,const Instruction::Op & opcode)64 std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
65   switch (opcode) {
66     case Instruction::Op::kReturn:
67       out << "kReturn";
68       return out;
69     case Instruction::Op::kReturnObject:
70       out << "kReturnObject";
71       return out;
72     case Instruction::Op::kMove:
73       out << "kMove";
74       return out;
75     case Instruction::Op::kMoveObject:
76       out << "kMoveObject";
77       return out;
78     case Instruction::Op::kInvokeVirtual:
79       out << "kInvokeVirtual";
80       return out;
81     case Instruction::Op::kInvokeDirect:
82       out << "kInvokeDirect";
83       return out;
84     case Instruction::Op::kInvokeStatic:
85       out << "kInvokeStatic";
86       return out;
87     case Instruction::Op::kInvokeInterface:
88       out << "kInvokeInterface";
89       return out;
90     case Instruction::Op::kBindLabel:
91       out << "kBindLabel";
92       return out;
93     case Instruction::Op::kBranchEqz:
94       out << "kBranchEqz";
95       return out;
96     case Instruction::Op::kBranchNEqz:
97       out << "kBranchNEqz";
98       return out;
99     case Instruction::Op::kNew:
100       out << "kNew";
101       return out;
102     case Instruction::Op::kCheckCast:
103       out << "kCheckCast";
104       return out;
105   }
106 }
107 
operator <<(std::ostream & out,const Value & value)108 std::ostream& operator<<(std::ostream& out, const Value& value) {
109   if (value.is_register()) {
110     out << "Register(" << value.value() << ")";
111   } else if (value.is_parameter()) {
112     out << "Parameter(" << value.value() << ")";
113   } else if (value.is_immediate()) {
114     out << "Immediate(" << value.value() << ")";
115   } else if (value.is_string()) {
116     out << "String(" << value.value() << ")";
117   } else if (value.is_label()) {
118     out << "Label(" << value.value() << ")";
119   } else if (value.is_type()) {
120     out << "Type(" << value.value() << ")";
121   } else {
122     out << "UnknownValue";
123   }
124   return out;
125 }
126 
Allocate(size_t size)127 void* TrackingAllocator::Allocate(size_t size) {
128   std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
129   void* raw_buffer = buffer.get();
130   allocations_[raw_buffer] = std::move(buffer);
131   return raw_buffer;
132 }
133 
Free(void * ptr)134 void TrackingAllocator::Free(void* ptr) { allocations_.erase(allocations_.find(ptr)); }
135 
136 // Write out a DEX file that is basically:
137 //
138 // package dextest;
139 // public class DexTest {
140 //     public static int foo(String s) { return s.length(); }
141 // }
WriteTestDexFile(const string & filename)142 void WriteTestDexFile(const string& filename) {
143   DexBuilder dex_file;
144 
145   ClassBuilder cbuilder{dex_file.MakeClass("dextest.DexTest")};
146   cbuilder.set_source_file("dextest.java");
147 
148   TypeDescriptor string_type = TypeDescriptor::FromClassname("java.lang.String");
149 
150   MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
151 
152   Value result = method.MakeRegister();
153 
154   MethodDeclData string_length =
155       dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
156 
157   method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
158   method.BuildReturn(result);
159 
160   method.Encode();
161 
162   slicer::MemView image{dex_file.CreateImage()};
163 
164   std::ofstream out_file(filename);
165   out_file.write(image.ptr<const char>(), image.size());
166 }
167 
FromClassname(const std::string & name)168 TypeDescriptor TypeDescriptor::FromClassname(const std::string& name) {
169   return TypeDescriptor{art::DotToDescriptor(name.c_str())};
170 }
171 
DexBuilder()172 DexBuilder::DexBuilder() : dex_file_{std::make_shared<ir::DexFile>()} {
173   dex_file_->magic = slicer::MemView{kDexFileMagic, sizeof(kDexFileMagic)};
174 }
175 
CreateImage()176 slicer::MemView DexBuilder::CreateImage() {
177   ::dex::Writer writer(dex_file_);
178   size_t image_size{0};
179   ::dex::u1* image = writer.CreateImage(&allocator_, &image_size);
180   return slicer::MemView{image, image_size};
181 }
182 
GetOrAddString(const std::string & string)183 ir::String* DexBuilder::GetOrAddString(const std::string& string) {
184   ir::String*& entry = strings_[string];
185 
186   if (entry == nullptr) {
187     // Need to encode the length and then write out the bytes, including 1 byte for null terminator
188     auto buffer = std::make_unique<uint8_t[]>(string.size() + kMaxEncodedStringLength + 1);
189     uint8_t* string_data_start = ::dex::WriteULeb128(buffer.get(), string.size());
190 
191     size_t header_length =
192         reinterpret_cast<uintptr_t>(string_data_start) - reinterpret_cast<uintptr_t>(buffer.get());
193 
194     auto end = std::copy(string.begin(), string.end(), string_data_start);
195     *end = '\0';
196 
197     entry = Alloc<ir::String>();
198     // +1 for null terminator
199     entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
200     ::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex();
201     dex_file_->strings_map[new_index] = entry;
202     entry->orig_index = new_index;
203     string_data_.push_back(std::move(buffer));
204   }
205   return entry;
206 }
207 
MakeClass(const std::string & name)208 ClassBuilder DexBuilder::MakeClass(const std::string& name) {
209   auto* class_def = Alloc<ir::Class>();
210   ir::Type* type_def = GetOrAddType(art::DotToDescriptor(name.c_str()));
211   type_def->class_def = class_def;
212 
213   class_def->type = type_def;
214   class_def->super_class = GetOrAddType(art::DotToDescriptor("java.lang.Object"));
215   class_def->access_flags = kAccPublic;
216   return ClassBuilder{this, name, class_def};
217 }
218 
GetOrAddType(const std::string & descriptor)219 ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
220   if (types_by_descriptor_.find(descriptor) != types_by_descriptor_.end()) {
221     return types_by_descriptor_[descriptor];
222   }
223 
224   ir::Type* type = Alloc<ir::Type>();
225   type->descriptor = GetOrAddString(descriptor);
226   types_by_descriptor_[descriptor] = type;
227   type->orig_index = dex_file_->types_indexes.AllocateIndex();
228   dex_file_->types_map[type->orig_index] = type;
229   return type;
230 }
231 
Encode(DexBuilder * dex) const232 ir::Proto* Prototype::Encode(DexBuilder* dex) const {
233   auto* proto = dex->Alloc<ir::Proto>();
234   proto->shorty = dex->GetOrAddString(Shorty());
235   proto->return_type = dex->GetOrAddType(return_type_.descriptor());
236   if (param_types_.size() > 0) {
237     proto->param_types = dex->Alloc<ir::TypeList>();
238     for (const auto& param_type : param_types_) {
239       proto->param_types->types.push_back(dex->GetOrAddType(param_type.descriptor()));
240     }
241   } else {
242     proto->param_types = nullptr;
243   }
244   return proto;
245 }
246 
Shorty() const247 std::string Prototype::Shorty() const {
248   std::string shorty;
249   shorty.append(return_type_.short_descriptor());
250   for (const auto& type_descriptor : param_types_) {
251     shorty.append(type_descriptor.short_descriptor());
252   }
253   return shorty;
254 }
255 
ArgType(size_t index) const256 const TypeDescriptor& Prototype::ArgType(size_t index) const {
257   CHECK_LT(index, param_types_.size());
258   return param_types_[index];
259 }
260 
ClassBuilder(DexBuilder * parent,const std::string & name,ir::Class * class_def)261 ClassBuilder::ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def)
262     : parent_(parent), type_descriptor_{TypeDescriptor::FromClassname(name)}, class_(class_def) {}
263 
CreateMethod(const std::string & name,Prototype prototype)264 MethodBuilder ClassBuilder::CreateMethod(const std::string& name, Prototype prototype) {
265   ir::MethodDecl* decl = parent_->GetOrDeclareMethod(type_descriptor_, name, prototype).decl;
266 
267   return MethodBuilder{parent_, class_, decl};
268 }
269 
set_source_file(const string & source)270 void ClassBuilder::set_source_file(const string& source) {
271   class_->source_file = parent_->GetOrAddString(source);
272 }
273 
MethodBuilder(DexBuilder * dex,ir::Class * class_def,ir::MethodDecl * decl)274 MethodBuilder::MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl)
275     : dex_{dex}, class_{class_def}, decl_{decl} {}
276 
Encode()277 ir::EncodedMethod* MethodBuilder::Encode() {
278   auto* method = dex_->Alloc<ir::EncodedMethod>();
279   method->decl = decl_;
280 
281   // TODO: make access flags configurable
282   method->access_flags = kAccPublic | ::dex::kAccStatic;
283 
284   auto* code = dex_->Alloc<ir::Code>();
285   CHECK(decl_->prototype != nullptr);
286   size_t const num_args =
287       decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
288   code->registers = num_registers_ + num_args + kMaxScratchRegisters;
289   code->ins_count = num_args;
290   EncodeInstructions();
291   code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
292   size_t const return_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
293   code->outs_count = std::max(return_count, max_args_);
294   method->code = code;
295 
296   class_->direct_methods.push_back(method);
297 
298   return method;
299 }
300 
MakeRegister()301 Value MethodBuilder::MakeRegister() { return Value::Local(num_registers_++); }
302 
MakeLabel()303 Value MethodBuilder::MakeLabel() {
304   labels_.push_back({});
305   return Value::Label(labels_.size() - 1);
306 }
307 
AddInstruction(Instruction instruction)308 void MethodBuilder::AddInstruction(Instruction instruction) {
309   instructions_.push_back(instruction);
310 }
311 
BuildReturn()312 void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
313 
BuildReturn(Value src,bool is_object)314 void MethodBuilder::BuildReturn(Value src, bool is_object) {
315   AddInstruction(Instruction::OpWithArgs(
316       is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src));
317 }
318 
BuildConst4(Value target,int value)319 void MethodBuilder::BuildConst4(Value target, int value) {
320   CHECK_LT(value, 16);
321   AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
322 }
323 
BuildConstString(Value target,const std::string & value)324 void MethodBuilder::BuildConstString(Value target, const std::string& value) {
325   const ir::String* const dex_string = dex_->GetOrAddString(value);
326   AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index)));
327 }
328 
EncodeInstructions()329 void MethodBuilder::EncodeInstructions() {
330   buffer_.clear();
331   for (const auto& instruction : instructions_) {
332     EncodeInstruction(instruction);
333   }
334 }
335 
EncodeInstruction(const Instruction & instruction)336 void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
337   switch (instruction.opcode()) {
338     case Instruction::Op::kReturn:
339       return EncodeReturn(instruction, ::art::Instruction::RETURN);
340     case Instruction::Op::kReturnObject:
341       return EncodeReturn(instruction, ::art::Instruction::RETURN_OBJECT);
342     case Instruction::Op::kMove:
343     case Instruction::Op::kMoveObject:
344       return EncodeMove(instruction);
345     case Instruction::Op::kInvokeVirtual:
346       return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
347     case Instruction::Op::kInvokeDirect:
348       return EncodeInvoke(instruction, art::Instruction::INVOKE_DIRECT);
349     case Instruction::Op::kInvokeStatic:
350       return EncodeInvoke(instruction, art::Instruction::INVOKE_STATIC);
351     case Instruction::Op::kInvokeInterface:
352       return EncodeInvoke(instruction, art::Instruction::INVOKE_INTERFACE);
353     case Instruction::Op::kBindLabel:
354       return BindLabel(instruction.args()[0]);
355     case Instruction::Op::kBranchEqz:
356       return EncodeBranch(art::Instruction::IF_EQZ, instruction);
357     case Instruction::Op::kBranchNEqz:
358       return EncodeBranch(art::Instruction::IF_NEZ, instruction);
359     case Instruction::Op::kNew:
360       return EncodeNew(instruction);
361     case Instruction::Op::kCheckCast:
362       return EncodeCast(instruction);
363   }
364 }
365 
EncodeReturn(const Instruction & instruction,::art::Instruction::Code opcode)366 void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
367   CHECK(!instruction.dest().has_value());
368   if (instruction.args().size() == 0) {
369     Encode10x(art::Instruction::RETURN_VOID);
370   } else {
371     CHECK_EQ(1, instruction.args().size());
372     size_t source = RegisterValue(instruction.args()[0]);
373     Encode11x(opcode, source);
374   }
375 }
376 
EncodeMove(const Instruction & instruction)377 void MethodBuilder::EncodeMove(const Instruction& instruction) {
378   CHECK(Instruction::Op::kMove == instruction.opcode() ||
379         Instruction::Op::kMoveObject == instruction.opcode());
380   CHECK(instruction.dest().has_value());
381   CHECK(instruction.dest()->is_variable());
382   CHECK_EQ(1, instruction.args().size());
383 
384   const Value& source = instruction.args()[0];
385 
386   if (source.is_immediate()) {
387     // TODO: support more registers
388     CHECK_LT(RegisterValue(*instruction.dest()), 16);
389     Encode11n(art::Instruction::CONST_4, RegisterValue(*instruction.dest()), source.value());
390   } else if (source.is_string()) {
391     constexpr size_t kMaxRegisters = 256;
392     CHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
393     CHECK_LT(source.value(), 65536);  // make sure we don't need a jumbo string
394     Encode21c(::art::Instruction::CONST_STRING, RegisterValue(*instruction.dest()), source.value());
395   } else if (source.is_variable()) {
396     // For the moment, we only use this when we need to reshuffle registers for
397     // an invoke instruction, meaning we are too big for the 4-bit version.
398     // We'll err on the side of caution and always generate the 16-bit form of
399     // the instruction.
400     Opcode opcode = instruction.opcode() == Instruction::Op::kMove
401                         ? ::art::Instruction::MOVE_16
402                         : ::art::Instruction::MOVE_OBJECT_16;
403     Encode32x(opcode, RegisterValue(*instruction.dest()), RegisterValue(source));
404   } else {
405     UNIMPLEMENTED(FATAL);
406   }
407 }
408 
EncodeInvoke(const Instruction & instruction,::art::Instruction::Code opcode)409 void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode) {
410   constexpr size_t kMaxArgs = 5;
411 
412   // Currently, we only support up to 5 arguments.
413   CHECK_LE(instruction.args().size(), kMaxArgs);
414 
415   uint8_t arguments[kMaxArgs]{};
416   bool has_long_args = false;
417   for (size_t i = 0; i < instruction.args().size(); ++i) {
418     CHECK(instruction.args()[i].is_variable());
419     arguments[i] = RegisterValue(instruction.args()[i]);
420     if (!IsShortRegister(arguments[i])) {
421       has_long_args = true;
422     }
423   }
424 
425   if (has_long_args) {
426     // Some of the registers don't fit in the four bit short form of the invoke
427     // instruction, so we need to do an invoke/range. To do this, we need to
428     // first move all the arguments into contiguous temporary registers.
429     std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
430 
431     const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
432     CHECK(prototype.has_value());
433 
434     for (size_t i = 0; i < instruction.args().size(); ++i) {
435       Instruction::Op move_op;
436       if (opcode == ::art::Instruction::INVOKE_VIRTUAL ||
437           opcode == ::art::Instruction::INVOKE_DIRECT) {
438         // In this case, there is an implicit `this` argument, which is always an object.
439         if (i == 0) {
440           move_op = Instruction::Op::kMoveObject;
441         } else {
442           move_op = prototype->ArgType(i - 1).is_object() ? Instruction::Op::kMoveObject
443                                                           : Instruction::Op::kMove;
444         }
445       } else {
446         move_op = prototype->ArgType(i).is_object() ? Instruction::Op::kMoveObject
447                                                     : Instruction::Op::kMove;
448       }
449 
450       EncodeMove(Instruction::OpWithArgs(move_op, scratch[i], instruction.args()[i]));
451     }
452 
453     Encode3rc(InvokeToInvokeRange(opcode),
454               instruction.args().size(),
455               instruction.method_id(),
456               RegisterValue(scratch[0]));
457   } else {
458     Encode35c(opcode,
459               instruction.args().size(),
460               instruction.method_id(),
461               arguments[0],
462               arguments[1],
463               arguments[2],
464               arguments[3],
465               arguments[4]);
466   }
467 
468   // If there is a return value, add a move-result instruction
469   if (instruction.dest().has_value()) {
470     Encode11x(instruction.result_is_object() ? art::Instruction::MOVE_RESULT_OBJECT
471                                              : art::Instruction::MOVE_RESULT,
472               RegisterValue(*instruction.dest()));
473   }
474 
475   max_args_ = std::max(max_args_, instruction.args().size());
476 }
477 
478 // Encodes a conditional branch that tests a single argument.
EncodeBranch(art::Instruction::Code op,const Instruction & instruction)479 void MethodBuilder::EncodeBranch(art::Instruction::Code op, const Instruction& instruction) {
480   const auto& args = instruction.args();
481   const auto& test_value = args[0];
482   const auto& branch_target = args[1];
483   CHECK_EQ(2, args.size());
484   CHECK(test_value.is_variable());
485   CHECK(branch_target.is_label());
486 
487   size_t instruction_offset = buffer_.size();
488   size_t field_offset = buffer_.size() + 1;
489   Encode21c(
490       op, RegisterValue(test_value), LabelValue(branch_target, instruction_offset, field_offset));
491 }
492 
EncodeNew(const Instruction & instruction)493 void MethodBuilder::EncodeNew(const Instruction& instruction) {
494   CHECK_EQ(Instruction::Op::kNew, instruction.opcode());
495   CHECK(instruction.dest().has_value());
496   CHECK(instruction.dest()->is_variable());
497   CHECK_EQ(1, instruction.args().size());
498 
499   const Value& type = instruction.args()[0];
500   CHECK_LT(RegisterValue(*instruction.dest()), 256);
501   CHECK(type.is_type());
502   Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
503 }
504 
EncodeCast(const Instruction & instruction)505 void MethodBuilder::EncodeCast(const Instruction& instruction) {
506   CHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
507   CHECK(instruction.dest().has_value());
508   CHECK(instruction.dest()->is_variable());
509   CHECK_EQ(1, instruction.args().size());
510 
511   const Value& type = instruction.args()[0];
512   CHECK_LT(RegisterValue(*instruction.dest()), 256);
513   CHECK(type.is_type());
514   Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
515 }
516 
RegisterValue(const Value & value) const517 size_t MethodBuilder::RegisterValue(const Value& value) const {
518   if (value.is_register()) {
519     return value.value();
520   } else if (value.is_parameter()) {
521     return value.value() + num_registers_ + kMaxScratchRegisters;
522   }
523   CHECK(false && "Must be either a parameter or a register");
524   return 0;
525 }
526 
BindLabel(const Value & label_id)527 void MethodBuilder::BindLabel(const Value& label_id) {
528   CHECK(label_id.is_label());
529 
530   LabelData& label = labels_[label_id.value()];
531   CHECK(!label.bound_address.has_value());
532 
533   label.bound_address = buffer_.size();
534 
535   // patch any forward references to this label.
536   for (const auto& ref : label.references) {
537     buffer_[ref.field_offset] = *label.bound_address - ref.instruction_offset;
538   }
539   // No point keeping these around anymore.
540   label.references.clear();
541 }
542 
LabelValue(const Value & label_id,size_t instruction_offset,size_t field_offset)543 ::dex::u2 MethodBuilder::LabelValue(const Value& label_id, size_t instruction_offset,
544                                     size_t field_offset) {
545   CHECK(label_id.is_label());
546   LabelData& label = labels_[label_id.value()];
547 
548   // Short-circuit if the label is already bound.
549   if (label.bound_address.has_value()) {
550     return *label.bound_address - instruction_offset;
551   }
552 
553   // Otherwise, save a reference to where we need to back-patch later.
554   label.references.push_front(LabelReference{instruction_offset, field_offset});
555   return 0;
556 }
557 
GetOrDeclareMethod(TypeDescriptor type,const std::string & name,Prototype prototype)558 const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
559                                                      Prototype prototype) {
560   MethodDeclData& entry = method_id_map_[{type, name, prototype}];
561 
562   if (entry.decl == nullptr) {
563     // This method has not already been declared, so declare it.
564     ir::MethodDecl* decl = dex_file_->Alloc<ir::MethodDecl>();
565     // The method id is the last added method.
566     size_t id = dex_file_->methods.size() - 1;
567 
568     ir::String* dex_name{GetOrAddString(name)};
569     decl->name = dex_name;
570     decl->parent = GetOrAddType(type.descriptor());
571     decl->prototype = GetOrEncodeProto(prototype);
572 
573     // update the index -> ir node map (see tools/dexter/slicer/dex_ir_builder.cc)
574     auto new_index = dex_file_->methods_indexes.AllocateIndex();
575     auto& ir_node = dex_file_->methods_map[new_index];
576     CHECK(ir_node == nullptr);
577     ir_node = decl;
578     decl->orig_index = decl->index = new_index;
579 
580     entry = {id, decl};
581   }
582 
583   return entry;
584 }
585 
GetPrototypeByMethodId(size_t method_id) const586 std::optional<const Prototype> DexBuilder::GetPrototypeByMethodId(size_t method_id) const {
587   for (const auto& entry : method_id_map_) {
588     if (entry.second.id == method_id) {
589       return entry.first.prototype;
590     }
591   }
592   return {};
593 }
594 
GetOrEncodeProto(Prototype prototype)595 ir::Proto* DexBuilder::GetOrEncodeProto(Prototype prototype) {
596   ir::Proto*& ir_proto = proto_map_[prototype];
597   if (ir_proto == nullptr) {
598     ir_proto = prototype.Encode(this);
599   }
600   return ir_proto;
601 }
602 
603 }  // namespace dex
604 }  // namespace startop
605