Fix crash when a test fails inside RealmReactTests

This also appears to fix crashes when running ReactTests with Chrome debugging enabled!

The crash when tests failed was due to JavaScriptCore trying to create a backtrace with the native call stack.
This commit is contained in:
Scott Kyle 2015-10-15 16:18:58 -07:00
parent 7f5902b021
commit 5d59431860
3 changed files with 40 additions and 23 deletions

View File

@ -17,11 +17,10 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@import Foundation; @import Foundation;
@import JavaScriptCore;
extern JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor);
@interface RealmReact : NSObject @interface RealmReact : NSObject
@property (nonatomic, readonly) id executor;
+ (id)executor;
@end @end

View File

@ -21,7 +21,6 @@
@import GCDWebServers; @import GCDWebServers;
@import RealmJS; @import RealmJS;
@import JavaScriptCore;
@import ObjectiveC; @import ObjectiveC;
@import Darwin; @import Darwin;
@ -30,11 +29,16 @@
- (JSGlobalContextRef)ctx; - (JSGlobalContextRef)ctx;
@end @end
JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor) {
Ivar contextIvar = class_getInstanceVariable([executor class], "_context");
id rctJSContext = contextIvar ? object_getIvar(executor, contextIvar) : nil;
return [rctJSContext ctx];
}
@interface RealmReact () <RCTBridgeModule> @interface RealmReact () <RCTBridgeModule>
@end @end
static id s_executor;
@implementation RealmReact @implementation RealmReact
@synthesize bridge = _bridge; @synthesize bridge = _bridge;
@ -54,16 +58,12 @@ static id s_executor;
return @"Realm"; return @"Realm";
} }
+ (id)executor {
return s_executor;
}
- (void)setBridge:(RCTBridge *)bridge { - (void)setBridge:(RCTBridge *)bridge {
_bridge = bridge; _bridge = bridge;
Ivar executorIvar = class_getInstanceVariable([bridge class], "_javaScriptExecutor"); Ivar executorIvar = class_getInstanceVariable([bridge class], "_javaScriptExecutor");
s_executor = object_getIvar(bridge, executorIvar); id executor = object_getIvar(bridge, executorIvar);
Ivar contextIvar = class_getInstanceVariable([s_executor class], "_context"); Ivar contextIvar = class_getInstanceVariable([executor class], "_context");
// The executor could be a RCTWebSocketExecutor, in which case it won't have a JS context. // The executor could be a RCTWebSocketExecutor, in which case it won't have a JS context.
if (!contextIvar) { if (!contextIvar) {
@ -96,8 +96,8 @@ static id s_executor;
return; return;
} }
[s_executor executeBlockOnJavaScriptQueue:^{ [executor executeBlockOnJavaScriptQueue:^{
id rctJSContext = object_getIvar(s_executor, contextIvar); id rctJSContext = object_getIvar(executor, contextIvar);
JSGlobalContextRef ctx; JSGlobalContextRef ctx;
if (rctJSContext) { if (rctJSContext) {
@ -108,7 +108,7 @@ static id s_executor;
if (RCTJavaScriptContext) { if (RCTJavaScriptContext) {
ctx = JSGlobalContextCreate(NULL); ctx = JSGlobalContextCreate(NULL);
object_setIvar(s_executor, contextIvar, [[RCTJavaScriptContext alloc] initWithJSContext:ctx]); object_setIvar(executor, contextIvar, [[RCTJavaScriptContext alloc] initWithJSContext:ctx]);
} }
else { else {
NSLog(@"Failed to load RCTJavaScriptContext class"); NSLog(@"Failed to load RCTJavaScriptContext class");

View File

@ -22,13 +22,31 @@
@import RealmReact; @import RealmReact;
extern void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack);
static id<RCTJavaScriptExecutor> s_currentJavaScriptExecutor;
@interface RealmReactTests : RealmJSTests @interface RealmReactTests : RealmJSTests
@end @end
@implementation RealmReactTests @implementation RealmReactTests
+ (XCTestSuite *)defaultTestSuite { + (XCTestSuite *)defaultTestSuite {
[self waitForNotification:RCTJavaScriptDidLoadNotification]; NSNotification *notification = [self waitForNotification:RCTJavaScriptDidLoadNotification];
RCTBridge *bridge = notification.userInfo[@"bridge"];
if (!bridge) {
NSLog(@"No RCTBridge provided by RCTJavaScriptDidLoadNotification");
exit(1);
}
s_currentJavaScriptExecutor = [bridge valueForKey:@"javaScriptExecutor"];
// FIXME: Remove this nonsense once the crashes go away when a test fails!
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(s_currentJavaScriptExecutor);
if (ctx) {
JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(ctx, false);
}
NSError *error; NSError *error;
NSDictionary *testCaseNames = [self invokeMethod:@"getTestNames" inModule:@"index" error:&error]; NSDictionary *testCaseNames = [self invokeMethod:@"getTestNames" inModule:@"index" error:&error];
@ -47,34 +65,34 @@
return suite; return suite;
} }
+ (void)waitForNotification:(NSString *)notificationName { + (NSNotification *)waitForNotification:(NSString *)notificationName {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
__block BOOL received = NO; __block NSNotification *notification;
id token = [nc addObserverForName:notificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { id token = [nc addObserverForName:notificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
received = YES; notification = note;
}]; }];
while (!received) { while (!notification) {
@autoreleasepool { @autoreleasepool {
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} }
} }
[nc removeObserver:token]; [nc removeObserver:token];
return notification;
} }
+ (id)invokeMethod:(NSString *)method inModule:(NSString *)module error:(NSError * __strong *)outError { + (id)invokeMethod:(NSString *)method inModule:(NSString *)module error:(NSError * __strong *)outError {
module = [NSString stringWithFormat:@"realm-tests/%@.js", module]; module = [NSString stringWithFormat:@"realm-tests/%@.js", module];
id<RCTJavaScriptExecutor> executor = [RealmReact executor];
dispatch_group_t group = dispatch_group_create(); dispatch_group_t group = dispatch_group_create();
__block id result; __block id result;
dispatch_group_enter(group); dispatch_group_enter(group);
[executor executeJSCall:module method:method arguments:@[] callback:^(id json, NSError *error) { [s_currentJavaScriptExecutor executeJSCall:module method:method arguments:@[] callback:^(id json, NSError *error) {
result = json; result = json;
if (error && outError) { if (error && outError) {