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 offset: Option<u32>,
24 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 #[inline]
72 pub fn s(self) -> SmaliNode {
73 SmaliNode { content: self.0, ..Default::default() }
74 }
75
76 #[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