Skip to content

Commit 08f5382

Browse files
authored
feat(r8): Support rewriteFrames annotation in ProguardMapper (#66)
Part of #63 - Added new header parsing - Adapted ProguardMapper to ccollect the frames and then apply the rewrite rules on the entire vector - Added parsing of the exception for `remap_stacktrace` which accepts a plain string -- we need that info to decide whether we remove inner frames or not See linked issue for more details.
1 parent ce85efb commit 08f5382

File tree

6 files changed

+657
-104
lines changed

6 files changed

+657
-104
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/builder.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,29 @@ pub(crate) struct MethodInfo {
121121
pub(crate) is_outline: bool,
122122
}
123123

124+
/// Supported rewrite frame actions.
125+
#[derive(Clone, Debug, PartialEq, Eq)]
126+
pub(crate) enum RewriteAction<'s> {
127+
RemoveInnerFrames(usize),
128+
/// Placeholder to retain unsupported action strings for future handling.
129+
Unknown(&'s str),
130+
}
131+
132+
/// Supported rewrite frame conditions.
133+
#[derive(Clone, Debug, PartialEq, Eq)]
134+
pub(crate) enum RewriteCondition<'s> {
135+
Throws(&'s str),
136+
/// Placeholder to retain unsupported condition strings for future handling.
137+
Unknown(&'s str),
138+
}
139+
140+
/// A rewrite frame rule attached to a method mapping.
141+
#[derive(Clone, Debug, PartialEq, Eq)]
142+
pub(crate) struct RewriteRule<'s> {
143+
pub(crate) conditions: Vec<RewriteCondition<'s>>,
144+
pub(crate) actions: Vec<RewriteAction<'s>>,
145+
}
146+
124147
/// A member record in a Proguard file.
125148
#[derive(Clone, Debug)]
126149
pub(crate) struct Member<'s> {
@@ -136,6 +159,51 @@ pub(crate) struct Member<'s> {
136159
pub(crate) original_endline: Option<usize>,
137160
/// Optional outline callsite positions map attached to this member.
138161
pub(crate) outline_callsite_positions: Option<HashMap<usize, usize>>,
162+
/// Optional rewrite rules attached to this member.
163+
pub(crate) rewrite_rules: Vec<RewriteRule<'s>>,
164+
}
165+
166+
fn parse_rewrite_rule<'s>(conditions: &[&'s str], actions: &[&'s str]) -> Option<RewriteRule<'s>> {
167+
if conditions.is_empty() || actions.is_empty() {
168+
return None;
169+
}
170+
171+
let mut parsed_conditions = Vec::with_capacity(conditions.len());
172+
for condition in conditions {
173+
let condition = condition.trim();
174+
if condition.is_empty() {
175+
return None;
176+
}
177+
if let Some(rest) = condition.strip_prefix("throws(") {
178+
let descriptor = rest.strip_suffix(')')?;
179+
if descriptor.is_empty() {
180+
return None;
181+
}
182+
parsed_conditions.push(RewriteCondition::Throws(descriptor));
183+
} else {
184+
parsed_conditions.push(RewriteCondition::Unknown(condition));
185+
}
186+
}
187+
188+
let mut parsed_actions = Vec::with_capacity(actions.len());
189+
for action in actions {
190+
let action = action.trim();
191+
if action.is_empty() {
192+
return None;
193+
}
194+
if let Some(rest) = action.strip_prefix("removeInnerFrames(") {
195+
let count_str = rest.strip_suffix(')')?;
196+
let count = count_str.parse().ok()?;
197+
parsed_actions.push(RewriteAction::RemoveInnerFrames(count));
198+
} else {
199+
parsed_actions.push(RewriteAction::Unknown(action));
200+
}
201+
}
202+
203+
Some(RewriteRule {
204+
conditions: parsed_conditions,
205+
actions: parsed_actions,
206+
})
139207
}
140208

141209
/// A collection of member records for a particular class
@@ -196,6 +264,7 @@ impl<'s> ParsedProguardMapping<'s> {
196264
// Consume R8 headers attached to this class.
197265
while let Some(ProguardRecord::R8Header(r8_header)) = records.peek() {
198266
match r8_header {
267+
R8Header::RewriteFrame { .. } => {}
199268
R8Header::SourceFile { file_name } => {
200269
current_class.source_file = Some(file_name)
201270
}
@@ -251,6 +320,7 @@ impl<'s> ParsedProguardMapping<'s> {
251320
.entry((current_class_obfuscated, ObfuscatedName(obfuscated)))
252321
.or_default();
253322

323+
let mut rewrite_rules: Vec<RewriteRule<'s>> = Vec::new();
254324
let method = MethodKey {
255325
// Save the receiver name, keeping track of whether it's the current class
256326
// (i.e. the one to which this member record belongs) or another class.
@@ -276,6 +346,14 @@ impl<'s> ParsedProguardMapping<'s> {
276346
R8Header::Outline => {
277347
method_info.is_outline = true;
278348
}
349+
R8Header::RewriteFrame {
350+
conditions,
351+
actions,
352+
} => {
353+
if let Some(rule) = parse_rewrite_rule(conditions, actions) {
354+
rewrite_rules.push(rule);
355+
}
356+
}
279357
R8Header::OutlineCallsite {
280358
positions,
281359
outline: _,
@@ -302,6 +380,7 @@ impl<'s> ParsedProguardMapping<'s> {
302380
original_startline,
303381
original_endline,
304382
outline_callsite_positions,
383+
rewrite_rules,
305384
};
306385

307386
members.all.push(member.clone());

0 commit comments

Comments
 (0)