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