|
42 | 42 | using ObjCRuntime; |
43 | 43 | #endif |
44 | 44 |
|
| 45 | +#if ENABLE_RUNTIME_DRIFT_CASE_CRASHLYTICS_STACKFRAMEWITHADDRESS |
| 46 | +using Firebase.Crashlytics; |
| 47 | +using ObjCRuntime; |
| 48 | +#endif |
| 49 | + |
45 | 50 | namespace FirebaseFoundationE2E; |
46 | 51 |
|
47 | 52 | static class FirebaseRuntimeDriftCases |
@@ -681,6 +686,72 @@ void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEven |
681 | 686 | } |
682 | 687 | #endif |
683 | 688 |
|
| 689 | +#if ENABLE_RUNTIME_DRIFT_CASE_CRASHLYTICS_STACKFRAMEWITHADDRESS |
| 690 | + static Task<string> VerifyCrashlyticsStackFrameWithAddressAsync() |
| 691 | + { |
| 692 | + const string staleSelector = "stackFrameWithAddress:address"; |
| 693 | + const string liveSelector = "stackFrameWithAddress:"; |
| 694 | + |
| 695 | + var signature = typeof(StackFrame).GetMethod( |
| 696 | + nameof(StackFrame.Create), |
| 697 | + BindingFlags.Static | BindingFlags.Public, |
| 698 | + binder: null, |
| 699 | + types: new[] { typeof(nuint) }, |
| 700 | + modifiers: null); |
| 701 | + if (signature is null) |
| 702 | + { |
| 703 | + throw new InvalidOperationException( |
| 704 | + $"Expected managed API '{nameof(StackFrame.Create)}({typeof(nuint).FullName})' was not found."); |
| 705 | + } |
| 706 | + |
| 707 | + var marshaledExceptionCaptured = false; |
| 708 | + MarshalObjectiveCExceptionMode? marshaledExceptionMode = null; |
| 709 | + |
| 710 | + void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEventArgs args) |
| 711 | + { |
| 712 | + marshaledExceptionCaptured = true; |
| 713 | + marshaledExceptionMode ??= args.ExceptionMode; |
| 714 | + } |
| 715 | + |
| 716 | + Runtime.MarshalObjectiveCException += OnMarshalObjectiveCException; |
| 717 | + try |
| 718 | + { |
| 719 | + try |
| 720 | + { |
| 721 | + using var stackFrame = StackFrame.Create((nuint)1); |
| 722 | + if (stackFrame is null) |
| 723 | + { |
| 724 | + throw new InvalidOperationException( |
| 725 | + $"Selector '{liveSelector}' returned null after the binding fix. Runtime address argument type: {typeof(nuint).FullName}."); |
| 726 | + } |
| 727 | + } |
| 728 | + catch (ObjCException ex) |
| 729 | + { |
| 730 | + throw new InvalidOperationException( |
| 731 | + $"Selector '{liveSelector}' should not throw after the binding fix, but observed {ex.GetType().FullName}. " + |
| 732 | + $"Stale selector was '{staleSelector}'. " + |
| 733 | + $"Runtime address argument type: {typeof(nuint).FullName}. " + |
| 734 | + $"Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}."); |
| 735 | + } |
| 736 | + |
| 737 | + if (marshaledExceptionCaptured) |
| 738 | + { |
| 739 | + throw new InvalidOperationException( |
| 740 | + $"Selector '{liveSelector}' completed, but Runtime.MarshalObjectiveCException captured an unexpected Objective-C exception. " + |
| 741 | + $"Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}."); |
| 742 | + } |
| 743 | + |
| 744 | + return Task.FromResult( |
| 745 | + $"Corrected stale selector '{staleSelector}' to native selector '{liveSelector}'. " + |
| 746 | + $"Runtime address argument type: {typeof(nuint).FullName}; StackFrame.Create returned without ObjC exception."); |
| 747 | + } |
| 748 | + finally |
| 749 | + { |
| 750 | + Runtime.MarshalObjectiveCException -= OnMarshalObjectiveCException; |
| 751 | + } |
| 752 | + } |
| 753 | +#endif |
| 754 | + |
684 | 755 | static string FormatDetail(string? value) |
685 | 756 | { |
686 | 757 | return string.IsNullOrWhiteSpace(value) ? "<empty>" : value; |
|
0 commit comments