Skip to content

Commit 95d1e1d

Browse files
committed
render as lines in server side
1 parent 9e81047 commit 95d1e1d

File tree

10 files changed

+169
-156
lines changed

10 files changed

+169
-156
lines changed

asm/src/impls/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub(crate) use util::*;
22

33
pub(crate) mod jvms;
44
pub(crate) mod node;
5+
pub(crate) mod smali;
56
pub(crate) mod dex;
67

78
mod util;

asm/src/impls/smali/mod.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use crate::impls::ToStringRef;
2+
use crate::smali::{tokens_to_raw, SmaliNode, SmaliToken};
3+
4+
impl SmaliNode {
5+
pub(crate) fn render_internal(&self, ident_level: usize, result: &mut String) {
6+
let indent_str = " ".repeat(ident_level);
7+
result.push_str(&indent_str);
8+
if let Some(offset_hint) = self.offset_hint {
9+
result.push_str(&offset_hint.to_string());
10+
result.push_str(": ");
11+
}
12+
let tag = self.tag;
13+
if let Some(tag) = tag {
14+
result.push_str(&tag.to_string());
15+
result.push(' ');
16+
}
17+
let content = &self.content;
18+
if !content.is_empty() {
19+
result.push_str(&tokens_to_raw(content));
20+
result.push(' ')
21+
}
22+
23+
if self.children.is_empty() && self.end_tag.is_none() {
24+
return;
25+
}
26+
for child in &self.children {
27+
result.push('\n');
28+
child.render_internal(ident_level + 1, result);
29+
}
30+
if let Some(postfix) = &self.end_tag {
31+
result.push('\n');
32+
result.push_str(&indent_str);
33+
result.push_str(&postfix);
34+
}
35+
}
36+
37+
// render the smali node to multiple lines.
38+
pub(crate) fn render_to_lines_internal(&self) -> Vec<Vec<SmaliToken>> {
39+
let max_offset_len = max_offset_hint(self).to_string().len();
40+
render_to_lines(self, 0, max_offset_len)
41+
}
42+
}
43+
44+
fn render_to_lines(
45+
node: &SmaliNode, ident_width: usize, max_offset_len: usize,
46+
) -> Vec<Vec<SmaliToken>> {
47+
let mut lines = Vec::new();
48+
49+
let mut current_line = Vec::new();
50+
let SmaliNode { tag, content, children, end_tag, .. } = node;
51+
current_line.push(offset_or_stub(max_offset_len, node));
52+
current_line.push(indent(ident_width));
53+
if let Some(tag) = tag {
54+
current_line.push(SmaliToken::Raw(tag));
55+
current_line.push(SmaliToken::Raw(" "));
56+
}
57+
for token in content {
58+
current_line.push(token.clone());
59+
current_line.push(SmaliToken::Raw(" "));
60+
}
61+
lines.push(current_line);
62+
63+
for child in children {
64+
let child_lines = render_to_lines(child, ident_width + 2, max_offset_len);
65+
lines.extend(child_lines);
66+
}
67+
if children.len() > 0 {
68+
lines.push(vec![])
69+
}
70+
if let Some(postfix) = end_tag {
71+
lines.push(vec![
72+
indent(ident_width),
73+
SmaliToken::Raw(postfix),
74+
]);
75+
}
76+
lines
77+
}
78+
79+
fn max_offset_hint(smali_node: &SmaliNode) -> u32 {
80+
let mut max = 0;
81+
for child in &smali_node.children {
82+
max = max.max(max_offset_hint(child));
83+
}
84+
if let Some(offset_hint) = smali_node.offset_hint {
85+
max = max.max(offset_hint);
86+
}
87+
max
88+
}
89+
90+
fn offset_or_stub(
91+
max_offset_len: usize, smali_node: &SmaliNode,
92+
) -> SmaliToken {
93+
let raw = if let Some(offset_hint) = smali_node.offset_hint {
94+
format!("{:width$}", offset_hint, width = max_offset_len)
95+
} else {
96+
" ".repeat(max_offset_len)
97+
};
98+
SmaliToken::Other(raw.to_ref())
99+
}
100+
101+
fn indent(indent_width: usize) -> SmaliToken {
102+
SmaliToken::Other(" ".repeat(indent_width).to_ref())
103+
}

asm/src/smali.rs

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,17 @@ impl SmaliTokensBuilder {
5151
self
5252
}
5353

54-
#[inline]
55-
pub fn build(self) -> Vec<SmaliToken> {
56-
self.0
57-
}
58-
5954
pub fn raw(self, raw: ConstStr) -> Self {
6055
self.push(SmaliToken::Raw(raw))
6156
}
6257

58+
// build the smali node with no children.
6359
#[inline]
6460
pub fn s(self) -> SmaliNode {
6561
SmaliNode { content: self.0, ..Default::default() }
6662
}
6763

64+
// build the smali node with children.
6865
#[inline]
6966
pub fn s_with_children(self, children: Vec<SmaliNode>) -> SmaliNode {
7067
SmaliNode { content: self.0, children, ..Default::default() }
@@ -194,36 +191,9 @@ impl SmaliNode {
194191
result
195192
}
196193

197-
fn render_internal(&self, ident_level: usize, result: &mut String) {
198-
let indent_str = " ".repeat(ident_level);
199-
result.push_str(&indent_str);
200-
if let Some(offset_hint) = self.offset_hint {
201-
result.push_str(&offset_hint.to_string());
202-
result.push_str(": ");
203-
}
204-
let tag = self.tag;
205-
if let Some(tag) = tag {
206-
result.push_str(&tag.to_string());
207-
result.push(' ');
208-
}
209-
let content = &self.content;
210-
if !content.is_empty() {
211-
result.push_str(&tokens_to_raw(content));
212-
result.push(' ')
213-
}
214-
215-
if self.children.is_empty() && self.end_tag.is_none() {
216-
return;
217-
}
218-
for child in &self.children {
219-
result.push('\n');
220-
child.render_internal(ident_level + 1, result);
221-
}
222-
if let Some(postfix) = &self.end_tag {
223-
result.push('\n');
224-
result.push_str(&indent_str);
225-
result.push_str(&postfix);
226-
}
194+
#[inline]
195+
pub fn render_to_lines(&self) -> Vec<Vec<SmaliToken>> {
196+
self.render_to_lines_internal()
227197
}
228198
}
229199

asm_egui/src/file_tree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn render_dir(ui: &mut egui::Ui, app: &mut EguiApp) {
1515
if let Some(server) = server.deref() {
1616
let content = &mut server_app_content;
1717
let row_height = ui.spacing().interact_size.y;
18-
ScrollArea::vertical()
18+
ScrollArea::vertical().auto_shrink(false)
1919
.show_rows(ui, row_height, entries.len(), |ui, range| {
2020
for i in range {
2121
let entry = &mut entries[i];

asm_egui/src/smali.rs

Lines changed: 35 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,64 @@
1+
use eframe::epaint::Color32;
12
use egui::text::LayoutJob;
2-
use egui::util::cache::{ComputerMut, FrameCache};
3-
use egui::{Color32, FontId, TextFormat, TextStyle, Ui};
3+
use egui::{FontId, Response, ScrollArea, TextStyle, Ui};
44
use java_asm::smali::{SmaliNode, SmaliToken};
55

6-
#[derive(Default)]
7-
struct SmaliHighlighter;
8-
9-
// font, dft_color, dark_mode, smali_node
10-
impl ComputerMut<(&FontId, Color32, bool, &SmaliNode), LayoutJob> for SmaliHighlighter {
11-
fn compute(&mut self, key: (&FontId, Color32, bool, &SmaliNode)) -> LayoutJob {
12-
let (font, dft_color, dark_mode, smali_node) = key;
13-
let mut job = LayoutJob::default();
14-
let smali_style = if dark_mode { SmaliStyle::DARK } else { SmaliStyle::LIGHT };
15-
let max_offset_len = max_offset_hint(smali_node).to_string().len();
16-
append_node(&font, dft_color, &smali_style, smali_node, &mut job, 0, max_offset_len);
17-
job
18-
}
19-
}
20-
216
pub fn smali_layout(ui: &mut Ui, smali_node: &SmaliNode) {
227
let ctx = &mut ui.ctx();
238

24-
type HighlightCache = FrameCache<LayoutJob, SmaliHighlighter>;
25-
269
let style = ui.ctx().style();
2710
let font = TextStyle::Monospace.resolve(&style);
2811
let dft_color = style.visuals.text_color();
2912
let dark_mode = style.visuals.dark_mode;
30-
let job = ctx.memory_mut(|mem| {
31-
mem.caches.cache::<HighlightCache>()
32-
.get( (&font, dft_color, dark_mode, smali_node))
33-
});
34-
ui.label(job);
35-
}
13+
let smali_style = if dark_mode { SmaliStyle::DARK } else { SmaliStyle::LIGHT };
3614

37-
fn append_node(
38-
font: &FontId, dft_color: Color32, smali_style: &SmaliStyle, node: &SmaliNode,
39-
job: &mut LayoutJob, indent: usize, max_offset_len: usize,
40-
) {
41-
let SmaliNode { tag, content, children, end_tag, .. } = node;
42-
append_offset_or_stub(max_offset_len, node, job, font, smali_style);
43-
append_indent(job, font, smali_style, indent);
44-
if let Some(tag) = tag {
45-
append(job, tag, font, dft_color);
46-
append_space(job, font, smali_style);
47-
}
48-
for token in content {
49-
append_token(font, dft_color, smali_style, token, job);
50-
append_space(job, font, smali_style);
51-
}
52-
for child in children {
53-
append(job, "\n", font, dft_color);
54-
append_node(font, dft_color, smali_style, child, job, indent + 1, max_offset_len);
55-
}
56-
if children.len() > 0 {
57-
append(job, "\n", font, dft_color);
58-
}
59-
if let Some(end_tag) = end_tag {
60-
append_indent(job, font, smali_style, indent);
61-
append(job, end_tag, font, dft_color);
62-
append(job, "\n", font, dft_color);
63-
}
64-
}
65-
66-
#[inline]
67-
fn max_offset_hint(smali_node: &SmaliNode) -> u32 {
68-
let mut max = 0;
69-
for child in &smali_node.children {
70-
max = max.max(max_offset_hint(child));
71-
}
72-
if let Some(offset_hint) = smali_node.offset_hint {
73-
max = max.max(offset_hint);
74-
}
75-
max
15+
let lines = smali_node.render_to_lines();
16+
let row_height = font.size + ui.spacing().item_spacing.y;
17+
ScrollArea::vertical().auto_shrink(false).show_rows(ui, row_height, lines.len(), |ui, range| {
18+
for i in range {
19+
let line = &lines[i];
20+
render_line(ui, &font, &smali_style, dft_color, line);
21+
}
22+
});
7623
}
7724

78-
fn append_offset_or_stub(
79-
max_offset_len: usize, smali_node: &SmaliNode, job: &mut LayoutJob,
80-
font: &FontId, smali_style: &SmaliStyle,
25+
fn render_line(
26+
ui: &mut Ui, font: &FontId, smali_style: &SmaliStyle, dft_color: Color32, line: &[SmaliToken],
8127
) {
82-
if let Some(offset_hint) = smali_node.offset_hint {
83-
let offset_str = format!("{:width$}", offset_hint, width = max_offset_len);
84-
append(job, &offset_str, font, smali_style.offset);
85-
} else {
86-
append(job, &" ".repeat(max_offset_len), font, smali_style.offset);
87-
}
28+
ui.horizontal(|ui| {
29+
ui.spacing_mut().item_spacing.x = 0.0;
30+
for token_item in line {
31+
token(ui, font, smali_style, dft_color, token_item);
32+
}
33+
});
8834
}
8935

90-
fn append_token(
91-
font: &FontId, dft_color: Color32, smali_style: &SmaliStyle, token: &SmaliToken,
92-
job: &mut LayoutJob,
93-
) {
36+
fn token(
37+
ui: &mut Ui, font: &FontId, smali_style: &SmaliStyle,
38+
dft_color: Color32, token: &SmaliToken,
39+
) -> Response {
9440
match token {
95-
SmaliToken::Raw(s) => append(job, s, font, dft_color),
96-
SmaliToken::Op(s) => append(job, s, font, smali_style.op),
41+
SmaliToken::Raw(s) => simple_text(ui, s.to_string(), font, dft_color),
42+
SmaliToken::Op(s) => simple_text(ui, s.to_string(), font, smali_style.op),
9743
SmaliToken::Offset { relative, absolute } => {
9844
let text = format!("@{absolute}({relative:+})");
99-
append(job, &text, font, smali_style.offset);
45+
simple_text(ui, text, font, smali_style.offset)
10046
}
101-
SmaliToken::Register(s) => append(job, &format!("v{s}"), font, smali_style.register),
47+
SmaliToken::Register(s) => simple_text(ui, format!("v{s}"), font, smali_style.register),
10248
SmaliToken::RegisterRange(start, end) => {
10349
let text = format!("v{start}..v{end}");
104-
append(job, &text, font, smali_style.register);
50+
simple_text(ui, text, font, smali_style.register)
10551
}
106-
SmaliToken::Descriptor(s) => append(job, s, font, smali_style.desc),
107-
SmaliToken::Literal(s) => append(job, s, font, smali_style.literal),
108-
SmaliToken::Other(s) => append(job, s, font, dft_color),
52+
SmaliToken::Descriptor(s) => simple_text(ui, s.to_string(), font, smali_style.desc),
53+
SmaliToken::Literal(s) => simple_text(ui, s.to_string(), font, smali_style.literal),
54+
SmaliToken::Other(s) => simple_text(ui, s.to_string(), font, dft_color),
10955
}
11056
}
11157

112-
#[inline]
113-
fn append_indent(job: &mut LayoutJob, font: &FontId, smali_style: &SmaliStyle, indent: usize) {
114-
let str = " ".repeat(indent);
115-
append(job, &str, font, smali_style.offset);
116-
}
117-
118-
#[inline]
119-
fn append_space(job: &mut LayoutJob, font: &FontId, smali_style: &SmaliStyle) {
120-
append(job, " ", font, smali_style.offset);
121-
}
122-
123-
#[inline]
124-
fn append(job: &mut LayoutJob, text: &str, font: &FontId, color: Color32) {
125-
job.append(text, 0.0, TextFormat::simple(font.clone(), color));
58+
fn simple_text(
59+
ui: &mut Ui, text: String, font: &FontId, color: Color32,
60+
) -> Response {
61+
ui.label(LayoutJob::simple_singleline(text, font.clone(), color))
12662
}
12763

12864
#[derive(Copy, Clone, Debug)]

asm_server/src/impls/apk_load.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ pub async fn read_apk(
6161
let class_name = Arc::from(class_name);
6262
let existed = map.get(&class_name);
6363
if existed.is_none() {
64+
// dex index is the priority, the lower the index, the higher the priority.
65+
// if two classes have the same name, the one with the lower index will be kept.
6466
map.insert(class_name, (Arc::clone(&dex_file), *class_def));
6567
}
6668
} else {

asm_server/src/impls/server.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
use crate::ui::AppContainer;
1+
use crate::impls::apk_load::read_apk;
2+
use crate::server::OpenFileError;
3+
use crate::ui::{AppContainer, DirInfo, Left};
24
use crate::{AccessorEnum, AccessorMut, AsmServer, ServerMut};
35
use log::info;
46
use std::io::{Read, Seek};
57
use std::ops::DerefMut;
8+
use std::sync::Arc;
69
use std::time::Instant;
710
use tokio::runtime::Runtime;
811
use tokio::sync::mpsc;
912
use tokio::sync::mpsc::Sender;
1013
use zip::ZipArchive;
11-
use crate::impls::apk_load::read_apk;
12-
use crate::server::OpenFileError;
1314

1415
pub enum ServerMessage {
1516
Progress(ProgressMessage),
@@ -52,7 +53,7 @@ impl AsmServer {
5253
sender
5354
}
5455

55-
pub async fn from_apk(
56+
pub async fn read_apk(
5657
apk_content: impl Read + Seek,
5758
sender: Sender<ServerMessage>,
5859
accessor: AccessorMut,
@@ -81,4 +82,12 @@ impl AsmServer {
8182
let top_mut = top.deref_mut();
8283
(*top_mut).loading_state = current_loading_state.clone();
8384
}
85+
86+
fn render_to_app(&self, app: AppContainer) {
87+
let classes = self.read_classes();
88+
let start = Instant::now();
89+
let dir_info = DirInfo::from_classes(Arc::from("Root"), &classes);
90+
info!("resolve dir info cost: {:?}", start.elapsed());
91+
app.set_left(Left { root_node: dir_info });
92+
}
8493
}

0 commit comments

Comments
 (0)