From 9b6db1f482e271ee75bc1bc7dcaff7e212fb385a Mon Sep 17 00:00:00 2001 From: John Knox Date: Thu, 14 Feb 2019 06:21:28 -0800 Subject: [PATCH] Respond on exceptions thrown when on main thread Summary: Changes FlipperPerformBlockOnMainThread to take a responder, and respond with an error if an exception is caught. Reviewed By: passy Differential Revision: D14066982 fbshipit-source-id: 70135bf58171684bcd013c66d9deec366aed36f5 --- iOS/FlipperKit/FlipperPlugin.h | 14 ++--- iOS/FlipperKit/FlipperUtil.m | 21 ++++++- iOS/FlipperKitTests/FlipperUtilTests.mm | 62 +++++++++++++++++++ .../FlipperKitLayoutPlugin.mm | 16 ++--- 4 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 iOS/FlipperKitTests/FlipperUtilTests.mm diff --git a/iOS/FlipperKit/FlipperPlugin.h b/iOS/FlipperKit/FlipperPlugin.h index 364396d4c..cffcb487e 100644 --- a/iOS/FlipperKit/FlipperPlugin.h +++ b/iOS/FlipperKit/FlipperPlugin.h @@ -1,16 +1,16 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the LICENSE - * file in the root directory of this source tree. +/** + * Copyright (c) Facebook, Inc. and its affiliates. * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. */ - #import #import "SKMacros.h" +#import "FlipperResponder.h" + SK_EXTERN_C_BEGIN -void FlipperPerformBlockOnMainThread(void(^block)()); +void FlipperPerformBlockOnMainThread(void(^block)(), id responder); SK_EXTERN_C_END @protocol FlipperConnection; diff --git a/iOS/FlipperKit/FlipperUtil.m b/iOS/FlipperKit/FlipperUtil.m index 46e47177a..c232f20d3 100644 --- a/iOS/FlipperKit/FlipperUtil.m +++ b/iOS/FlipperKit/FlipperUtil.m @@ -7,12 +7,27 @@ */ #import #import "FlipperPlugin.h" +#import "FlipperResponder.h" -void FlipperPerformBlockOnMainThread(void(^block)()) +void FlipperPerformBlockOnMainThread(void(^block)(), id responder) { if ([NSThread isMainThread]) { - block(); + @try { + block(); + } @catch (NSException *e) { + [responder error:@{@"name": e.name, @"message": e.reason}]; + } @catch (...) { + [responder error:@{@"name": @"Unknown", @"message": @"Unknown error caught when processing operation on main thread"}]; + } } else { - dispatch_async(dispatch_get_main_queue(), block); + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + block(); + } @catch (NSException *e) { + [responder error:@{@"name": e.name, @"message": e.reason}]; + } @catch (...) { + [responder error:@{@"name": @"Unknown", @"message": @"Unknown error caught when processing operation on main thread"}]; + } + }); } } diff --git a/iOS/FlipperKitTests/FlipperUtilTests.mm b/iOS/FlipperKitTests/FlipperUtilTests.mm new file mode 100644 index 000000000..a2b5e587f --- /dev/null +++ b/iOS/FlipperKitTests/FlipperUtilTests.mm @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +#import +#if FB_SONARKIT_ENABLED + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "FlipperPlugin.h" + +@interface FlipperUtilTests : XCTestCase + +@end + +@implementation FlipperUtilTests { + FlipperResponderMock *responder; +} + + +- (void)setUp { + responder = [FlipperResponderMock new]; +} + +- (void)testPerformOnMainThreadSuccess { + FlipperPerformBlockOnMainThread(^{}, responder); + NSAssert([responder.successes count] == 0, @"No successes are output"); + NSAssert([responder.errors count] == 0, @"No errors are output"); +} + +- (void)testPerformOnMainThreadStdException { + FlipperPerformBlockOnMainThread(^{ + throw new std::exception(); + }, responder); + NSAssert([responder.successes count] == 0, @"No successes are output"); + NSAssert([responder.errors count] == 1, @"1 error is output"); +} + +- (void)testPerformOnMainThreadNSException { + FlipperPerformBlockOnMainThread(^{ + NSArray *a = [NSArray init]; + [a objectAtIndex:1]; + }, responder); + NSAssert([responder.successes count] == 0, @"No successes are output"); + NSAssert([responder.errors count] == 1, @"1 error is output"); +} + +@end +#endif diff --git a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm index c5ae6fc02..f7d35a3da 100644 --- a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm +++ b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm @@ -84,11 +84,11 @@ __weak FlipperKitLayoutPlugin *weakSelf = self; [connection receive:@"getRoot" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [weakSelf onCallGetRoot: responder]; }); + FlipperPerformBlockOnMainThread(^{ [weakSelf onCallGetRoot: responder]; }, responder); }]; [connection receive:@"getNodes" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [weakSelf onCallGetNodes: params[@"ids"] withResponder: responder]; }); + FlipperPerformBlockOnMainThread(^{ [weakSelf onCallGetNodes: params[@"ids"] withResponder: responder]; }, responder); }]; [connection receive:@"setData" withBlock:^(NSDictionary *params, id responder) { @@ -97,27 +97,27 @@ withPath: params[@"path"] toValue: params[@"value"] withConnection: connection]; - }); + }, responder); }]; [connection receive:@"setHighlighted" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [weakSelf onCallSetHighlighted: params[@"id"] withResponder: responder]; }); + FlipperPerformBlockOnMainThread(^{ [weakSelf onCallSetHighlighted: params[@"id"] withResponder: responder]; }, responder); }]; [connection receive:@"setSearchActive" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [weakSelf onCallSetSearchActive: [params[@"active"] boolValue] withConnection: connection]; }); + FlipperPerformBlockOnMainThread(^{ [weakSelf onCallSetSearchActive: [params[@"active"] boolValue] withConnection: connection]; }, responder); }]; [connection receive:@"isSearchActive" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [weakSelf onCallIsSearchActiveWithConnection: responder]; }); + FlipperPerformBlockOnMainThread(^{ [weakSelf onCallIsSearchActiveWithConnection: responder]; }, responder); }]; [connection receive:@"isConsoleEnabled" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [responder success: @{@"isEnabled": @NO}];}); + FlipperPerformBlockOnMainThread(^{ [responder success: @{@"isEnabled": @NO}];}, responder); }]; [connection receive:@"getSearchResults" withBlock:^(NSDictionary *params, id responder) { - FlipperPerformBlockOnMainThread(^{ [weakSelf onCallGetSearchResults: params[@"query"] withResponder: responder]; }); + FlipperPerformBlockOnMainThread(^{ [weakSelf onCallGetSearchResults: params[@"query"] withResponder: responder]; }, responder); }]; }