Skip to content

Commit

Permalink
[ReactNative] Fix crash when reload during profile (attempt facebook#2)
Browse files Browse the repository at this point in the history
Summary:
Fixes facebook#1642

When reloading during profiling, the profile wouldn't unhook from the instance
being deallocated.
  • Loading branch information
tadeuzagallo committed Jul 22, 2015
1 parent 8dd1256 commit 49b5580
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 19 deletions.
20 changes: 16 additions & 4 deletions React/Base/RCTBatchedBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge
*/
[self registerModules];

/**
* If currently profiling, hook into the current instance
*/
if (RCTProfileIsProfiling()) {
RCTProfileHookModules(self);
}

/**
* Start the application script
*/
Expand Down Expand Up @@ -361,18 +368,23 @@ - (void)invalidate
}
moduleData.queue = nil;
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
[_jsDisplayLink invalidate];
_jsDisplayLink = nil;

[_javaScriptExecutor invalidate];
_javaScriptExecutor = nil;
}];

_modules = nil;
_modulesByName = nil;
_frameUpdateObservers = nil;
if (RCTProfileIsProfiling()) {
RCTProfileUnhookModules(self);
}
_modules = nil;
_modulesByName = nil;
_frameUpdateObservers = nil;

}];
});
}

Expand Down
13 changes: 13 additions & 0 deletions React/Base/RCTProfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ RCT_EXTERN void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *
RCTProfileEndEvent([NSString stringWithFormat:@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)], category, arguments); \
}

/**
* Hook into a bridge instance to log all bridge module's method calls
*/
RCT_EXTERN void RCTProfileHookModules(RCTBridge *);

/**
* Unhook from a given bridge instance's modules
*/
RCT_EXTERN void RCTProfileUnhookModules(RCTBridge *);

#else

#define RCTProfileBeginFlowEvent()
Expand All @@ -125,4 +135,7 @@ RCT_EXTERN void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *

#define RCTProfileBlock(block, ...) block

#define RCTProfileHookModules(...)
#define RCTProfileUnhookModules(...)

#endif
30 changes: 16 additions & 14 deletions React/Base/RCTProfile.m
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ static void RCTProfileForwardInvocation(NSObject *self, __unused SEL cmd, NSInvo
RCTProfileBeginEvent();
[invocation invoke];
RCTProfileEndEvent(name, @"objc_call,modules,auto", nil);
} else if ([self respondsToSelector:invocation.selector]) {
[invocation invoke];
} else {
// Use original selector to don't change error message
[self doesNotRecognizeSelector:invocation.selector];
Expand All @@ -144,14 +146,17 @@ static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
return imp;
}

static void RCTProfileHookModules(RCTBridge *);
static void RCTProfileHookModules(RCTBridge *bridge)
void RCTProfileHookModules(RCTBridge *bridge)
{
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
[moduleData dispatchBlock:^{
Class moduleClass = moduleData.cls;
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);

if (!proxyClass) {
return;
}

unsigned int methodCount;
Method *methods = class_copyMethodList(moduleClass, &methodCount);
for (NSUInteger i = 0; i < methodCount; i++) {
Expand Down Expand Up @@ -185,20 +190,17 @@ static void RCTProfileHookModules(RCTBridge *bridge)
}
}

void RCTProfileUnhookModules(RCTBridge *);
void RCTProfileUnhookModules(RCTBridge *bridge)
{
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
[moduleData dispatchBlock:^{
RCTProfileLock(
Class proxyClass = object_getClass(moduleData.instance);
if (moduleData.cls != proxyClass) {
object_setClass(moduleData.instance, moduleData.cls);
objc_disposeClassPair(proxyClass);
}
);
}];
};
RCTProfileLock(
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
Class proxyClass = object_getClass(moduleData.instance);
if (moduleData.cls != proxyClass) {
object_setClass(moduleData.instance, moduleData.cls);
objc_disposeClassPair(proxyClass);
}
};
);
}


Expand Down
2 changes: 1 addition & 1 deletion React/Views/RCTNavigator.m
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ - (void)dispatchFakeScrollEvent
*/
- (UIView *)reactSuperview
{
RCTAssert(self.superview != nil, @"put reactNavSuperviewLink back");
RCTAssert(!_bridge.isValid || self.superview != nil, @"put reactNavSuperviewLink back");
return self.superview ? self.superview : self.reactNavSuperviewLink;
}

Expand Down

0 comments on commit 49b5580

Please sign in to comment.