Skip to content

Commit 5b49d23

Browse files
Fix Crashlytics stack frame address selector (#119)
1 parent 967dcc7 commit 5b49d23

3 files changed

Lines changed: 83 additions & 1 deletion

File tree

source/Firebase/Crashlytics/ApiDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ interface StackFrame
102102

103103
// + (instancetype)stackFrameWithAddress:(NSUInteger)address;
104104
[Static]
105-
[Export ("stackFrameWithAddress:address")]
105+
[Export ("stackFrameWithAddress:")]
106106
StackFrame Create (nuint address);
107107

108108
// +(instancetype _Nonnull)stackFrameWithSymbol:(NSString * _Nonnull)symbol file:(NSString * _Nonnull)file line:(NSInteger)line __attribute__((availability(swift, unavailable)));

tests/E2E/Firebase.Foundation/FirebaseFoundationE2E/FirebaseRuntimeDriftCases.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
using ObjCRuntime;
4343
#endif
4444

45+
#if ENABLE_RUNTIME_DRIFT_CASE_CRASHLYTICS_STACKFRAMEWITHADDRESS
46+
using Firebase.Crashlytics;
47+
using ObjCRuntime;
48+
#endif
49+
4550
namespace FirebaseFoundationE2E;
4651

4752
static class FirebaseRuntimeDriftCases
@@ -681,6 +686,72 @@ void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEven
681686
}
682687
#endif
683688

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+
684755
static string FormatDetail(string? value)
685756
{
686757
return string.IsNullOrWhiteSpace(value) ? "<empty>" : value;

tests/E2E/Firebase.Foundation/runtime-drift-cases.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@
7171
"version": "12.6.0"
7272
}
7373
]
74+
},
75+
{
76+
"id": "crashlytics-stackframewithaddress",
77+
"method": "VerifyCrashlyticsStackFrameWithAddressAsync",
78+
"bindingPackage": "AdamE.Firebase.iOS.Crashlytics",
79+
"packages": [
80+
{
81+
"id": "AdamE.Firebase.iOS.Crashlytics",
82+
"version": "12.6.0"
83+
}
84+
]
7485
}
7586
]
7687
}

0 commit comments

Comments
 (0)