java_asm/
smali.rs

1use crate::dex::DexFileAccessor;
2use crate::{ConstStr, StrRef};
3
4#[derive(Debug, Clone, Default, Hash, Eq, PartialEq)]
5pub struct SmaliNode {
6    pub tag: Option<ConstStr>,
7    pub content: Vec<SmaliToken>,
8    pub offset_hint: Option<u32>,
9    pub children: Vec<SmaliNode>,
10    pub end_tag: Option<ConstStr>,
11}
12
13#[derive(Debug, Clone, Hash, Eq, PartialEq)]
14pub enum SmaliToken {
15    SourceInfo(StrRef),
16    
17    Raw(ConstStr),
18    Op(ConstStr),
19
20    LineStartOffsetMarker {
21        // the offset of this instruction.
22        // None if this isn't a instruction.
23        offset: Option<u32>,
24        // rendered text for this marker.
25        raw: String,
26    },
27    Offset {
28        relative: i32,
29        absolute: u32,
30    },
31
32    Register(u16),
33    RegisterRange(u16, u16),
34
35    MemberName(StrRef),
36    Descriptor(StrRef),
37    Literal(StrRef),
38
39    Other(StrRef),
40}
41
42#[inline]
43pub fn stb() -> SmaliTokensBuilder {
44    SmaliTokensBuilder::new()
45}
46
47pub struct SmaliTokensBuilder(Vec<SmaliToken>);
48
49impl SmaliTokensBuilder {
50    pub fn new() -> Self {
51        Self(Vec::new())
52    }
53
54    #[inline]
55    pub fn push(mut self, token: SmaliToken) -> Self {
56        self.0.push(token);
57        self
58    }
59
60    #[inline]
61    pub fn append(mut self, tokens: Vec<SmaliToken>) -> Self {
62        self.0.extend(tokens);
63        self
64    }
65
66    pub fn raw(self, raw: ConstStr) -> Self {
67        self.push(SmaliToken::Raw(raw))
68    }
69
70    // build the smali node with no children.
71    #[inline]
72    pub fn s(self) -> SmaliNode {
73        SmaliNode { content: self.0, ..Default::default() }
74    }
75
76    // build the smali node with children.
77    #[inline]
78    pub fn s_with_children(self, children: Vec<SmaliNode>) -> SmaliNode {
79        SmaliNode { content: self.0, children, ..Default::default() }
80    }
81
82    #[inline]
83    pub fn into_smali(self, children: Vec<SmaliNode>, postfix: ConstStr) -> SmaliNode {
84        SmaliNode { content: self.0, children, end_tag: Some(postfix), ..Default::default() }
85    }
86
87    #[inline]
88    pub fn op(self, op: ConstStr) -> Self {
89        self.push(SmaliToken::Op(op))
90    }
91
92    #[inline]
93    pub fn off(self, current: impl Into<u32>, relative: impl Into<i32>) -> Self {
94        let relative = relative.into();
95        let absolute = (current.into() as i32 + relative) as u32;
96        self.push(SmaliToken::Offset { relative, absolute })
97    }
98
99    #[inline]
100    pub fn v(self, reg: impl Into<u16>) -> Self {
101        self.push(SmaliToken::Register(reg.into()))
102    }
103
104    #[inline]
105    pub fn vr(self, start: impl Into<u16>, end: impl Into<u16>) -> Self {
106        self.push(SmaliToken::RegisterRange(start.into(), end.into()))
107    }
108
109    #[inline]
110    pub fn mn(self, name: StrRef) -> Self {
111        self.push(SmaliToken::MemberName(name))
112    }
113    
114    #[inline]
115    pub fn d(self, desc: StrRef) -> Self {
116        self.push(SmaliToken::Descriptor(desc))
117    }
118
119    #[inline]
120    pub fn l(self, lit: StrRef) -> Self {
121        self.push(SmaliToken::Literal(lit))
122    }
123
124    #[inline]
125    pub fn other(self, other: StrRef) -> Self {
126        self.push(SmaliToken::Other(other))
127    }
128}
129
130pub fn tokens_to_raw(tokens: &[SmaliToken]) -> String {
131    tokens.iter().map(|token| token.raw()).collect::<Vec<_>>().join(" ")
132}
133
134impl SmaliToken {
135    pub fn raw(&self) -> String {
136        match self {
137            Self::SourceInfo(source) => format!("# source: {source}"),
138            Self::Raw(tag) => tag.to_string(),
139            Self::Op(op) => op.to_string(),
140            Self::LineStartOffsetMarker { raw, .. } => raw.clone(),
141            Self::Offset { relative, absolute } => {
142                format!("@{absolute}({relative:+})")
143            }
144            Self::Register(reg) => format!("v{reg}"),
145            Self::RegisterRange(start, end) => format!("v{start}..v{end}"),
146            Self::MemberName(name) => name.to_string(),
147            Self::Descriptor(desc) => desc.to_string(),
148            Self::Literal(lit) => lit.to_string(),
149            Self::Other(other) => other.to_string(),
150        }
151    }
152}
153
154
155#[macro_export]
156macro_rules! raw_smali {
157    ($($arg:tt)*) => {
158        crate::smali::SmaliNode { content: vec![
159            crate::smali::SmaliToken::Other(format!($($arg)*).to_ref())
160        ], ..Default::default() }
161    }
162}
163
164pub use raw_smali;
165use crate::impls::ToStringRef;
166
167impl SmaliNode {
168    const fn raw(s: ConstStr) -> SmaliNode {
169        SmaliNode {
170            tag: Some(s),
171            content: vec![],
172            offset_hint: None,
173            children: vec![],
174            end_tag: None,
175        }
176    }
177
178    pub const NULL: SmaliNode = Self::raw("null");
179    pub const TRUE: SmaliNode = Self::raw("true");
180    pub const FALSE: SmaliNode = Self::raw("false");
181
182
183    #[inline]
184    pub fn empty() -> Self {
185        Default::default()
186    }
187
188    #[deprecated]
189    pub fn new(current: String) -> Self {
190        Self { content: vec![SmaliToken::Other(current.to_ref())], ..Default::default() }
191    }
192
193    #[deprecated]
194    pub fn new_with_children(
195        current: String, children: Vec<SmaliNode>,
196    ) -> Self {
197        Self { content: vec![SmaliToken::Other(current.to_ref())], children, ..Default::default() }
198    }
199}
200
201impl SmaliNode {
202    #[inline]
203    pub fn add_child(&mut self, child: SmaliNode) {
204        self.children.push(child);
205    }
206
207    #[inline]
208    pub fn render(&self, indent: usize) -> String {
209        let mut result = String::new();
210        self.render_internal(indent, &mut result);
211        result
212    }
213    
214    #[inline]
215    pub fn render_to_lines(&self) -> Vec<Vec<SmaliToken>> {
216        self.render_to_lines_internal()
217    }
218}
219
220pub trait ToSmali {
221    fn to_smali(&self) -> SmaliNode;
222}
223
224pub trait Dex2Smali {
225    fn to_smali(&self, accessor: &DexFileAccessor) -> SmaliNode;
226}
227
228impl<T: ToString> ToSmali for T {
229    #[inline]
230    fn to_smali(&self) -> SmaliNode {
231        SmaliNode::new(self.to_string())
232    }
233}
234