1use std::{collections::BTreeMap, mem, ops::Deref};
9
10use crate::{
11 analysis::{self, SymbolMap},
12 index::{IdMap, SymbolId},
13};
14
15pub struct SymbolMapping {
16 left_to_right: IdMap<SymbolId, SymbolId>,
18 left_non_matched: Vec<SymbolId>,
19 right_non_matched: Vec<SymbolId>,
20}
21
22impl SymbolMapping {
23 pub fn mapped_list(&self) -> impl Iterator<Item = (SymbolId, SymbolId)> + use<'_> {
24 self.left_to_right
25 .iter()
26 .map(|(right, left)| (right, *left))
27 }
28
29 pub fn map(&self, left: SymbolId) -> Option<SymbolId> {
30 self.left_to_right.get(left).copied()
31 }
32 pub fn left_only(&self) -> impl Iterator<Item = SymbolId> + use<'_> {
33 self.left_non_matched.iter().copied()
34 }
35 pub fn right_only(&self) -> impl Iterator<Item = SymbolId> + use<'_> {
36 self.right_non_matched.iter().copied()
37 }
38}
39
40type SVec<T, const SIZE: usize = 4> = smallvec::SmallVec<[T; SIZE]>;
41
42pub struct Differ<L, R> {
43 left: L,
44 right: R,
45}
46
47impl<'left, 'right, L, R> Differ<L, R>
48where
49 L: SymbolMapWithContent<'left>,
50 R: SymbolMapWithContent<'right>,
51{
52 pub fn new(left: L, right: R) -> Self {
53 Self { left, right }
54 }
55 pub fn symbol_map(&self) -> SymbolMapping {
56 let mut mapping = self.build_name_mapping();
57 self.try_match_wamex_split_point(&mut mapping);
58 self.refine_mapping(&mut mapping);
59 mapping
60 }
61
62 fn is_anon_name(name: &str) -> bool {
63 name.starts_with(".L") || name.starts_with("$L")
64 }
65
66 fn build_name_mapping(&self) -> SymbolMapping {
67 let mut name_to_left_symbol: BTreeMap<&str, SymbolId> = BTreeMap::new();
70 let mut duplicate_left: BTreeMap<&str, SVec<SymbolId>> = BTreeMap::new();
71 for (sym_id, symbol) in self.left.symbols().iter() {
72 if let Some(name) = &symbol.linking_name {
73 if Self::is_anon_name(name) {
74 continue;
75 }
76 if let Some(prev) = name_to_left_symbol.insert(name, sym_id) {
77 duplicate_left.entry(name).or_default().push(prev);
78 }
79 }
80 }
81
82 let duplicate_names: SVec<_, 16> = duplicate_left.keys().cloned().collect();
84 for name in duplicate_names {
85 let last = name_to_left_symbol.remove(name).unwrap();
86 let duplicates = duplicate_left.get_mut(name).unwrap();
87 duplicates.push(last);
88 }
89
90 let mut mapping = IdMap::new();
92
93 let mut non_matched_right_symbols: Vec<SymbolId> = Vec::new();
94 let mut dups: SVec<_, 16> = SVec::new();
95 for (right_sym_id, right_symbol) in self.right.symbols().iter() {
96 if let Some(name) = &right_symbol.linking_name
97 && !Self::is_anon_name(name)
98 {
99 if let Some(&left_sym_id) = name_to_left_symbol.get(name.deref()) {
100 if let Some(dup) = mapping.insert(left_sym_id, right_sym_id) {
101 dups.push(left_sym_id);
102 non_matched_right_symbols.push(dup);
103 }
104 continue;
105 }
106 }
107 non_matched_right_symbols.push(right_sym_id);
108 }
109
110 for dup in dups {
112 let right = mapping.remove(dup).unwrap();
113 non_matched_right_symbols.push(right);
114 }
115
116 let non_matched_left_symbols: Vec<SymbolId> = self
118 .left
119 .symbols()
120 .iter()
121 .filter_map(|(left_sym_id, _)| {
122 if mapping.get(left_sym_id).is_none() {
123 Some(left_sym_id)
124 } else {
125 None
126 }
127 })
128 .collect();
129
130 SymbolMapping {
131 left_to_right: mapping,
132 left_non_matched: non_matched_left_symbols,
133 right_non_matched: non_matched_right_symbols,
134 }
135 }
136
137 fn wamex_parse_name(name: &str) -> Option<(&str, &str)> {
138 use analysis::split_point::{
139 SPLIT_EXPORT_POSTFIX, SPLIT_IMPORT_POSTFIX, WAMEX_ENTRY_PREFIX, parser,
140 };
141 if let Some(v) = parser(name, WAMEX_ENTRY_PREFIX, SPLIT_IMPORT_POSTFIX) {
142 return Some(v);
143 };
144 parser(name, WAMEX_ENTRY_PREFIX, SPLIT_EXPORT_POSTFIX)
145 }
146 fn try_match_wamex_split_point(&self, mapping: &mut SymbolMapping) {
148 let mut non_matched_wamex_left_symbols: BTreeMap<&str, SVec<(&str, SymbolId)>> =
151 BTreeMap::new();
152 let mut non_matched_wamex_right_symbols: BTreeMap<&str, SVec<(&str, SymbolId)>> =
153 BTreeMap::new();
154 for left in &mapping.left_non_matched {
155 let left_symbol = &self.left.symbols().get(*left).unwrap();
156 let Some(name) = &left_symbol.linking_name else {
157 continue;
158 };
159 if !name.contains(analysis::split_point::WAMEX_ENTRY_PREFIX) {
160 continue;
161 }
162 let Some((module, fn_name)) = Self::wamex_parse_name(name) else {
163 continue;
164 };
165 non_matched_wamex_left_symbols
166 .entry(module)
167 .or_default()
168 .push((fn_name, *left));
169 }
170 for right in &mapping.right_non_matched {
171 let right_symbol = &self.right.symbols().get(*right).unwrap();
172 let Some(name) = &right_symbol.linking_name else {
173 continue;
174 };
175 if !name.contains(analysis::split_point::WAMEX_ENTRY_PREFIX) {
176 continue;
177 }
178 let Some((module, fn_name)) = Self::wamex_parse_name(name) else {
179 continue;
180 };
181 non_matched_wamex_right_symbols
182 .entry(module)
183 .or_default()
184 .push((fn_name, *right));
185 }
186 for (module, mut left_syms) in non_matched_wamex_left_symbols {
188 let Some(mut right_syms) = non_matched_wamex_right_symbols.remove(module) else {
189 continue;
190 };
191 left_syms.sort_by_key(|(fn_name, _)| *fn_name);
192 right_syms.sort_by_key(|(fn_name, _)| *fn_name);
193 for (left, right) in left_syms.into_iter().zip(right_syms.into_iter()) {
194 log::info!(
195 "Matched wamex split point symbol: module: {module}, left: {:?}, right: {:?}",
196 left.0,
197 right.0
198 );
199 mapping.left_to_right.insert(left.1, right.1);
200 mapping.left_non_matched.retain(|v| *v != left.1);
202 mapping.right_non_matched.retain(|v| *v != right.1);
203 }
204 }
205 }
206
207 fn is_same_content(
209 &self,
210 mapping: &SymbolMapping,
211 left_sym_id: SymbolId,
212 right_sym_id: SymbolId,
213 ) -> Result<(), ReplaceDetail> {
214 let left_symbol = &self.left.symbols().get(left_sym_id).unwrap();
215 let right_symbol = &self.right.symbols().get(right_sym_id).unwrap();
216
217 let left_content = self.left.stable_content(left_sym_id);
218 let right_content = self.right.stable_content(right_sym_id);
219 if left_content != right_content {
220 return Err(ReplaceDetail::BodyChanged);
221 }
222
223 let right_childs = right_symbol.childs().collect::<Vec<_>>();
225
226 let mut left_childs: Vec<SymbolId> = Vec::new();
228 for left_sym in left_symbol.childs() {
229 let Some(&right_sym_mapped) = mapping.left_to_right.get(left_sym) else {
230 return Err(ReplaceDetail::UnresolvedChildren(left_sym));
231 };
232 left_childs.push(right_sym_mapped);
233 }
234
235 if left_childs.len() != right_childs.len() {
236 return Err(ReplaceDetail::ChildrenChanged);
237 }
238
239 Ok(())
240 }
241
242 fn refine_mapping(&self, mapping: &mut SymbolMapping) {
249 let mut left_parent_map: BTreeMap<SymbolId, SymbolContext> = BTreeMap::new();
251 for (sym_id, symbol) in self.left.symbols().iter() {
252 for child in symbol.childs() {
253 left_parent_map
254 .entry(child)
255 .or_default()
256 .parents
257 .push(sym_id);
258 }
259 }
260
261 let mut right_parent_map: BTreeMap<SymbolId, SymbolContext> = BTreeMap::new();
263 for (sym_id, symbol) in self.right.symbols().iter() {
264 for child in symbol.childs() {
265 right_parent_map
266 .entry(child)
267 .or_default()
268 .parents
269 .push(sym_id);
270 }
271 }
272
273 let mut right_candidates = BTreeMap::<_, SVec<_>>::new();
281 for right_sym in std::mem::take(&mut mapping.right_non_matched) {
282 let right_symbol = &self.right.symbols().get(right_sym).unwrap();
283 let context = right_parent_map
284 .get(&right_sym)
285 .cloned()
286 .unwrap_or_default();
287
288 let key = SymbolKey {
292 stable_name: right_symbol.stable_name(),
293 };
294
295 let entry = right_candidates.entry(key).or_default();
296 entry.push(SymbolWithContext {
297 symbol: right_sym,
298 context,
299 });
300 }
301 let mut queue = std::mem::take(&mut mapping.left_non_matched);
304
305 loop {
308 let queue_len = queue.len();
309 let mut left_candidates = BTreeMap::<_, SVec<_>>::new();
311 for left_sym in std::mem::take(&mut queue) {
312 let left_mapped_candidate_key = {
314 let left_symbol = &self.left.symbols().get(left_sym).unwrap();
315
316 SymbolKey {
317 stable_name: left_symbol.stable_name(),
318 }
319 };
320
321 let mut context = left_parent_map.get(&left_sym).cloned().unwrap_or_default();
322 let parents: Option<SVec<_>> = std::mem::take(&mut context.parents)
323 .into_iter()
324 .map(|parent| mapping.left_to_right.get(parent).copied())
325 .collect();
326 let Some(parents) = parents else {
327 queue.push(left_sym);
329 continue;
330 };
331 context.parents = parents;
332
333 left_candidates
334 .entry(left_mapped_candidate_key)
335 .or_default()
336 .push(SymbolWithContext {
337 symbol: left_sym,
338 context,
339 });
340 }
341 for (key, mut left_syms) in left_candidates {
342 let Some(mut right_syms) = right_candidates.remove(&key) else {
343 for s in left_syms {
346 mapping.left_non_matched.push(s.symbol);
347 }
348 continue;
349 };
350 mapping.match_list_by_context(&mut left_syms, &mut right_syms);
351 if !right_syms.is_empty() {
354 assert!(right_candidates.insert(key, right_syms).is_none());
355 }
356 for s in left_syms {
357 queue.push(s.symbol);
358 }
359 }
360
361 if queue.is_empty() || queue.len() == queue_len {
362 break;
363 }
364 }
365
366 for (_, right_syms) in right_candidates {
368 for s in right_syms {
369 mapping.right_non_matched.push(s.symbol);
370 }
371 }
372
373 for left_sym in queue {
374 mapping.left_non_matched.push(left_sym);
375 }
376 }
377
378 pub fn build_diff(&self, mapping: &SymbolMapping) -> DiffResult {
379 let mut diff_result = DiffResult::new();
380
381 for (left_sym_id, right_sym_id) in mapping.mapped_list() {
383 match self.is_same_content(mapping, left_sym_id, right_sym_id) {
384 Ok(()) => diff_result.push_same(left_sym_id, right_sym_id),
385 Err(detail) => diff_result.push_replaced(left_sym_id, right_sym_id, detail),
386 }
387 }
388
389 for left_sym_id in mapping.left_only() {
391 diff_result.push_removed(left_sym_id);
392 }
393
394 for right_sym_id in mapping.right_only() {
396 diff_result.push_added(right_sym_id);
397 }
398
399 diff_result
400 }
401
402 fn left_sym_name<'a>(&'a self, sym_id: SymbolId) -> Option<&'a str>
403 where
404 'left: 'a,
405 {
406 self.left.symbols().get(sym_id).map(|s| &*s.name)
407 }
408 fn right_sym_name<'a>(&'a self, sym_id: SymbolId) -> Option<&'a str>
409 where
410 'right: 'a,
411 {
412 self.right.symbols().get(sym_id).map(|s| &*s.name)
413 }
414 pub fn debug_diff(&self, diff: &DiffResult) {
415 let replaced_iter = diff.replaced();
416 let added_iter = diff.added();
417 let removed_iter = diff.removed();
418 println!(
420 "Replaced: {}, Added: {}, Removed: {}, Same: {}",
421 replaced_iter.clone().count(),
422 added_iter.clone().count(),
423 removed_iter.clone().count(),
424 diff.same().count()
425 );
426
427 for entry in added_iter {
428 let DiffEntry::Added { right } = entry else {
429 continue;
430 };
431 let name = self.right_sym_name(*right).unwrap_or("<unknown>");
432 println!("Added: {name} [{index}]", index = right);
433 }
434 for entry in removed_iter {
435 let DiffEntry::Removed { left } = entry else {
436 continue;
437 };
438 let name = self.left_sym_name(*left).unwrap_or("<unknown>");
439 println!("Removed: {name} [{index}]", index = left);
440 }
441 for entry in replaced_iter {
442 let DiffEntry::Replaced {
443 left,
444 right,
445 detail,
446 } = entry
447 else {
448 continue;
449 };
450 let left_name = self.left_sym_name(*left).unwrap_or("<unknown>");
451 let right_name = self.right_sym_name(*right).unwrap_or("<unknown>");
452
453 let detail = match detail {
454 ReplaceDetail::BodyChanged => {
455 let left_content = self.left.stable_content(*left).unwrap_or_default();
456 let right_content = self.right.stable_content(*right).unwrap_or_default();
457 format_args!(
458 "Body changed from {left_content} to {right_content}",
459 left_content = hex::encode(left_content),
460 right_content = hex::encode(right_content)
461 )
462 }
463 ReplaceDetail::ChildrenChanged => {
464 format_args!("Children changed")
466 }
467 ReplaceDetail::UnresolvedChildren(v) => {
468 format_args!("Unresolved child symbol id: {v}", v = *v)
469 }
470 };
471 println!(
472 "Replaced: {left_name} [{left_index}] -> {right_name} [{right_index}] Detail: {detail}",
473 left_index = left,
474 right_index = right
475 );
476 }
477 }
478}
479
480#[derive(Debug, Clone)]
482pub struct DiffResult {
483 entries: Vec<DiffEntry>,
484}
485
486impl FromIterator<DiffEntry> for DiffResult {
487 fn from_iter<T: IntoIterator<Item = DiffEntry>>(iter: T) -> Self {
488 let mut diff_result = DiffResult::new();
489 for entry in iter {
490 diff_result.push(entry);
491 }
492 diff_result
493 }
494}
495
496impl DiffResult {
497 pub fn new() -> Self {
498 Self {
499 entries: Vec::new(),
500 }
501 }
502 pub fn is_empty(&self) -> bool {
503 self.entries.is_empty()
504 }
505 pub fn push(&mut self, entry: DiffEntry) {
506 self.entries.push(entry);
507 }
508 pub fn push_added(&mut self, right: SymbolId) {
509 self.entries.push(DiffEntry::Added { right });
510 }
511 pub fn push_removed(&mut self, left: SymbolId) {
512 self.entries.push(DiffEntry::Removed { left });
513 }
514 pub fn push_replaced(&mut self, left: SymbolId, right: SymbolId, detail: ReplaceDetail) {
515 self.entries.push(DiffEntry::Replaced {
516 left,
517 right,
518 detail,
519 });
520 }
521 pub fn push_same(&mut self, left: SymbolId, right: SymbolId) {
522 self.entries.push(DiffEntry::Same { left, right });
523 }
524
525 pub fn same(&self) -> impl Iterator<Item = &DiffEntry> + Clone {
528 self.entries
529 .iter()
530 .filter(|entry| matches!(entry, DiffEntry::Same { .. }))
531 }
532 pub fn all_changes(&self) -> impl Iterator<Item = &DiffEntry> + Clone {
537 self.entries.iter()
538 }
539 pub fn replaced(&self) -> impl Iterator<Item = &DiffEntry> + Clone {
541 self.entries.iter().filter(|entry| entry.is_replaced())
542 }
543 pub fn added(&self) -> impl Iterator<Item = &DiffEntry> + Clone {
545 self.entries.iter().filter(|entry| entry.is_added())
546 }
547 pub fn removed(&self) -> impl Iterator<Item = &DiffEntry> + Clone {
549 self.entries.iter().filter(|entry| entry.is_removed())
550 }
551
552 pub fn entries(&self) -> impl Iterator<Item = &DiffEntry> + Clone {
553 self.entries.iter()
554 }
555}
556
557#[derive(Debug, Clone, Copy)]
558pub enum DiffEntry {
559 Replaced {
560 left: SymbolId,
561 right: SymbolId,
562 detail: ReplaceDetail,
563 },
564 Same {
565 left: SymbolId,
566 right: SymbolId,
567 },
568 Added {
569 right: SymbolId,
570 },
571 Removed {
572 left: SymbolId,
573 },
574}
575impl DiffEntry {
576 fn is_added(&self) -> bool {
577 matches!(self, DiffEntry::Added { .. })
578 }
579 fn is_removed(&self) -> bool {
580 matches!(self, DiffEntry::Removed { .. })
581 }
582 fn is_replaced(&self) -> bool {
583 matches!(self, DiffEntry::Replaced { .. })
584 }
585}
586
587#[derive(Debug, Clone, Copy)]
588pub enum ReplaceDetail {
589 BodyChanged,
591 ChildrenChanged,
593 UnresolvedChildren(SymbolId),
596}
597
598#[derive(Ord, PartialOrd, PartialEq, Eq)]
601struct SymbolKey<'a> {
602 stable_name: Option<&'a str>,
603 }
606
607#[derive(Default, Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
608struct SymbolContext {
609 parents: SVec<SymbolId>,
610}
611
612impl SymbolContext {
613 fn num_same_parents(&self, other: &SymbolContext) -> usize {
614 let mut same = 0;
615 for parent in &self.parents {
616 if other.parents.contains(parent) {
617 same += 1;
618 }
619 }
620 same
621 }
622}
623
624#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
625struct SymbolWithContext {
626 pub symbol: SymbolId,
627 pub context: SymbolContext,
628}
629
630impl SymbolMapping {
631 fn match_list_by_context(
632 &mut self,
633 old_contexts: &mut SVec<SymbolWithContext>,
634 new_contexts: &mut SVec<SymbolWithContext>,
635 ) {
636 let len_before = old_contexts.len() + new_contexts.len() + self.left_to_right.len() * 2;
637
638 self.match_by_exact_parents(old_contexts, new_contexts);
640
641 let len_after = old_contexts.len() + new_contexts.len() + self.left_to_right.len() * 2;
642 debug_assert_eq!(len_before, len_after);
643
644 self.match_by_changed_parents(old_contexts, new_contexts);
646
647 let len_after = old_contexts.len() + new_contexts.len() + self.left_to_right.len() * 2;
648 debug_assert_eq!(len_before, len_after);
649 }
650
651 fn match_by_exact_parents(
653 &mut self,
654 old_contexts: &mut SVec<SymbolWithContext>,
655 new_contexts: &mut SVec<SymbolWithContext>,
656 ) {
657 let old_iter = mem::take(old_contexts);
658
659 let mut new_vec = mem::take(new_contexts);
660
661 for old_ctx in old_iter {
662 let with_same_context = new_vec
663 .iter()
664 .enumerate()
665 .find(|(_, new_ctx)| &old_ctx.context == &new_ctx.context);
666
667 let Some((id, _)) = with_same_context else {
668 old_contexts.push(old_ctx);
669 continue;
670 };
671 let new_ctx = new_vec.remove(id);
672 self.left_to_right.insert(old_ctx.symbol, new_ctx.symbol);
673 }
674
675 *new_contexts = new_vec;
676 }
677
678 fn match_by_changed_parents(
680 &mut self,
681 old_contexts: &mut SVec<SymbolWithContext>,
682 new_contexts: &mut SVec<SymbolWithContext>,
683 ) {
684 let old_iter = mem::take(old_contexts);
685
686 let mut new_vec = mem::take(new_contexts)
687 .into_iter()
688 .enumerate()
689 .collect::<Vec<_>>();
690
691 for old_ctx in old_iter {
692 new_vec.sort_by_key(|(_, b)| b.context.num_same_parents(&old_ctx.context));
693
694 let new_ctx = match new_vec.as_slice() {
696 &[.., (_, ref prev), _] if prev.context.num_same_parents(&old_ctx.context) > 0 => {
699 old_contexts.push(old_ctx);
700 continue;
701 }
702 &[] => {
704 old_contexts.push(old_ctx);
705 continue;
706 }
707 &[(_, ref new_ctx)] if new_ctx.context.num_same_parents(&old_ctx.context) == 0 => {
709 old_contexts.push(old_ctx);
710 continue;
711 }
712 &[..] => new_vec.pop().unwrap().1,
713 };
714
715 log::warn!(
717 "Matched symbol by changed context: old {:?}, new {:?}",
718 old_ctx,
719 new_ctx
720 );
721
722 self.left_to_right.insert(old_ctx.symbol, new_ctx.symbol);
723 }
724 new_vec.sort_by_key(|(original_order, _)| *original_order);
725 *new_contexts = new_vec.into_iter().map(|(_, ctx)| ctx).collect();
726 }
727}
728
729pub trait SymbolMapWithContent<'src> {
730 fn stable_content(&self, sym_id: SymbolId) -> Option<Vec<u8>>;
731 fn symbols(&self) -> &SymbolMap<'src>;
732}
733
734impl<'src> SymbolMapWithContent<'src> for analysis::ModuleInfo<'src> {
735 fn stable_content(&self, sym_id: SymbolId) -> Option<Vec<u8>> {
736 self.symbols
737 .get(sym_id)
738 .and_then(|s| s.stable_content(self))
739 }
740 fn symbols(&self) -> &SymbolMap<'src> {
741 &self.symbols
742 }
743}
744
745#[derive(Clone, Default, Debug)]
746pub struct StaticModuleInfo {
747 symbols: SymbolMap<'static>,
748 contents: IdMap<SymbolId, Vec<u8>>,
749}
750
751impl StaticModuleInfo {
752 pub fn empty() -> Self {
753 Self {
754 symbols: SymbolMap::empty(),
755 contents: IdMap::new(),
756 }
757 }
758 pub fn new(info: &analysis::ModuleInfo<'_>) -> Self {
759 let symbols = info.symbols.clone_owned();
760 let mut contents = IdMap::new();
761 for (sym_id, symbol) in info.symbols.iter() {
762 if let Some(content) = symbol.stable_content(info) {
763 contents.insert(sym_id, content);
764 }
765 }
766 Self { symbols, contents }
767 }
768}
769
770impl SymbolMapWithContent<'static> for StaticModuleInfo {
771 fn stable_content(&self, sym_id: SymbolId) -> Option<Vec<u8>> {
773 self.contents.get(sym_id).cloned()
774 }
775 fn symbols(&self) -> &SymbolMap<'static> {
776 &self.symbols
777 }
778}
779
780impl<'a, 'src, M> SymbolMapWithContent<'src> for &'a M
781where
782 M: SymbolMapWithContent<'src>,
783{
784 fn stable_content(&self, sym_id: SymbolId) -> Option<Vec<u8>> {
785 <M as SymbolMapWithContent<'src>>::stable_content(*self, sym_id)
786 }
787 fn symbols(&self) -> &SymbolMap<'src> {
788 <M as SymbolMapWithContent<'src>>::symbols(*self)
789 }
790}