java_asm/impls/dex/r/
smali.rs

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
482// guaranteed have no children
483fn 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
490// guaranteed have no children
491fn 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            // transparent for children, no more level
737            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        // some dex modification magic might shares same debug info with different method
813        // to minimize the package size. We only takes debug info that we need.
814        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