From e2b081e66f277948fcb8bc546c8184c8eaff4d17 Mon Sep 17 00:00:00 2001 From: zhongwuzw Date: Tue, 11 Mar 2025 06:06:55 -0700 Subject: [PATCH] Fixes TextInput crashes when any text is entered while running as iOS app on apple silicon mac (#49320) Summary: Fixes https://github.com/facebook/react-native/issues/48544. We can make `RCTWeakEventEmitterWrapper` to subclass `NSDictionary` that Textinput supports encode it. System allowed classes are: ``` Allowed classes are: {( "'NSMorphology' (0x20088b6d8) [/System/Library/Frameworks/Foundation.framework]", "'NSString' (0x2003f4738) [/System/Library/Frameworks/Foundation.framework]", "'NSInflectionRule' (0x20088d348) [/System/Library/Frameworks/Foundation.framework]", "'UIColor' (0x2006c0520) [/System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.framework]", "'NSTextAttachment' (0x20042af98) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSShadow' (0x20042ae30) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSTextEncapsulation' (0x200897e50) [/System/Library/Frameworks/CoreText.framework]", "'NSTextAlternatives' (0x20042af70) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSFont' (0x20042a9f8) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSAttributedString' (0x2003f3838) [/System/Library/Frameworks/Foundation.framework]", "'NSData' (0x2003ed528) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSURL' (0x2003ed938) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSAdaptiveImageGlyph' (0x2008ae538) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSNumber' (0x2003f4238) [/System/Library/Frameworks/Foundation.framework]", "'NSParagraphStyle' (0x20042ad40) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSDictionary' (0x2003ed5a0) [/System/Library/Frameworks/CoreFoundation.framework]", "'UIFont' (0x20042c668) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSColor' (0x200412350) [/System/Library/Frameworks/AppKit.framework]", "'NSGlyphInfo' (0x20042aa98) [/System/Library/PrivateFrameworks/UIFoundation.framework]", "'NSArray' (0x2003ed460) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSPresentationIntent' (0x20088da28) [/System/Library/Frameworks/Foundation.framework]" )} ``` ## Changelog: [IOS] [FIXED] - Fixes TextInput crashes when any text is entered while running as iOS app on apple silicon mac Pull Request resolved: https://github.com/facebook/react-native/pull/49320 Test Plan: Run RNTester on apple silicon mac, and entered some text in textinput, no crash occured. Also, verified that the caret was not jumping around, thus preserving the original fix. https://github.com/user-attachments/assets/6304f6e7-c663-4351-ace8-ab1842ee545f Reviewed By: javache Differential Revision: D69981500 Pulled By: cipolleschi fbshipit-source-id: 2af9b280e42f621446efda9b101af50525e8fef7 --- .../TextInput/RCTTextInputComponentView.mm | 8 +--- .../RCTAttributedTextUtils.h | 27 ++++++++++-- .../RCTAttributedTextUtils.mm | 44 +------------------ .../textlayoutmanager/RCTTextLayoutManager.mm | 9 ++-- 4 files changed, 30 insertions(+), 58 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 1a17730e4ad..bd4c1e95e19 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -99,11 +99,7 @@ static NSSet *returnKeyTypesSet; NSMutableDictionary *defaultAttributes = [_backedTextInputView.defaultTextAttributes mutableCopy]; -#if !TARGET_OS_MACCATALYST - RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new]; - eventEmitterWrapper.eventEmitter = _eventEmitter; - defaultAttributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper; -#endif + defaultAttributes[RCTAttributedStringEventEmitterKey] = RCTWrapEventEmitter(_eventEmitter); _backedTextInputView.defaultTextAttributes = defaultAttributes; } @@ -263,10 +259,8 @@ static NSSet *returnKeyTypesSet; if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) { NSMutableDictionary *defaultAttributes = RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier())); -#if !TARGET_OS_MACCATALYST defaultAttributes[RCTAttributedStringEventEmitterKey] = _backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey]; -#endif _backedTextInputView.defaultTextAttributes = defaultAttributes; } diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h index 3427663d53d..908cfc0b612 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h @@ -52,8 +52,29 @@ BOOL RCTIsAttributedStringEffectivelySame( NSDictionary *insensitiveAttributes, const facebook::react::TextAttributes &baseTextAttributes); -@interface RCTWeakEventEmitterWrapper : NSObject -@property (nonatomic, assign) facebook::react::SharedEventEmitter eventEmitter; -@end +static inline NSData *RCTWrapEventEmitter(const facebook::react::SharedEventEmitter &eventEmitter) +{ + auto eventEmitterPtr = new std::weak_ptr(eventEmitter); + return [[NSData alloc] initWithBytesNoCopy:eventEmitterPtr + length:sizeof(eventEmitterPtr) + deallocator:^(void *ptrToDelete, NSUInteger) { + delete (std::weak_ptr *)ptrToDelete; + }]; +} + +static inline facebook::react::SharedEventEmitter RCTUnwrapEventEmitter(NSData *data) +{ + if (data.length == 0) { + return nullptr; + } + + auto weakPtr = dynamic_cast *>( + (std::weak_ptr *)data.bytes); + if (weakPtr) { + return weakPtr->lock(); + } + + return nullptr; +} NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm index f2dde69b83f..0d606352ec8 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm @@ -16,45 +16,6 @@ using namespace facebook::react; -@implementation RCTWeakEventEmitterWrapper { - std::weak_ptr _weakEventEmitter; -} - -- (void)setEventEmitter:(SharedEventEmitter)eventEmitter -{ - _weakEventEmitter = eventEmitter; -} - -- (SharedEventEmitter)eventEmitter -{ - return _weakEventEmitter.lock(); -} - -- (void)dealloc -{ - _weakEventEmitter.reset(); -} - -- (BOOL)isEqual:(id)object -{ - // We consider the underlying EventEmitter as the identity - if (![object isKindOfClass:[self class]]) { - return NO; - } - - auto thisEventEmitter = [self eventEmitter]; - auto otherEventEmitter = [((RCTWeakEventEmitterWrapper *)object) eventEmitter]; - return thisEventEmitter == otherEventEmitter; -} - -- (NSUInteger)hash -{ - // We consider the underlying EventEmitter as the identity - return (NSUInteger)_weakEventEmitter.lock().get(); -} - -@end - inline static UIFontWeight RCTUIFontWeightFromInteger(NSInteger fontWeight) { assert(fontWeight > 50); @@ -405,10 +366,8 @@ static NSMutableAttributedString *RCTNSAttributedStringFragmentWithAttributesFro { auto nsAttributedStringFragment = RCTNSAttributedStringFragmentFromFragment(fragment, placeholderImage); -#if !TARGET_OS_MACCATALYST if (fragment.parentShadowView.componentHandle) { - RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new]; - eventEmitterWrapper.eventEmitter = fragment.parentShadowView.eventEmitter; + auto eventEmitterWrapper = RCTWrapEventEmitter(fragment.parentShadowView.eventEmitter); NSDictionary *additionalTextAttributes = @{RCTAttributedStringEventEmitterKey : eventEmitterWrapper}; @@ -416,7 +375,6 @@ static NSMutableAttributedString *RCTNSAttributedStringFragmentWithAttributesFro [nsAttributedStringFragment addAttributes:additionalTextAttributes range:NSMakeRange(0, nsAttributedStringFragment.length)]; } -#endif return nsAttributedStringFragment; } diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index 8b00c5f8e2b..78291bf4a4a 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -280,11 +280,10 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi // after (fraction == 1.0) the last character, then the attribute is valid. if (textStorage.length > 0 && (fraction > 0 || characterIndex > 0) && (fraction < 1 || characterIndex < textStorage.length - 1)) { - RCTWeakEventEmitterWrapper *eventEmitterWrapper = - (RCTWeakEventEmitterWrapper *)[textStorage attribute:RCTAttributedStringEventEmitterKey - atIndex:characterIndex - effectiveRange:NULL]; - return eventEmitterWrapper.eventEmitter; + NSData *eventEmitterWrapper = (NSData *)[textStorage attribute:RCTAttributedStringEventEmitterKey + atIndex:characterIndex + effectiveRange:NULL]; + return RCTUnwrapEventEmitter(eventEmitterWrapper); } return nil;