@@ -51,7 +51,7 @@ impl fmt::Display for DeobfuscatedSignature {
5151 }
5252}
5353
54- #[ derive( Clone , Debug , PartialEq , Eq , Hash ) ]
54+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
5555struct MemberMapping < ' s > {
5656 startline : usize ,
5757 endline : usize ,
@@ -61,6 +61,8 @@ struct MemberMapping<'s> {
6161 original_startline : usize ,
6262 original_endline : Option < usize > ,
6363 is_synthesized : bool ,
64+ is_outline : bool ,
65+ outline_callsite_positions : Option < HashMap < usize , usize > > ,
6466}
6567
6668#[ derive( Clone , Debug , Default ) ]
@@ -265,7 +267,7 @@ impl<'s> ProguardMapper<'s> {
265267 . entry ( obfuscated_method. as_str ( ) )
266268 . or_default ( ) ;
267269
268- for member in members. all . iter ( ) . copied ( ) {
270+ for member in members. all . iter ( ) {
269271 method_mappings
270272 . all_mappings
271273 . push ( Self :: resolve_mapping ( & parsed, member) ) ;
@@ -274,8 +276,8 @@ impl<'s> ProguardMapper<'s> {
274276 for ( args, param_members) in members. by_params . iter ( ) {
275277 let param_mappings = method_mappings. mappings_by_params . entry ( args) . or_default ( ) ;
276278
277- for member in param_members {
278- param_mappings. push ( Self :: resolve_mapping ( & parsed, * member) ) ;
279+ for member in param_members. iter ( ) {
280+ param_mappings. push ( Self :: resolve_mapping ( & parsed, member) ) ;
279281 }
280282 }
281283 }
@@ -287,7 +289,7 @@ impl<'s> ProguardMapper<'s> {
287289
288290 fn resolve_mapping (
289291 parsed : & ParsedProguardMapping < ' s > ,
290- member : Member < ' s > ,
292+ member : & Member < ' s > ,
291293 ) -> MemberMapping < ' s > {
292294 let original_file = parsed
293295 . class_infos
@@ -306,6 +308,9 @@ impl<'s> ProguardMapper<'s> {
306308 . copied ( )
307309 . unwrap_or_default ( ) ;
308310 let is_synthesized = method_info. is_synthesized ;
311+ let is_outline = method_info. is_outline ;
312+
313+ let outline_callsite_positions = member. outline_callsite_positions . clone ( ) ;
309314
310315 MemberMapping {
311316 startline : member. startline ,
@@ -316,9 +321,94 @@ impl<'s> ProguardMapper<'s> {
316321 original_startline : member. original_startline ,
317322 original_endline : member. original_endline ,
318323 is_synthesized,
324+ is_outline,
325+ outline_callsite_positions,
319326 }
320327 }
321328
329+ /// If the previous frame was an outline and carried a position, attempt to
330+ /// map that outline position to a callsite position for the given method.
331+ fn map_outline_position (
332+ & self ,
333+ class : & str ,
334+ method : & str ,
335+ callsite_line : usize ,
336+ pos : usize ,
337+ parameters : Option < & str > ,
338+ ) -> Option < usize > {
339+ let ms = self . classes . get ( class) ?. members . get ( method) ?;
340+ let candidates: & [ _ ] = if let Some ( params) = parameters {
341+ match ms. mappings_by_params . get ( params) {
342+ Some ( v) => & v[ ..] ,
343+ None => & [ ] ,
344+ }
345+ } else {
346+ & ms. all_mappings [ ..]
347+ } ;
348+
349+ // Find the member mapping covering the callsite line, then map the pos.
350+ candidates
351+ . iter ( )
352+ . filter ( |m| {
353+ m. endline == 0 || ( callsite_line >= m. startline && callsite_line <= m. endline )
354+ } )
355+ . find_map ( |m| {
356+ m. outline_callsite_positions
357+ . as_ref ( )
358+ . and_then ( |mm| mm. get ( & pos) . copied ( ) )
359+ } )
360+ }
361+
362+ /// Determines if a frame refers to an outline method, either via the
363+ /// method-level flag or via any matching mapping entry for the frame line.
364+ fn is_outline_frame (
365+ & self ,
366+ class : & str ,
367+ method : & str ,
368+ line : usize ,
369+ parameters : Option < & str > ,
370+ ) -> bool {
371+ self . classes
372+ . get ( class)
373+ . and_then ( |c| c. members . get ( method) )
374+ . map ( |ms| {
375+ let mappings: & [ _ ] = if let Some ( params) = parameters {
376+ match ms. mappings_by_params . get ( params) {
377+ Some ( v) => & v[ ..] ,
378+ None => & [ ] ,
379+ }
380+ } else {
381+ & ms. all_mappings [ ..]
382+ } ;
383+ mappings. iter ( ) . any ( |m| {
384+ m. is_outline && ( m. endline == 0 || ( line >= m. startline && line <= m. endline ) )
385+ } )
386+ } )
387+ . unwrap_or ( false )
388+ }
389+
390+ /// Applies any carried outline position to the frame line and returns the adjusted frame.
391+ fn prepare_frame_for_mapping < ' a > (
392+ & self ,
393+ frame : & StackFrame < ' a > ,
394+ carried_outline_pos : & mut Option < usize > ,
395+ ) -> StackFrame < ' a > {
396+ let mut effective = frame. clone ( ) ;
397+ if let Some ( pos) = carried_outline_pos. take ( ) {
398+ if let Some ( mapped) = self . map_outline_position (
399+ effective. class ,
400+ effective. method ,
401+ effective. line ,
402+ pos,
403+ effective. parameters ,
404+ ) {
405+ effective. line = mapped;
406+ }
407+ }
408+
409+ effective
410+ }
411+
322412 /// Remaps an obfuscated Class.
323413 ///
324414 /// This works on the fully-qualified name of the class, with its complete
@@ -423,12 +513,31 @@ impl<'s> ProguardMapper<'s> {
423513 pub fn remap_stacktrace ( & self , input : & str ) -> Result < String , std:: fmt:: Error > {
424514 let mut stacktrace = String :: new ( ) ;
425515 let mut lines = input. lines ( ) ;
516+ let mut carried_outline_pos: Option < usize > = None ;
426517
427518 if let Some ( line) = lines. next ( ) {
428519 match stacktrace:: parse_throwable ( line) {
429520 None => match stacktrace:: parse_frame ( line) {
430521 None => writeln ! ( & mut stacktrace, "{line}" ) ?,
431- Some ( frame) => format_frames ( & mut stacktrace, line, self . remap_frame ( & frame) ) ?,
522+ Some ( frame) => {
523+ if self . is_outline_frame (
524+ frame. class ,
525+ frame. method ,
526+ frame. line ,
527+ frame. parameters ,
528+ ) {
529+ carried_outline_pos = Some ( frame. line ) ;
530+ } else {
531+ let effective_frame =
532+ self . prepare_frame_for_mapping ( & frame, & mut carried_outline_pos) ;
533+
534+ format_frames (
535+ & mut stacktrace,
536+ line,
537+ self . remap_frame ( & effective_frame) ,
538+ ) ?;
539+ }
540+ }
432541 } ,
433542 Some ( throwable) => {
434543 format_throwable ( & mut stacktrace, line, self . remap_throwable ( & throwable) ) ?
@@ -447,7 +556,22 @@ impl<'s> ProguardMapper<'s> {
447556 format_cause ( & mut stacktrace, line, self . remap_throwable ( & cause) ) ?
448557 }
449558 } ,
450- Some ( frame) => format_frames ( & mut stacktrace, line, self . remap_frame ( & frame) ) ?,
559+ Some ( frame) => {
560+ if self . is_outline_frame (
561+ frame. class ,
562+ frame. method ,
563+ frame. line ,
564+ frame. parameters ,
565+ ) {
566+ carried_outline_pos = Some ( frame. line ) ;
567+ continue ;
568+ }
569+
570+ let effective_frame =
571+ self . prepare_frame_for_mapping ( & frame, & mut carried_outline_pos) ;
572+
573+ format_frames ( & mut stacktrace, line, self . remap_frame ( & effective_frame) ) ?;
574+ }
451575 }
452576 }
453577 Ok ( stacktrace)
@@ -460,20 +584,22 @@ impl<'s> ProguardMapper<'s> {
460584 . as_ref ( )
461585 . and_then ( |t| self . remap_throwable ( t) ) ;
462586
463- let frames =
464- trace
465- . frames
466- . iter ( )
467- . fold ( Vec :: with_capacity ( trace. frames . len ( ) ) , |mut frames, f| {
468- let mut peek_frames = self . remap_frame ( f) . peekable ( ) ;
469- if peek_frames. peek ( ) . is_some ( ) {
470- frames. extend ( peek_frames) ;
471- } else {
472- frames. push ( f. clone ( ) ) ;
473- }
587+ let mut carried_outline_pos: Option < usize > = None ;
588+ let mut frames_out = Vec :: with_capacity ( trace. frames . len ( ) ) ;
589+ for f in trace. frames . iter ( ) {
590+ if self . is_outline_frame ( f. class , f. method , f. line , f. parameters ) {
591+ carried_outline_pos = Some ( f. line ) ;
592+ continue ;
593+ }
474594
475- frames
476- } ) ;
595+ let effective = self . prepare_frame_for_mapping ( f, & mut carried_outline_pos) ;
596+ let mut iter = self . remap_frame ( & effective) . peekable ( ) ;
597+ if iter. peek ( ) . is_some ( ) {
598+ frames_out. extend ( iter) ;
599+ } else {
600+ frames_out. push ( f. clone ( ) ) ;
601+ }
602+ }
477603
478604 let cause = trace
479605 . cause
@@ -482,7 +608,7 @@ impl<'s> ProguardMapper<'s> {
482608
483609 StackTrace {
484610 exception,
485- frames,
611+ frames : frames_out ,
486612 cause,
487613 }
488614 }
0 commit comments