callstack / callstack/react-native-brownfield
iOS: emitOnBrownfieldMessage: can crash with std::bad_function_call when native posts before JS requires the TurboModule (cold-start race)
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- TypeScript
- Star
- 550
- Fork
- 52
- Merge trung bình
- 3 ngày 16 giờ
- Pull request đã merge (30 ngày)
- 5
Mô tả
ReactNativeBrownfieldModule.mm's +emitMessageFromNative: only checks that the Obj-C singleton exists before calling emitOnBrownfieldMessage::
+ (void)emitMessageFromNative:(NSString *)message {
if (_sharedInstance) {
[_sharedInstance emitOnBrownfieldMessage:@{ @"text": message }];
} else {
NSLog(@"ReactNativeBrownfieldModule is not initialized, dropping message");
}
}
_sharedInstance is set in -init, which runs as soon as the bridge/TurboModuleManager instantiates the Obj-C module. But the codegen SpecBase's _eventEmitterCallback (the std::function that emitOnBrownfieldMessage: invokes) is only populated inside the C++ SpecJSI constructor, which only runs when getTurboModule: fires — i.e. when JS actually evaluates TurboModuleRegistry.getEnforcing('ReactNativeBrownfield').
If a host app calls ReactNativeBrownfield.shared.postMessage(...) (→ posts BrownfieldMessageToJSNotification → handleNativeToJSMessage: → emitMessageFromNative:) after the Obj-C module exists but before JS has required the module — e.g. right as a hosting view controller's viewDidAppear fires on a cold app launch, racing bundle evaluation — _eventEmitterCallback is still empty, and calling it throws std::bad_function_call, crashing the whole app (uncaught C++ exception).
Confirmed still present on main (4.0.0) as of 2026-07-06 — emitMessageFromNative:'s guard is unchanged from what's below (also reproduced against 3.3.0 in our own app):
- (void)handleNativeToJSMessage:(NSNotification *)notification {
NSString *message = notification.userInfo[@"message"];
if (message) {
[ReactNativeBrownfieldModule emitMessageFromNative:message];
}
}
Suggested fixes
- Wrap the
_eventEmitterCallbackinvocation in a readiness check (or try/catch) inside the generatedemitOnBrownfieldMessage:, or - Expose a callback/promise so host apps can gate native→JS
postMessagecalls on "TurboModule ready" rather than just "bundle loaded" (the closest signalstartReactNative(onBundleLoaded:)currently offers).
Repro
Timing-dependent — we haven't produced a deterministic repro case, but the code path is unambiguous from source, and we hit it in production (Crashlytics, std::__1::bad_function_call: std::exception, 100% of occurrences within the first second of a session). Happy to share a sanitized stack trace if useful.
Environment
@callstack/react-native-brownfield: 3.3.0 (also read againstmain/4.0.0 source)- iOS 26.x, React Native new architecture (TurboModules/Fabric)
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu trong ReactNativeBrownfieldModule.mm với -handleNativeToJSMessage: và +emitMessageFromNative:, sau đó lần theo emitOnBrownfieldMessage: đến phần khởi tạo callback của SpecBase được tạo. Tái hiện hoặc instrument một native post trước khi JS yêu cầu TurboModule, và xác minh rằng đường dẫn khởi động lạnh này không còn bị crash khi callback không khả dụng.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- cpp, ios, objective-c, react-native
- Lĩnh vực
- api, mobile-dev
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 48/100