Skip to content

Commit

Permalink
Allow remapping just a method without line numbers (#30)
Browse files Browse the repository at this point in the history
This takes a conservative approach and will ignore mappings that
are ambiguous.
  • Loading branch information
Swatinem authored Oct 17, 2023
1 parent 1a4d3d7 commit ba90a26
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
24 changes: 22 additions & 2 deletions src/mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl<'m> Iterator for RemappedFrameIter<'m> {
let (frame, ref mut members) = self.inner.as_mut()?;

for member in members {
// skip any members which do not match our the frames line
// skip any members which do not match our frames line
if member.endline > 0 && (frame.line < member.startline || frame.line > member.endline)
{
continue;
Expand Down Expand Up @@ -190,6 +190,26 @@ impl<'s> ProguardMapper<'s> {
self.classes.get(class).map(|class| class.original)
}

/// Remaps an obfuscated Class Method.
///
/// The `class` argument has to be the fully-qualified obfuscated name of the
/// class, with its complete module prefix.
///
/// If the `method` can be resolved unambiguously, it will be returned
/// alongside the remapped `class`, otherwise `None` is being returned.
pub fn remap_method(&'s self, class: &str, method: &str) -> Option<(&'s str, &'s str)> {
let class = self.classes.get(class)?;
let mut members = class.members.get(method)?.iter();
let first = members.next()?;

// We conservatively check that all the mappings point to the same method,
// as we don’t have line numbers to disambiguate.
// We could potentially skip inlined functions here, but lets rather be conservative.
let all_matching = members.all(|member| member.original == first.original);

all_matching.then_some((class.original, first.original))
}

/// Remaps a single Stackframe.
///
/// Returns zero or more [`StackFrame`]s, based on the information in
Expand Down Expand Up @@ -231,7 +251,7 @@ impl<'s> ProguardMapper<'s> {
})
}

/// Remaps a complete Java StackTrace, similar to [`Self::remap_stacktrace`] but instead works on
/// Remaps a complete Java StackTrace, similar to [`Self::remap_stacktrace_typed`] but instead works on
/// strings as input and output.
pub fn remap_stacktrace(&self, input: &str) -> Result<String, std::fmt::Error> {
let mut stacktrace = String::new();
Expand Down
23 changes: 23 additions & 0 deletions tests/retrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,26 @@ fn test_remap_kotlin() {
.trim()
);
}

#[test]
fn test_remap_just_method() {
let mapper = ProguardMapper::from(
r#"com.exmaple.app.MainActivity -> a.b.c.d:
com.example1.domain.MyBean myBean -> p
1:1:void <init>():11:11 -> <init>
1:1:void buttonClicked(android.view.View):29:29 -> buttonClicked
2:2:void com.example1.domain.MyBean.doWork():16:16 -> buttonClicked
2:2:void buttonClicked(android.view.View):29 -> buttonClicked
1:1:void onCreate(android.os.Bundle):17:17 -> onCreate
2:5:void onCreate(android.os.Bundle):22:25 -> onCreate"#,
);

let unambiguous = mapper.remap_method("a.b.c.d", "onCreate");
assert_eq!(
unambiguous,
Some(("com.exmaple.app.MainActivity", "onCreate"))
);

let ambiguous = mapper.remap_method("a.b.c.d", "buttonClicked");
assert_eq!(ambiguous, None);
}

0 comments on commit ba90a26

Please sign in to comment.