From bf546ef743b5d61c1d698fc9ae8fb4b48e60e11b Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 13 Nov 2025 15:25:20 +0100 Subject: [PATCH 1/4] feat(outline): Expose is_outline and prepare_frame_for_mapping --- Cargo.lock | 2 +- src/cache/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7765341..23debbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -334,7 +334,7 @@ dependencies = [ [[package]] name = "proguard" -version = "5.6.2" +version = "5.7.0" dependencies = [ "criterion", "serde", diff --git a/src/cache/mod.rs b/src/cache/mod.rs index b681761..6d19030 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -402,7 +402,7 @@ impl<'data> ProguardCache<'data> { /// Determines if a frame refers to an outline method, either via the /// method-level flag or via any matching mapping entry for the frame line. - fn is_outline_frame( + pub fn is_outline_frame( &self, class: &str, method: &str, @@ -449,7 +449,7 @@ impl<'data> ProguardCache<'data> { } /// Applies any carried outline position to the frame line and returns the adjusted frame. - fn prepare_frame_for_mapping<'a>( + pub fn prepare_frame_for_mapping<'a>( &self, frame: &StackFrame<'a>, carried_outline_pos: &mut Option, From 9237c4fa5a1064931222b04b1a57bb326abd11ce Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 26 Nov 2025 17:27:51 +0100 Subject: [PATCH 2/4] Rely on method-level outline flag and dont inspect all mappings --- src/cache/mod.rs | 14 +++++++------- src/mapper.rs | 13 ++++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 6d19030..66afe8a 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -400,13 +400,14 @@ impl<'data> ProguardCache<'data> { }) } - /// Determines if a frame refers to an outline method, either via the - /// method-level flag or via any matching mapping entry for the frame line. + /// Determines if a frame refers to an outline method via the method-level flag. + /// Outline metadata is consistent across all mapping entries for a method, so + /// we only need to inspect the method itself instead of individual line ranges. pub fn is_outline_frame( &self, class: &str, method: &str, - line: usize, + _line: usize, parameters: Option<&str>, ) -> bool { let Some(class) = self.get_class(class) else { @@ -442,10 +443,9 @@ impl<'data> ProguardCache<'data> { range }; - candidates.iter().any(|m| { - m.is_outline() - && (m.endline == 0 || (line >= m.startline as usize && line <= m.endline as usize)) - }) + candidates + .first() + .map_or(false, |member| member.is_outline()) } /// Applies any carried outline position to the frame line and returns the adjusted frame. diff --git a/src/mapper.rs b/src/mapper.rs index 0e8643c..b0b187c 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -359,20 +359,21 @@ impl<'s> ProguardMapper<'s> { }) } - /// Determines if a frame refers to an outline method, either via the - /// method-level flag or via any matching mapping entry for the frame line. + /// Determines if a frame refers to an outline method via the method-level flag. + /// Outline metadata is consistent across all mappings for a method, so we can + /// inspect the first entry rather than matching individual line ranges. fn is_outline_frame( &self, class: &str, method: &str, - line: usize, + _line: usize, parameters: Option<&str>, ) -> bool { self.classes .get(class) .and_then(|c| c.members.get(method)) .map(|ms| { - let mappings: &[_] = if let Some(params) = parameters { + let mappings: &[MemberMapping<'_>] = if let Some(params) = parameters { match ms.mappings_by_params.get(params) { Some(v) => &v[..], None => &[], @@ -380,9 +381,7 @@ impl<'s> ProguardMapper<'s> { } else { &ms.all_mappings[..] }; - mappings.iter().any(|m| { - m.is_outline && (m.endline == 0 || (line >= m.startline && line <= m.endline)) - }) + mappings.first().map_or(false, |m| m.is_outline) }) .unwrap_or(false) } From fd5c16ea7eff209274f09eae01569610c7dc8ca0 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 26 Nov 2025 17:29:54 +0100 Subject: [PATCH 3/4] fix lint --- src/cache/mod.rs | 4 +--- src/mapper.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 66afe8a..e2df100 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -443,9 +443,7 @@ impl<'data> ProguardCache<'data> { range }; - candidates - .first() - .map_or(false, |member| member.is_outline()) + candidates.first().is_some_and(|member| member.is_outline()) } /// Applies any carried outline position to the frame line and returns the adjusted frame. diff --git a/src/mapper.rs b/src/mapper.rs index b0b187c..5016429 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -381,7 +381,7 @@ impl<'s> ProguardMapper<'s> { } else { &ms.all_mappings[..] }; - mappings.first().map_or(false, |m| m.is_outline) + mappings.first().is_some_and(|m| m.is_outline) }) .unwrap_or(false) } From 69165e34c91c1c4dd1f2b22360632015572241f8 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 26 Nov 2025 21:01:24 +0100 Subject: [PATCH 4/4] Drop unnecessary arguments from is_outline_frame --- src/cache/mod.rs | 62 ++++++++++++------------------------------------ src/mapper.rs | 42 +++++++------------------------- 2 files changed, 23 insertions(+), 81 deletions(-) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index e2df100..16308e9 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -402,45 +402,23 @@ impl<'data> ProguardCache<'data> { /// Determines if a frame refers to an outline method via the method-level flag. /// Outline metadata is consistent across all mapping entries for a method, so - /// we only need to inspect the method itself instead of individual line ranges. - pub fn is_outline_frame( - &self, - class: &str, - method: &str, - _line: usize, - parameters: Option<&str>, - ) -> bool { + /// we only need to inspect the method metadata instead of individual lines. + pub fn is_outline_frame(&self, class: &str, method: &str) -> bool { let Some(class) = self.get_class(class) else { return false; }; - let candidates: &[raw::Member] = if let Some(params) = parameters { - let Some(members) = self.get_class_members_by_params(class) else { - return false; - }; - let Some(range) = Self::find_range_by_binary_search(members, |m| { - let Ok(obfuscated_name) = self.read_string(m.obfuscated_name_offset) else { - return Ordering::Greater; - }; - let p = self.read_string(m.params_offset).unwrap_or_default(); - (obfuscated_name, p).cmp(&(method, params)) - }) else { - return false; - }; - range - } else { - let Some(members) = self.get_class_members(class) else { - return false; - }; - let Some(range) = Self::find_range_by_binary_search(members, |m| { - let Ok(obfuscated_name) = self.read_string(m.obfuscated_name_offset) else { - return Ordering::Greater; - }; - obfuscated_name.cmp(method) - }) else { - return false; + let Some(members) = self.get_class_members(class) else { + return false; + }; + + let Some(candidates) = Self::find_range_by_binary_search(members, |m| { + let Ok(obfuscated_name) = self.read_string(m.obfuscated_name_offset) else { + return Ordering::Greater; }; - range + obfuscated_name.cmp(method) + }) else { + return false; }; candidates.first().is_some_and(|member| member.is_outline()) @@ -481,12 +459,7 @@ impl<'data> ProguardCache<'data> { None => match stacktrace::parse_frame(line) { None => writeln!(&mut stacktrace, "{line}")?, Some(frame) => { - if self.is_outline_frame( - frame.class, - frame.method, - frame.line, - frame.parameters, - ) { + if self.is_outline_frame(frame.class, frame.method) { carried_outline_pos = Some(frame.line); } else { let effective_frame = @@ -518,12 +491,7 @@ impl<'data> ProguardCache<'data> { } }, Some(frame) => { - if self.is_outline_frame( - frame.class, - frame.method, - frame.line, - frame.parameters, - ) { + if self.is_outline_frame(frame.class, frame.method) { carried_outline_pos = Some(frame.line); continue; } @@ -547,7 +515,7 @@ impl<'data> ProguardCache<'data> { let mut carried_outline_pos: Option = None; let mut frames: Vec> = Vec::with_capacity(trace.frames.len()); for f in trace.frames.iter() { - if self.is_outline_frame(f.class, f.method, f.line, f.parameters) { + if self.is_outline_frame(f.class, f.method) { carried_outline_pos = Some(f.line); continue; } diff --git a/src/mapper.rs b/src/mapper.rs index 5016429..421ab7f 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -360,30 +360,14 @@ impl<'s> ProguardMapper<'s> { } /// Determines if a frame refers to an outline method via the method-level flag. - /// Outline metadata is consistent across all mappings for a method, so we can - /// inspect the first entry rather than matching individual line ranges. - fn is_outline_frame( - &self, - class: &str, - method: &str, - _line: usize, - parameters: Option<&str>, - ) -> bool { + /// Outline metadata is consistent across all mappings for a method, so checking + /// a single mapping entry is sufficient. + fn is_outline_frame(&self, class: &str, method: &str) -> bool { self.classes .get(class) .and_then(|c| c.members.get(method)) - .map(|ms| { - let mappings: &[MemberMapping<'_>] = if let Some(params) = parameters { - match ms.mappings_by_params.get(params) { - Some(v) => &v[..], - None => &[], - } - } else { - &ms.all_mappings[..] - }; - mappings.first().is_some_and(|m| m.is_outline) - }) - .unwrap_or(false) + .and_then(|ms| ms.all_mappings.first()) + .is_some_and(|m| m.is_outline) } /// Applies any carried outline position to the frame line and returns the adjusted frame. @@ -519,12 +503,7 @@ impl<'s> ProguardMapper<'s> { None => match stacktrace::parse_frame(line) { None => writeln!(&mut stacktrace, "{line}")?, Some(frame) => { - if self.is_outline_frame( - frame.class, - frame.method, - frame.line, - frame.parameters, - ) { + if self.is_outline_frame(frame.class, frame.method) { carried_outline_pos = Some(frame.line); } else { let effective_frame = @@ -556,12 +535,7 @@ impl<'s> ProguardMapper<'s> { } }, Some(frame) => { - if self.is_outline_frame( - frame.class, - frame.method, - frame.line, - frame.parameters, - ) { + if self.is_outline_frame(frame.class, frame.method) { carried_outline_pos = Some(frame.line); continue; } @@ -586,7 +560,7 @@ impl<'s> ProguardMapper<'s> { let mut carried_outline_pos: Option = None; let mut frames_out = Vec::with_capacity(trace.frames.len()); for f in trace.frames.iter() { - if self.is_outline_frame(f.class, f.method, f.line, f.parameters) { + if self.is_outline_frame(f.class, f.method) { carried_outline_pos = Some(f.line); continue; }