From 56d72463f120974701084ad33e35fbe99f7df675 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 9 Feb 2026 11:58:35 +0100 Subject: [PATCH] feat(r8): Sort ambiguous no-range entries alphabetically by method name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple no-range entries with different original method names all have line mappings, sort them alphabetically. Bare method entries (no line mapping) preserve original mapping file order. Fixes test_single_line_no_line_number_stacktrace — all 10 R8 line number handling tests now pass. Co-Authored-By: Claude Opus 4.6 --- src/cache/mod.rs | 10 ++++++++++ src/mapper.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 9a86f27..e700fde 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -1214,6 +1214,7 @@ fn resolve_base_entries<'a>( let mut first_no_range_offset: Option = None; // Whether all no-range entries map to the same original method name. let mut all_no_range_same_name = true; + let mut all_no_range_have_line_mapping = true; for member in base_entries { if member.startline().is_some() { if member.original_endline != u32::MAX @@ -1223,6 +1224,9 @@ fn resolve_base_entries<'a>( } } else { no_range_count += 1; + if member.original_startline().is_none() { + all_no_range_have_line_mapping = false; + } match first_no_range_offset { None => first_no_range_offset = Some(member.original_name_offset), Some(first) if member.original_name_offset != first => { @@ -1268,6 +1272,12 @@ fn resolve_base_entries<'a>( } } + // Sort no-range frames by original method name when all have line mappings; + // bare method entries preserve original mapping file order. + if !all_no_range_same_name && all_no_range_have_line_mapping { + frames.sort_by(|a, b| a.method.cmp(b.method)); + } + frames } diff --git a/src/mapper.rs b/src/mapper.rs index cf91365..165c07b 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -361,6 +361,7 @@ fn resolve_base_entries<'s>( let mut first_no_range_name: Option<&str> = None; // Whether all no-range entries map to the same original method name. let mut all_no_range_same_name = true; + let mut all_no_range_have_line_mapping = true; for member in base_entries { if member.startline.is_some() { if member.original_endline.is_some() @@ -370,6 +371,9 @@ fn resolve_base_entries<'s>( } } else { no_range_count += 1; + if member.original_startline.is_none() { + all_no_range_have_line_mapping = false; + } match first_no_range_name { None => first_no_range_name = Some(member.original), Some(first) if member.original != first => all_no_range_same_name = false, @@ -413,6 +417,12 @@ fn resolve_base_entries<'s>( collected.rewrite_rules.extend(member.rewrite_rules.iter()); } } + + // Sort no-range frames by original method name when all have line mappings; + // bare method entries preserve original mapping file order. + if !all_no_range_same_name && all_no_range_have_line_mapping { + collected.frames.sort_by_key(|f| f.method); + } } /// A Proguard Remapper.