1#![allow(non_snake_case)]
2
3use crate::dex::element::{ClassContentElement, FieldElement, MethodElement};
4use crate::dex::insn::{DexInsn, FillArrayDataPayload, PackedSwitchPayload, SparseSwitchPayload};
5use crate::dex::insn_syntax::*;
6use crate::dex::{ClassAccessFlags, ClassDef, CodeItem, DebugInfoItem, DexFileAccessor, EncodedAnnotation, EncodedAnnotationAttribute, EncodedArray, EncodedValue, FieldAccessFlags, InsnContainer, MethodAccessFlags, MethodHandle, MethodHandleType, NO_INDEX, U4};
7use crate::impls::dex::r::element::DebugInfoMap;
8use crate::impls::ToStringRef;
9use crate::smali::{stb, tokens_to_raw, Dex2Smali, SmaliNode};
10use crate::{raw_smali, AsmResult, ConstContainer, DescriptorRef, StrRef};
11use std::collections::HashMap;
12
13impl InsnContainer {
14 fn to_smali(&self, accessor: &DexFileAccessor, mut debug_info: DebugInfoMap) -> SmaliNode {
15 let mut current_offset = 0usize;
16 let (payloads, insns): (Vec<_>, Vec<_>) = self.insns.iter()
17 .map(|insn| {
18 let insn_width = insn.insn_width();
19 let mapped = (current_offset, insn);
20 current_offset += insn_width;
21 mapped
22 })
23 .partition(|(_, insn)| match insn {
24 DexInsn::PackedSwitchPayload(_) |
25 DexInsn::SparseSwitchPayload(_) |
26 DexInsn::FillArrayDataPayload(_) => true,
27 _ => false,
28 });
29 let payload_map: HashMap<usize, &DexInsn> = HashMap::from_iter(payloads);
30 let payload_map = PayloadMap { payload_map };
31
32 let mut insn_list: Vec<SmaliNode> = Vec::with_capacity(
33 insns.len() + debug_info.records.lines.len() + debug_info.local_vars.lines.len()
34 );
35 insn_list.shrink_to_fit();
36 for (offset, insn) in insns {
37 let line_info = debug_info.records.move_to(offset as u32);
38 let local_var_info = debug_info.local_vars.move_to(offset as u32);
39
40 for (src_line, src_file_name_idx) in line_info {
41 let mut stb = stb().raw(".source-line").other(src_line.to_ref());
42 if let Some(src_file_name_idx) = src_file_name_idx.value() {
43 let src_file_name = accessor.opt_str(src_file_name_idx as usize);
44 stb = stb.l(src_file_name);
45 }
46 insn_list.push(SmaliNode::empty());
47 insn_list.push(stb.s());
48 }
49
50 for var_info in local_var_info {
51 let mut stb = stb().raw(".local").v(var_info.register.value() as u16);
52 if let Some(end_addr) = var_info.end_addr {
53 let relative = end_addr - offset as u32;
54 stb = stb.off(offset as u16, relative as u16);
55 }
56 if let Some(name_idx) = var_info.name_idx.value() {
57 stb = stb.other(accessor.opt_str(name_idx as usize));
58 }
59 if let Some(type_idx) = var_info.type_idx.value() {
60 stb = stb.d(accessor.opt_type(type_idx as usize));
61 }
62 if let Some(sig_idx) = var_info.sig_idx.value() {
63 stb = stb.d(accessor.opt_type(sig_idx as usize));
64 }
65 insn_list.push(stb.s());
66 }
67
68 let mut insn = insn.to_smali(accessor, offset, &payload_map);
69 insn.offset_hint = Some(offset as u32);
70 insn_list.push(insn);
71 };
72
73 SmaliNode {
74 tag: Some(".code"),
75 end_tag: Some(".end code"),
76 children: insn_list,
77 ..Default::default()
78 }
79 }
80}
81
82struct PayloadMap<'a> {
83 payload_map: HashMap<usize, &'a DexInsn>,
84}
85
86impl DexInsn {
87 fn to_smali(
88 &self, accessor: &DexFileAccessor, current_offset: usize,
89 payload_map: &PayloadMap,
90 ) -> SmaliNode {
91 let cur = current_offset as u32;
92 let tb = stb();
93 let insn = self;
94 match insn {
95 DexInsn::Nop(_) => tb.op("nop").s(),
96 DexInsn::Move(F12x { vA, vB, .. }) =>
97 tb.op("move").v(*vA).v(*vB).s(),
98 DexInsn::MoveFrom16(F22x { vA, vB, .. }) =>
99 tb.op("move/from16").v(*vA).v(*vB).s(),
100 DexInsn::Move16(F32x { vA, vB, .. }) =>
101 tb.op("move/16").v(*vA).v(*vB).s(),
102 DexInsn::MoveWide(F12x { vA, vB, .. }) =>
103 tb.op("move-wide").v(*vA).v(*vB).s(),
104 DexInsn::MoveWideFrom16(F22x { vA, vB, .. }) =>
105 tb.op("move-wide/from16").v(*vA).v(*vB).s(),
106 DexInsn::MoveWide16(F32x { vA, vB, .. }) =>
107 tb.op("move-wide/16").v(*vA).v(*vB).s(),
108 DexInsn::MoveObject(F12x { vA, vB, .. }) =>
109 tb.op("move-object").v(*vA).v(*vB).s(),
110 DexInsn::MoveObjectFrom16(F22x { vA, vB, .. }) =>
111 tb.op("move-object/from16").v(*vA).v(*vB).s(),
112 DexInsn::MoveObject16(F32x { vA, vB, .. }) =>
113 tb.op("move-object/16").v(*vA).v(*vB).s(),
114 DexInsn::MoveResult(F11x { vA, .. }) =>
115 tb.op("move-result").v(*vA).s(),
116 DexInsn::MoveResultWide(F11x { vA, .. }) =>
117 tb.op("move-result-wide").v(*vA).s(),
118 DexInsn::MoveResultObject(F11x { vA, .. }) =>
119 tb.op("move-result-object").v(*vA).s(),
120 DexInsn::MoveException(F11x { vA, .. }) =>
121 tb.op("move-exception").v(*vA).s(),
122 DexInsn::ReturnVoid(_) => tb.op("return-void").s(),
123 DexInsn::Return(F11x { vA, .. }) =>
124 tb.op("return").v(*vA).s(),
125 DexInsn::ReturnWide(F11x { vA, .. }) =>
126 tb.op("return-wide").v(*vA).s(),
127 DexInsn::ReturnObject(F11x { vA, .. }) =>
128 tb.op("return-object").v(*vA).s(),
129 DexInsn::Const4(F11n { vA, literalB, .. }) =>
130 tb.op("const/4").v(*vA).l(literalB.0.to_ref()).s(),
131 DexInsn::Const16(F21s { vA, literalB, .. }) =>
132 tb.op("const/16").v(*vA).l(literalB.to_ref()).s(),
133 DexInsn::Const(F31i { vA, literalB, .. }) =>
134 tb.op("const").v(*vA).l(literalB.to_ref()).s(),
135 DexInsn::ConstHigh16(F21h { vA, literalB, .. }) =>
136 tb.op("const/high16").v(*vA).l(literalB.to_ref()).s(),
137 DexInsn::ConstWide16(F21s { vA, literalB, .. }) =>
138 tb.op("const-wide/16").v(*vA).l(literalB.to_ref()).s(),
139 DexInsn::ConstWide32(F31i { vA, literalB, .. }) =>
140 tb.op("const-wide/32").v(*vA).l(literalB.to_ref()).s(),
141 DexInsn::ConstWide(F51l { vA, literalB, .. }) =>
142 tb.op("const-wide").v(*vA).l(literalB.to_ref()).s(),
143 DexInsn::ConstWideHigh16(F21h { vA, literalB, .. }) =>
144 tb.op("const-wide/high16").v(*vA).l(literalB.to_ref()).s(),
145 DexInsn::ConstString(F21c { vA, constB, .. }) =>
146 tb.op("const-string").v(*vA).l(accessor.opt_str(*constB)).s(),
147 DexInsn::ConstStringJumbo(F31c { vA, constB, .. }) =>
148 tb.op("const-string/jumbo").v(*vA).l(accessor.opt_str(*constB)).s(),
149 DexInsn::ConstClass(F21c { vA, constB, .. }) =>
150 tb.op("const-class").v(*vA).d(accessor.opt_type(*constB)).s(),
151 DexInsn::MonitorEnter(F11x { vA, .. }) =>
152 tb.op("monitor-enter").v(*vA).s(),
153 DexInsn::MonitorExit(F11x { vA, .. }) =>
154 tb.op("monitor-exit").v(*vA).s(),
155 DexInsn::CheckCast(F21c { vA, constB, .. }) =>
156 tb.op("check-cast").v(*vA).d(accessor.opt_type(*constB)).s(),
157 DexInsn::InstanceOf(F22c { vA, vB, constC, .. }) =>
158 tb.op("instance-of").v(*vA).v(*vB).d(accessor.opt_type(*constC)).s(),
159 DexInsn::ArrayLength(F12x { vA, vB, .. }) =>
160 tb.op("array-length").v(*vA).v(*vB).s(),
161 DexInsn::NewInstance(F21c { vA, constB, .. }) =>
162 tb.op("new-instance").v(*vA).d(accessor.opt_type(*constB)).s(),
163 DexInsn::NewArray(F22c { vA, vB, constC, .. }) =>
164 tb.op("new-array").v(*vA).v(*vB).d(accessor.opt_type(*constC)).s(),
165 DexInsn::FilledNewArray(F35c { a, vC, vD, vE, vF, vG, constB, .. }) =>
166 render_f35("filled-new-array", *a, tb.l(accessor.opt_str(*constB)).s(),
167 *vC, *vD, *vE, *vF, *vG),
168 DexInsn::FilledNewArrayRange(F3rc { a, vC, constB, .. }) =>
169 render_f3r("filled-new-array/range", *a, tb.l(accessor.opt_str(*constB)).s(), *vC),
170 DexInsn::FillArrayData(F31t { vA, offsetB, .. }) => {
171 let payload = payload_map.read(cur, *offsetB);
172 tb.op("fill-array-data").v(*vA).append(payload.content).s_with_children(payload.children)
173 }
174 DexInsn::Throw(F11x { vA, .. }) =>
175 tb.op("throw").v(*vA).s(),
176 DexInsn::Goto(F10t { offsetA, .. }) =>
177 tb.op("goto").off(cur, *offsetA).s(),
178 DexInsn::Goto16(F20t { offsetA, .. }) =>
179 tb.op("goto/16").off(cur, *offsetA).s(),
180 DexInsn::Goto32(F30t { offsetA, .. }) =>
181 tb.op("goto/32").off(cur, *offsetA).s(),
182 DexInsn::PackedSwitch(F31t { vA, offsetB, .. }) => {
183 let payload = payload_map.read(cur, *offsetB);
184 tb.op("packed-switch").v(*vA)
185 .append(payload.content).s_with_children(payload.children)
186 }
187 DexInsn::SparseSwitch(F31t { vA, offsetB, .. }) => {
188 let payload = payload_map.read(cur, *offsetB);
189 tb.op("sparse-switch").v(*vA)
190 .append(payload.content).s_with_children(payload.children)
191 }
192 DexInsn::Cmpkind(F23x { opcode, vA, vB, vC }) => {
193 let op_name = match opcode {
194 0x2d => "cmpl-float",
195 0x2e => "cmpg-float",
196 0x2f => "cmpl-double",
197 0x30 => "cmpg-double",
198 0x31 => "cmp-long",
199 _ => "cmpkind",
200 };
201 tb.op(op_name).v(*vA).v(*vB).v(*vC).s()
202 }
203 DexInsn::IfTest(F22t { opcode, vA, vB, offsetC }) => {
204 let op_name = match opcode {
205 0x32 => "if-eq",
206 0x33 => "if-ne",
207 0x34 => "if-lt",
208 0x35 => "if-ge",
209 0x36 => "if-gt",
210 0x37 => "if-le",
211 _ => "if-test",
212 };
213 tb.op(op_name).v(*vA).v(*vB).off(cur, *offsetC).s()
214 }
215 DexInsn::IfTestz(F21t { opcode, vA, offsetB }) => {
216 let op_name = match opcode {
217 0x38 => "if-eqz",
218 0x39 => "if-nez",
219 0x3a => "if-ltz",
220 0x3b => "if-gez",
221 0x3c => "if-gtz",
222 0x3d => "if-lez",
223 _ => "if-testz"
224 };
225 tb.op(op_name).v(*vA).off(cur, *offsetB).s()
226 }
227 DexInsn::ArrayOp(F23x { opcode, vA, vB, vC }) => {
228 let op_name = match opcode {
229 0x44 => "aget",
230 0x45 => "aget-wide",
231 0x46 => "aget-object",
232 0x47 => "aget-boolean",
233 0x48 => "aget-byte",
234 0x49 => "aget-char",
235 0x4a => "aget-short",
236 0x4b => "aput",
237 0x4c => "aput-wide",
238 0x4d => "aput-object",
239 0x4e => "aput-boolean",
240 0x4f => "aput-byte",
241 0x50 => "aput-char",
242 0x51 => "aput-short",
243 _ => "arrayop",
244 };
245 tb.op(op_name).v(*vA).v(*vB).v(*vC).s()
246 }
247 DexInsn::IInstanceOp(F22c { opcode, vA, vB, constC }) => {
248 let op_name = match opcode {
249 0x52 => "iget",
250 0x53 => "iget-wide",
251 0x54 => "iget-object",
252 0x55 => "iget-boolean",
253 0x56 => "iget-byte",
254 0x57 => "iget-char",
255 0x58 => "iget-short",
256 0x59 => "iput",
257 0x5a => "iput-wide",
258 0x5b => "iput-object",
259 0x5c => "iput-boolean",
260 0x5d => "iput-byte",
261 0x5e => "iput-char",
262 0x5f => "iput-short",
263 _ => "instanceop",
264 };
265 tb.op(op_name).v(*vA).v(*vB).append(render_field(accessor, *constC).content).s()
266 }
267 DexInsn::SStaticOp(F21c { opcode, vA, constB }) => {
268 let op_name = match opcode {
269 0x60 => "sget",
270 0x61 => "sget-wide",
271 0x62 => "sget-object",
272 0x63 => "sget-boolean",
273 0x64 => "sget-byte",
274 0x65 => "sget-char",
275 0x66 => "sget-short",
276 0x67 => "sput",
277 0x68 => "sput-wide",
278 0x69 => "sput-object",
279 0x6a => "sput-boolean",
280 0x6b => "sput-byte",
281 0x6c => "sput-char",
282 0x6d => "sput-short",
283 _ => "staticop",
284 };
285 tb.op(op_name).v(*vA).append(render_field(accessor, *constB).content).s()
286 }
287 DexInsn::InvokeKind(F35c { opcode, a, vC, vD, vE, vF, vG, constB }) => {
288 let op_name = match opcode {
289 0x6e => "invoke-virtual",
290 0x6f => "invoke-super",
291 0x70 => "invoke-direct",
292 0x71 => "invoke-static",
293 0x72 => "invoke-interface",
294 _ => "invokekind",
295 };
296 let constB = render_method(accessor, *constB);
297 render_f35(op_name, *a, constB, *vC, *vD, *vE, *vF, *vG)
298 }
299 DexInsn::InvokeKindRange(F3rc { opcode, a, constB, vC }) => {
300 let op_name = match opcode {
301 0x74 => "invoke-virtual/range",
302 0x75 => "invoke-super/range",
303 0x76 => "invoke-direct/range",
304 0x77 => "invoke-static/range",
305 0x78 => "invoke-interface/range",
306 _ => "invokekindrange",
307 };
308 let constB = render_method(accessor, *constB);
309 render_f3r(op_name, *a, constB, *vC)
310 }
311 DexInsn::Unop(F12x { opcode, vA, vB }) => {
312 let op_name = match opcode {
313 0x7b => "neg-int",
314 0x7c => "not-int",
315 0x7d => "neg-long",
316 0x7e => "not-long",
317 0x7f => "neg-float",
318 0x80 => "neg-double",
319 0x81 => "int-to-long",
320 0x82 => "int-to-float",
321 0x83 => "int-to-double",
322 0x84 => "long-to-int",
323 0x85 => "long-to-float",
324 0x86 => "long-to-double",
325 0x87 => "float-to-int",
326 0x88 => "float-to-long",
327 0x89 => "float-to-double",
328 0x8a => "double-to-int",
329 0x8b => "double-to-long",
330 0x8c => "double-to-float",
331 0x8d => "int-to-byte",
332 0x8e => "int-to-char",
333 0x8f => "int-to-short",
334 _ => "unop",
335 };
336 tb.op(op_name).v(*vA).v(*vB).s()
337 }
338 DexInsn::Binop(F23x { opcode, vA, vB, vC }) => {
339 let op_name = match opcode {
340 0x90 => "add-int",
341 0x91 => "sub-int",
342 0x92 => "mul-int",
343 0x93 => "div-int",
344 0x94 => "rem-int",
345 0x95 => "and-int",
346 0x96 => "or-int",
347 0x97 => "xor-int",
348 0x98 => "shl-int",
349 0x99 => "shr-int",
350 0x9a => "ushr-int",
351 0x9b => "add-long",
352 0x9c => "sub-long",
353 0x9d => "mul-long",
354 0x9e => "div-long",
355 0x9f => "rem-long",
356 0xa0 => "and-long",
357 0xa1 => "or-long",
358 0xa2 => "xor-long",
359 0xa3 => "shl-long",
360 0xa4 => "shr-long",
361 0xa5 => "ushr-long",
362 0xa6 => "add-float",
363 0xa7 => "sub-float",
364 0xa8 => "mul-float",
365 0xa9 => "div-float",
366 0xaa => "rem-float",
367 0xab => "add-double",
368 0xac => "sub-double",
369 0xad => "mul-double",
370 0xae => "div-double",
371 0xaf => "rem-double",
372 _ => "binop",
373 };
374 tb.op(op_name).v(*vA).v(*vB).v(*vC).s()
375 }
376 DexInsn::Binop2Addr(F12x { opcode, vA, vB }) => {
377 let op_name = match opcode {
378 0xb0 => "add-int/2addr",
379 0xb1 => "sub-int/2addr",
380 0xb2 => "mul-int/2addr",
381 0xb3 => "div-int/2addr",
382 0xb4 => "rem-int/2addr",
383 0xb5 => "and-int/2addr",
384 0xb6 => "or-int/2addr",
385 0xb7 => "xor-int/2addr",
386 0xb8 => "shl-int/2addr",
387 0xb9 => "shr-int/2addr",
388 0xba => "ushr-int/2addr",
389 0xbb => "add-long/2addr",
390 0xbc => "sub-long/2addr",
391 0xbd => "mul-long/2addr",
392 0xbe => "div-long/2addr",
393 0xbf => "rem-long/2addr",
394 0xc0 => "and-long/2addr",
395 0xc1 => "or-long/2addr",
396 0xc2 => "xor-long/2addr",
397 0xc3 => "shl-long/2addr",
398 0xc4 => "shr-long/2addr",
399 0xc5 => "ushr-long/2addr",
400 0xc6 => "add-float/2addr",
401 0xc7 => "sub-float/2addr",
402 0xc8 => "mul-float/2addr",
403 0xc9 => "div-float/2addr",
404 0xca => "rem-float/2addr",
405 0xcb => "add-double/2addr",
406 0xcc => "sub-double/2addr",
407 0xcd => "mul-double/2addr",
408 0xce => "div-double/2addr",
409 0xcf => "rem-double/2addr",
410 _ => "binop2addr",
411 };
412 tb.op(op_name).v(*vA).v(*vB).s()
413 }
414 DexInsn::BinopLit16(F22s { opcode, vA, vB, literalC }) => {
415 let op_name = match opcode {
416 0xd0 => "add-int/lit16",
417 0xd1 => "rsub-int",
418 0xd2 => "mul-int/lit16",
419 0xd3 => "div-int/lit16",
420 0xd4 => "rem-int/lit16",
421 0xd5 => "and-int/lit16",
422 0xd6 => "or-int/lit16",
423 0xd7 => "xor-int/lit16",
424 _ => "binoplit16",
425 };
426 tb.op(op_name).v(*vA).v(*vB).l(literalC.to_ref()).s()
427 },
428 DexInsn::BinopLit8(F22b { opcode, vA, vB, literalC }) => {
429 let op_name = match opcode {
430 0xd8 => "add-int/lit8",
431 0xd9 => "rsub-int/lit8",
432 0xda => "mul-int/lit8",
433 0xdb => "div-int/lit8",
434 0xdc => "rem-int/lit8",
435 0xdd => "and-int/lit8",
436 0xde => "or-int/lit8",
437 0xdf => "xor-int/lit8",
438 0xe0 => "shl-int/lit8",
439 0xe1 => "shr-int/lit8",
440 0xe2 => "ushr-int/lit8",
441 _ => "binoplit8",
442 };
443 tb.op(op_name).v(*vA).v(*vB).l(literalC.to_ref()).s()
444 }
445 DexInsn::InvokePoly(f45cc) =>
446 render_invoke_poly(accessor, *f45cc),
447 DexInsn::InvokePolyRange(f4rcc) =>
448 render_invoke_poly_range(accessor, *f4rcc),
449 DexInsn::InvokeCustom(F35c { a, vC, vD, vE, vF, vG, constB, .. }) =>
450 render_f35_smali("invoke-custom", *a, render_call_site(accessor, *constB), *vC, *vD, *vE, *vF, *vG),
451 DexInsn::InvokeCustomRange(F3rc { a, constB, vC, .. }) =>
452 render_f3r_smali("invoke-custom-range", *a, render_call_site(accessor, *constB), *vC),
453 DexInsn::ConstMethodHandle(F21c { vA, constB, .. }) =>
454 tb.op("const-method-handle").v(*vA).other(render_method_handle_str(accessor, *constB)).s(),
455 DexInsn::ConstMethodType(F21c { vA, constB, .. }) =>
456 tb.op("const-method-type").v(*vA).d(render_proto(accessor, *constB)).s(),
457 DexInsn::NotUsed(_) => SmaliNode::empty(),
458 DexInsn::PackedSwitchPayload(p) => p.to_smali(cur),
459 DexInsn::SparseSwitchPayload(p) => p.to_smali(cur),
460 DexInsn::FillArrayDataPayload(p) => p.to_smali(),
461 }
462 }
463}
464
465impl PayloadMap<'_> {
466 pub fn read(&self, current: u32, offset: i32) -> SmaliNode {
467 let payload_offset = (current as i32 + offset) as usize;
468 let tb = stb();
469 let payload = match self.payload_map.get(&payload_offset) {
470 Some(p) => p,
471 None => return tb.raw("payload").off(current, offset).s(),
472 };
473 match payload {
474 DexInsn::PackedSwitchPayload(p) => p.to_smali(current),
475 DexInsn::SparseSwitchPayload(p) => p.to_smali(current),
476 DexInsn::FillArrayDataPayload(p) => p.to_smali(),
477 _ => tb.raw("payload").off(current, offset).s(),
478 }
479 }
480}
481
482fn render_field(accessor: &DexFileAccessor, field_idx: u16) -> SmaliNode {
484 accessor.get_field(field_idx)
485 .map(|f| stb()
486 .d(f.class_type).other(f.field_name).d(f.field_type).s()
487 ).unwrap_or_else(|_| raw_smali!("field@{}", field_idx))
488}
489
490fn render_method(accessor: &DexFileAccessor, method_idx: u16) -> SmaliNode {
492 accessor.get_method(method_idx)
493 .map(|m| stb()
494 .d(m.class_type).other(m.method_name).d(m.desc).s()
495 ).unwrap_or_else(|_| raw_smali!("method@{}", method_idx))
496}
497
498fn render_proto(accessor: &DexFileAccessor, proto_idx: u16) -> DescriptorRef {
499 accessor.get_proto(proto_idx)
500 .map(|p| p.to_string())
501 .unwrap_or_else(|_| format!("proto@{}", proto_idx))
502 .to_ref()
503}
504
505fn render_call_site(accessor: &DexFileAccessor, call_site_idx: u16) -> SmaliNode {
506 accessor.get_call_site(call_site_idx)
507 .map(|cs| cs.to_smali(accessor))
508 .unwrap_or_else(|_| raw_smali!("call_site@{}", call_site_idx))
509}
510
511fn render_method_handle(accessor: &DexFileAccessor, method_handle_idx: u16) -> SmaliNode {
512 accessor.get_method_handle(method_handle_idx)
513 .map(|mh| mh.to_smali(accessor))
514 .unwrap_or_else(|_| raw_smali!("method_handle@{}", method_handle_idx))
515}
516
517fn render_method_handle_str(accessor: &DexFileAccessor, method_handle_idx: u16) -> StrRef {
518 let tokens = render_method_handle(accessor, method_handle_idx).content;
519 tokens_to_raw(&tokens).to_ref()
520}
521
522fn render_invoke_poly(accessor: &DexFileAccessor, f45cc: F45cc) -> SmaliNode {
523 let F45cc {
524 a, constB, constH,
525 vC, vD, vE, vF, vG, ..
526 } = f45cc;
527 let a = a.0;
528 let mut tb = stb();
529 tb = tb.op("invoke-polymorphic");
530 let method = render_method(accessor, constB);
531 let proto = render_proto(accessor, constH);
532 if a > 0 { tb = tb.v(vC) };
533 if a > 1 { tb = tb.v(vD) };
534 if a > 2 { tb = tb.v(vE) };
535 if a > 3 { tb = tb.v(vF) };
536 if a > 4 { tb = tb.v(vG) };
537 tb.append(method.content).d(proto).s()
538}
539
540fn render_invoke_poly_range(accessor: &DexFileAccessor, f4rcc: F4rcc) -> SmaliNode {
541 let F4rcc {
542 a, constB, constH, vC, ..
543 } = f4rcc;
544 let method = render_method(accessor, constB);
545 let proto = render_proto(accessor, constH);
546 let n = vC + (a as u16) - 1;
547 stb().op("invoke-polymorphic/range")
548 .vr(vC, n).append(method.content).d(proto).s()
549}
550
551fn render_f35_smali(
552 op_name: &'static str, a: U4, constB: SmaliNode,
553 vC: U4, vD: U4, vE: U4, vF: U4, vG: U4,
554) -> SmaliNode {
555 let a = a.0;
556 let mut tb = stb();
557 tb = tb.op(op_name);
558 if a > 0 { tb = tb.v(vC) };
559 if a > 1 { tb = tb.v(vD) };
560 if a > 2 { tb = tb.v(vE) };
561 if a > 3 { tb = tb.v(vF) };
562 if a > 4 { tb = tb.v(vG) };
563 tb.append(constB.content).s_with_children(constB.children)
564}
565
566fn render_f35(
567 op_name: &'static str, a: U4, constB: SmaliNode,
568 vC: U4, vD: U4, vE: U4, vF: U4, vG: U4,
569) -> SmaliNode {
570 render_f35_smali(op_name, a, constB, vC, vD, vE, vF, vG)
571}
572
573fn render_f3r_smali(
574 op_name: &'static str, a: u8, constB: SmaliNode, vC: u16,
575) -> SmaliNode {
576 let vN = vC + (a as u16) - 1;
577 stb()
578 .op(op_name).vr(vC, vN).append(constB.content)
579 .s_with_children(constB.children)
580}
581
582fn render_f3r(
583 op_name: &'static str, a: u8, constB: SmaliNode, vC: u16,
584) -> SmaliNode {
585 render_f3r_smali(op_name, a, constB, vC)
586}
587
588impl Dex2Smali for MethodHandle {
589 fn to_smali(&self, dex_file_accessor: &DexFileAccessor) -> SmaliNode {
590 let handle_type = MethodHandleType::const_name_or_default(self.method_handle_type, "method_h");
591 let member_id = self.field_or_method_id;
592 let member = match self.method_handle_type {
593 0x00..=0x03 => render_field(dex_file_accessor, member_id),
594 0x04..=0x08 => render_method(dex_file_accessor, member_id),
595 _ => return raw_smali!("{handle_type}@{member_id}"),
596 };
597 stb().other(handle_type.to_ref()).append(member.content).s()
598 }
599}
600
601impl Dex2Smali for EncodedArray {
602 fn to_smali(&self, dex_file_accessor: &DexFileAccessor) -> SmaliNode {
603 let mut values = Vec::with_capacity(self.values.len());
604 for value in self.values.iter() {
605 values.push(value.to_smali(dex_file_accessor));
606 }
607 SmaliNode {
608 children: values,
609 tag: Some(".array"),
610 end_tag: Some(".end array"),
611 ..Default::default()
612 }
613 }
614}
615
616impl Dex2Smali for EncodedValue {
617 fn to_smali(&self, dex_file_accessor: &DexFileAccessor) -> SmaliNode {
618 let tb = stb();
619 match self {
620 EncodedValue::Byte(v) => tb.l(v.to_ref()).s(),
621 EncodedValue::Short(v) => tb.l(v.to_ref()).s(),
622 EncodedValue::Char(v) => tb.l(v.to_ref()).s(),
623 EncodedValue::Int(v) => tb.l(v.to_ref()).s(),
624 EncodedValue::Long(v) => tb.l(v.to_ref()).s(),
625 EncodedValue::Float(v) => tb.l(f32::from_be_bytes(*v).to_ref()).s(),
626 EncodedValue::Double(v) => tb.l(f64::from_be_bytes(*v).to_ref()).s(),
627 EncodedValue::MethodType(v) => tb.l(render_proto(dex_file_accessor, v.0 as u16)).s(),
628 EncodedValue::MethodHandle(v) => render_method_handle(dex_file_accessor, v.0 as u16),
629 EncodedValue::String(v) => tb.l(dex_file_accessor.opt_str(*v)).s(),
630 EncodedValue::Type(v) => tb.d(dex_file_accessor.opt_type(*v)).s(),
631 EncodedValue::Field(v) => render_field(dex_file_accessor, v.0 as u16),
632 EncodedValue::Method(v) => render_method(dex_file_accessor, v.0 as u16),
633 EncodedValue::Enum(v) => render_field(dex_file_accessor, v.0 as u16),
634 EncodedValue::Array(v) => v.to_smali(dex_file_accessor),
635 EncodedValue::Annotation(v) => v.to_smali(dex_file_accessor),
636 EncodedValue::Null => SmaliNode::NULL,
637 EncodedValue::Boolean(v) => if *v { SmaliNode::TRUE } else { SmaliNode::FALSE }
638 }
639 }
640}
641
642impl Dex2Smali for EncodedAnnotation {
643 fn to_smali(&self, dex_file_accessor: &DexFileAccessor) -> SmaliNode {
644 let annotation_type = dex_file_accessor.opt_type(self.type_idx);
645 let res: Vec<_> = self.elements.iter().map(|e| e.to_smali(dex_file_accessor)).collect();
646 if res.is_empty() {
647 stb().raw("annotation").d(annotation_type).s()
648 } else {
649 stb().raw(".annotation").d(annotation_type)
650 .into_smali(res, ".end annotation")
651 }
652 }
653}
654
655impl Dex2Smali for EncodedAnnotationAttribute {
656 fn to_smali(&self, dex_file_accessor: &DexFileAccessor) -> SmaliNode {
657 let name = dex_file_accessor.opt_str(self.name_idx);
658 let value = self.value.to_smali(dex_file_accessor);
659 stb().other(name).append(value.content)
660 .s_with_children(value.children)
661 }
662}
663
664impl PackedSwitchPayload {
665 fn to_smali(&self, current_offset: u32) -> SmaliNode {
666 let mut children = Vec::with_capacity(self.size as usize);
667 let size = self.size as u32;
668 let first_key = self.first_key;
669 for i in 0..size {
670 let target_offset = self.targets[i as usize];
671 let key = first_key + i as i32;
672 let child = stb().l(key.to_ref()).raw("->").off(current_offset, target_offset);
673 children.push(child.s())
674 }
675 SmaliNode {
676 children,
677 tag: Some(".packed-switch"),
678 end_tag: Some(".end packed-switch"),
679 ..Default::default()
680 }
681 }
682}
683
684impl SparseSwitchPayload {
685 fn to_smali(&self, current_offset: u32) -> SmaliNode {
686 let mut children = Vec::with_capacity(self.size as usize);
687 for i in 0..self.size {
688 let key = self.keys[i as usize];
689 let target_offset = self.targets[i as usize];
690 let child = stb().l(key.to_ref()).raw("->").off(current_offset, target_offset);
691 children.push(child.s())
692 }
693 SmaliNode {
694 children,
695 tag: Some(".sparse-switch"),
696 end_tag: Some(".end sparse-switch"),
697 ..Default::default()
698 }
699 }
700}
701
702impl FillArrayDataPayload {
703 fn to_smali(&self) -> SmaliNode {
704 let size = self.size.0;
705 let data = self.data.iter().map(|b| format!("{b:02x}"))
706 .collect::<Vec<_>>().join(" ");
707 stb().raw("array-data").other(format!("size={size}").to_ref()).other(data.to_ref()).s()
708 }
709}
710
711impl ClassDef {
712 pub fn to_smali(&self, accessor: &DexFileAccessor) -> AsmResult<SmaliNode> {
713 let mut tb = stb();
714 let access_flags = self.access_flags;
715 tb = ClassAccessFlags::render(access_flags, tb);
716 let class_type = accessor.opt_type(self.class_idx);
717 let mut smali = tb.d(class_type).s();
718
719 if self.source_file_idx.0 != NO_INDEX {
720 let source_file = accessor.opt_str(self.source_file_idx);
721 smali.add_child(stb().raw(".source").other(source_file).s());
722 };
723
724 if self.superclass_idx.0 != NO_INDEX {
725 let super_type = accessor.opt_type(self.superclass_idx);
726 smali.add_child(stb().raw(".super").d(super_type).s());
727 };
728
729 let interfaces = accessor.get_type_list(self.interfaces_off)?;
730 for interface in interfaces {
731 smali.add_child(stb().raw(".implements").d(interface).s());
732 }
733
734 if self.class_data_off != 0 {
735 let class_element = accessor.get_class_element(self.class_data_off)?;
736 smali.children.extend(class_element.to_smali(accessor)?.children);
738 };
739 Ok(smali)
740 }
741}
742
743impl ClassContentElement {
744 pub fn to_smali(&self, accessor: &DexFileAccessor) -> AsmResult<SmaliNode> {
745 let mut smali = SmaliNode::empty();
746 for field in self.static_fields.iter() {
747 smali.add_child(field.to_smali());
748 }
749 for field in self.instance_fields.iter() {
750 smali.add_child(field.to_smali());
751 }
752 for method in self.direct_methods.iter() {
753 smali.add_child(method.to_smali(accessor)?);
754 }
755 for method in self.virtual_methods.iter() {
756 smali.add_child(method.to_smali(accessor)?);
757 }
758 Ok(smali)
759 }
760}
761
762impl FieldElement {
763 pub fn to_smali(&self) -> SmaliNode {
764 let mut tb = stb();
765 let access_flags = self.access_flags;
766 tb = FieldAccessFlags::render(access_flags, tb);
767 let name = self.name.clone();
768 let descriptor = self.descriptor.clone();
769 let smali = tb.other(name).d(descriptor).s();
770 smali
771 }
772}
773
774impl MethodElement {
775 pub fn to_smali(&self, accessor: &DexFileAccessor) -> AsmResult<SmaliNode> {
776 let mut tb = stb();
777 let access_flags = self.access_flags;
778 tb = MethodAccessFlags::render(access_flags, tb);
779 let name = self.name.clone();
780 let descriptor = format!("({}){}", self.parameters.join(""), self.return_type);
781 let mut smali = tb.other(name).d(descriptor.to_ref()).s();
782 let code = accessor.get_code_item(self.code_off)?;
783 if let Some(code) = code {
784 smali.children.extend(code.to_smali(accessor).children);
785 }
786 Ok(smali)
787 }
788}
789
790impl CodeItem {
791 pub fn to_smali(&self, accessor: &DexFileAccessor) -> SmaliNode {
792 let mut smali = SmaliNode::empty();
793 let debug_info_item: Option<DebugInfoItem> = accessor.get_data_impl(self.debug_info_off).ok();
794
795 let registers_size = self.registers_size;
796 smali.add_child(stb().raw(".registers").l(registers_size.to_ref()).s());
797
798 self.add_parameters(accessor, &mut smali, &debug_info_item);
799
800 let debug_info = DebugInfoMap::from_raw(debug_info_item);
801 let insn_container_smali = self.insn_container.to_smali(accessor, debug_info);
802 smali.children.extend(insn_container_smali.children);
803
804 smali
805 }
806
807 fn add_parameters(
808 &self, accessor: &DexFileAccessor, smali_node: &mut SmaliNode, debug_info: &Option<DebugInfoItem>,
809 ) {
810 let Some(debug_info) = debug_info else { return; };
811 let parameter_names = &debug_info.parameter_names;
812 let parameter_count = self.ins_size as usize;
815 for (i, name) in parameter_names.iter().enumerate().take(parameter_count) {
816 let Some(name_idx) = name.value() else { continue };
817 let name = accessor.opt_str(name_idx as usize);
818 smali_node.add_child(stb().raw(".parameter").v(i as u16).l(name).s());
819 }
820 }
821}
822
823
824
825