[ReactNative] Move module info from bridge to RCTModuleData
Summary: @public The info about bridge modules (such as id, name, queue, methods...) was spread across arrays & dictionaries on the bridge, move it into a specific class. It also removes a lot of information that was statically cached, and now have the same lifecycle of the bridge. Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own files, for organization sake. NOTE: This diff seems huge, but most of it was just moving code :) Test Plan: Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything looks fine.
This commit is contained in:
parent
c157051da7
commit
336e18d20b
|
@ -0,0 +1,785 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "RCTAssert.h"
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
#import "RCTContextExecutor.h"
|
||||||
|
#import "RCTFrameUpdate.h"
|
||||||
|
#import "RCTJavaScriptLoader.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTModuleData.h"
|
||||||
|
#import "RCTModuleMethod.h"
|
||||||
|
#import "RCTPerformanceLogger.h"
|
||||||
|
#import "RCTPerfStats.h"
|
||||||
|
#import "RCTProfile.h"
|
||||||
|
#import "RCTRedBox.h"
|
||||||
|
#import "RCTSourceCode.h"
|
||||||
|
#import "RCTSparseArray.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
|
#define RCTAssertJSThread() \
|
||||||
|
RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTContextExecutor"] || \
|
||||||
|
[[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \
|
||||||
|
@"This method must be called on JS thread")
|
||||||
|
|
||||||
|
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
||||||
|
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be kept in sync with `MessageQueue.js`.
|
||||||
|
*/
|
||||||
|
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||||
|
RCTBridgeFieldRequestModuleIDs = 0,
|
||||||
|
RCTBridgeFieldMethodIDs,
|
||||||
|
RCTBridgeFieldParamss,
|
||||||
|
};
|
||||||
|
|
||||||
|
RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
|
static id<RCTJavaScriptExecutor> RCTLatestExecutor = nil;
|
||||||
|
id<RCTJavaScriptExecutor> RCTGetLatestExecutor(void);
|
||||||
|
id<RCTJavaScriptExecutor> RCTGetLatestExecutor(void)
|
||||||
|
{
|
||||||
|
return RCTLatestExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface RCTBatchedBridge : RCTBridge
|
||||||
|
|
||||||
|
@property (nonatomic, weak) RCTBridge *parentBridge;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTBatchedBridge
|
||||||
|
{
|
||||||
|
BOOL _loading;
|
||||||
|
__weak id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||||
|
NSMutableArray *_modules;
|
||||||
|
NSDictionary *_modulesByName;
|
||||||
|
CADisplayLink *_mainDisplayLink;
|
||||||
|
CADisplayLink *_jsDisplayLink;
|
||||||
|
NSMutableSet *_frameUpdateObservers;
|
||||||
|
NSMutableArray *_scheduledCalls;
|
||||||
|
RCTSparseArray *_scheduledCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@synthesize valid = _valid;
|
||||||
|
|
||||||
|
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
RCTAssertParam(bridge);
|
||||||
|
|
||||||
|
if ((self = [super initWithBundleURL:bridge.bundleURL
|
||||||
|
moduleProvider:bridge.moduleProvider
|
||||||
|
launchOptions:bridge.launchOptions])) {
|
||||||
|
|
||||||
|
_parentBridge = bridge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Initial State
|
||||||
|
*/
|
||||||
|
_valid = YES;
|
||||||
|
_loading = YES;
|
||||||
|
_modules = [[NSMutableArray alloc] init];
|
||||||
|
_frameUpdateObservers = [[NSMutableSet alloc] init];
|
||||||
|
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||||
|
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||||
|
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
|
||||||
|
|
||||||
|
if (RCT_DEV) {
|
||||||
|
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
||||||
|
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and register bridge modules *before* adding the display link
|
||||||
|
* so we don't have threading issues
|
||||||
|
*/
|
||||||
|
[self registerModules];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the application script
|
||||||
|
*/
|
||||||
|
[self initJS];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
||||||
|
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
||||||
|
launchOptions:(__unused NSDictionary *)launchOptions)
|
||||||
|
|
||||||
|
- (void)setUp {}
|
||||||
|
- (void)bindKeys {}
|
||||||
|
|
||||||
|
- (void)reload
|
||||||
|
{
|
||||||
|
[_parentBridge reload];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (Class)executorClass
|
||||||
|
{
|
||||||
|
return _parentBridge.executorClass ?: [RCTContextExecutor class];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setExecutorClass:(Class)executorClass
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
_parentBridge.executorClass = executorClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isLoading
|
||||||
|
{
|
||||||
|
return _loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return _valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)registerModules
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
// Register passed-in module instances
|
||||||
|
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
||||||
|
for (id<RCTBridgeModule> module in self.moduleProvider ? self.moduleProvider() : nil) {
|
||||||
|
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate modules
|
||||||
|
_modules = [[NSMutableArray alloc] init];
|
||||||
|
NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy];
|
||||||
|
for (Class moduleClass in RCTGetModuleClasses()) {
|
||||||
|
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
|
||||||
|
|
||||||
|
// Check if module instance has already been registered for this name
|
||||||
|
id<RCTBridgeModule> module = modulesByName[moduleName];
|
||||||
|
|
||||||
|
if (module) {
|
||||||
|
// Preregistered instances takes precedence, no questions asked
|
||||||
|
if (!preregisteredModules[moduleName]) {
|
||||||
|
// It's OK to have a name collision as long as the second instance is nil
|
||||||
|
RCTAssert([[moduleClass alloc] init] == nil,
|
||||||
|
@"Attempted to register RCTBridgeModule class %@ for the name "
|
||||||
|
"'%@', but name was already registered by class %@", moduleClass,
|
||||||
|
moduleName, [modulesByName[moduleName] class]);
|
||||||
|
}
|
||||||
|
if ([module class] != moduleClass) {
|
||||||
|
RCTLogInfo(@"RCTBridgeModule of class %@ with name '%@' was encountered "
|
||||||
|
"in the project, but name was already registered by class %@."
|
||||||
|
"That's fine if it's intentional - just letting you know.",
|
||||||
|
moduleClass, moduleName, [modulesByName[moduleName] class]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Module name hasn't been used before, so go ahead and instantiate
|
||||||
|
module = [[moduleClass alloc] init];
|
||||||
|
}
|
||||||
|
if (module) {
|
||||||
|
modulesByName[moduleName] = module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store modules
|
||||||
|
_modulesByName = [modulesByName copy];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The executor is a bridge module, wait for it to be created and set it before
|
||||||
|
* any other module has access to the bridge
|
||||||
|
*/
|
||||||
|
_javaScriptExecutor = _modulesByName[RCTBridgeModuleNameForClass(self.executorClass)];
|
||||||
|
RCTLatestExecutor = _javaScriptExecutor;
|
||||||
|
RCTSetExecutorID(_javaScriptExecutor);
|
||||||
|
|
||||||
|
[_javaScriptExecutor setUp];
|
||||||
|
|
||||||
|
// Set bridge
|
||||||
|
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
|
||||||
|
if ([module respondsToSelector:@selector(setBridge:)]) {
|
||||||
|
module.bridge = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTModuleData *moduleData = [[RCTModuleData alloc] initWithExecutor:_javaScriptExecutor
|
||||||
|
uid:@(_modules.count)
|
||||||
|
instance:module];
|
||||||
|
[_modules addObject:moduleData];
|
||||||
|
|
||||||
|
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
||||||
|
[_frameUpdateObservers addObject:moduleData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initJS
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
// Inject module data into JS context
|
||||||
|
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||||
|
for (RCTModuleData *moduleData in _modules) {
|
||||||
|
config[moduleData.name] = moduleData.config;
|
||||||
|
}
|
||||||
|
NSString *configJSON = RCTJSONStringify(@{
|
||||||
|
@"remoteModuleConfig": config,
|
||||||
|
}, NULL);
|
||||||
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||||
|
[_javaScriptExecutor injectJSONText:configJSON
|
||||||
|
asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:
|
||||||
|
^(__unused id err) {
|
||||||
|
dispatch_semaphore_signal(semaphore);
|
||||||
|
}];
|
||||||
|
|
||||||
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
|
||||||
|
|
||||||
|
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||||
|
if (_javaScriptExecutor == nil) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HACK (tadeu): If it failed to connect to the debugger, set loading to NO
|
||||||
|
* so we can attempt to reload again.
|
||||||
|
*/
|
||||||
|
_loading = NO;
|
||||||
|
|
||||||
|
} else if (!bundleURL) {
|
||||||
|
|
||||||
|
// Allow testing without a script
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
_loading = NO;
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||||
|
object:_parentBridge
|
||||||
|
userInfo:@{ @"bridge": self }];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
RCTPerformanceLoggerStart(RCTPLScriptDownload);
|
||||||
|
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
||||||
|
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
||||||
|
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
||||||
|
RCTProfileEndEvent(@"JavaScript dowload", @"init,download", @[]);
|
||||||
|
|
||||||
|
_loading = NO;
|
||||||
|
if (!self.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[RCTRedBox sharedInstance] dismiss];
|
||||||
|
|
||||||
|
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||||
|
sourceCodeModule.scriptURL = bundleURL;
|
||||||
|
sourceCodeModule.scriptText = script;
|
||||||
|
if (error) {
|
||||||
|
|
||||||
|
NSArray *stack = [error userInfo][@"stack"];
|
||||||
|
if (stack) {
|
||||||
|
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||||
|
withStack:stack];
|
||||||
|
} else {
|
||||||
|
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||||
|
withDetails:[error localizedFailureReason]];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||||
|
object:_parentBridge
|
||||||
|
userInfo:userInfo];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
||||||
|
|
||||||
|
if (!loadError) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the display link to start sending js calls after everything
|
||||||
|
* is setup
|
||||||
|
*/
|
||||||
|
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
|
||||||
|
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||||
|
object:_parentBridge
|
||||||
|
userInfo:@{ @"bridge": self }];
|
||||||
|
} else {
|
||||||
|
[[RCTRedBox sharedInstance] showErrorMessage:[loadError localizedDescription]
|
||||||
|
withDetails:[loadError localizedFailureReason]];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)modules
|
||||||
|
{
|
||||||
|
RCTAssert(!self.isValid || _modulesByName != nil, @"Bridge modules have not yet been initialized. "
|
||||||
|
"You may be trying to access a module too early in the startup procedure.");
|
||||||
|
|
||||||
|
return _modulesByName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RCTInvalidating
|
||||||
|
|
||||||
|
- (void)invalidate
|
||||||
|
{
|
||||||
|
if (!self.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
_valid = NO;
|
||||||
|
if (RCTLatestExecutor == _javaScriptExecutor) {
|
||||||
|
RCTLatestExecutor = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (^mainThreadInvalidate)(void) = ^{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
[_mainDisplayLink invalidate];
|
||||||
|
_mainDisplayLink = nil;
|
||||||
|
|
||||||
|
// Invalidate modules
|
||||||
|
dispatch_group_t group = dispatch_group_create();
|
||||||
|
for (RCTModuleData *moduleData in _modules) {
|
||||||
|
if ([moduleData.instance respondsToSelector:@selector(invalidate)]) {
|
||||||
|
[moduleData dispatchBlock:^{
|
||||||
|
[(id<RCTInvalidating>)moduleData.instance invalidate];
|
||||||
|
} dispatchGroup:group];
|
||||||
|
}
|
||||||
|
moduleData.queue = nil;
|
||||||
|
}
|
||||||
|
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||||
|
_modules = nil;
|
||||||
|
_modulesByName = nil;
|
||||||
|
_frameUpdateObservers = nil;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!_javaScriptExecutor) {
|
||||||
|
|
||||||
|
// No JS thread running
|
||||||
|
mainThreadInvalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS Thread deallocations
|
||||||
|
*/
|
||||||
|
[_javaScriptExecutor invalidate];
|
||||||
|
_javaScriptExecutor = nil;
|
||||||
|
|
||||||
|
[_jsDisplayLink invalidate];
|
||||||
|
_jsDisplayLink = nil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Thread deallocations
|
||||||
|
*/
|
||||||
|
dispatch_async(dispatch_get_main_queue(), mainThreadInvalidate);
|
||||||
|
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RCTBridge methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public. Can be invoked from any thread.
|
||||||
|
*/
|
||||||
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
||||||
|
{
|
||||||
|
NSArray *ids = [moduleDotMethod componentsSeparatedByString:@"."];
|
||||||
|
|
||||||
|
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"callFunctionReturnFlushedQueue"
|
||||||
|
arguments:@[ids[0], ids[1], args ?: @[]]
|
||||||
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private hack to support `setTimeout(fn, 0)`
|
||||||
|
*/
|
||||||
|
- (void)_immediatelyCallTimer:(NSNumber *)timer
|
||||||
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"callFunctionReturnFlushedQueue"
|
||||||
|
arguments:@[@"JSTimersExecution", @"callTimers", @[@[timer]]]
|
||||||
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||||
|
};
|
||||||
|
|
||||||
|
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||||
|
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
||||||
|
} else {
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||||
|
{
|
||||||
|
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
|
||||||
|
|
||||||
|
RCTProfileBeginFlowEvent();
|
||||||
|
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
|
||||||
|
RCTProfileEndFlowEvent();
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
|
if (scriptLoadError) {
|
||||||
|
onComplete(scriptLoadError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
NSNumber *context = RCTGetExecutorID(_javaScriptExecutor);
|
||||||
|
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
|
||||||
|
method:@"flushedQueue"
|
||||||
|
arguments:@[]
|
||||||
|
context:context
|
||||||
|
callback:^(id json, NSError *error) {
|
||||||
|
RCTProfileEndEvent(@"FetchApplicationScriptCallbacks", @"js_call,init", @{
|
||||||
|
@"json": RCTNullIfNil(json),
|
||||||
|
@"error": RCTNullIfNil(error),
|
||||||
|
});
|
||||||
|
|
||||||
|
[self _handleBuffer:json context:context];
|
||||||
|
|
||||||
|
onComplete(error);
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Payload Generation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Completely remove `context` - no longer needed
|
||||||
|
*/
|
||||||
|
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||||
|
{
|
||||||
|
[self _invokeAndProcessModule:module
|
||||||
|
method:method
|
||||||
|
arguments:args
|
||||||
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by enqueueJSCall from any thread, or from _immediatelyCallTimer,
|
||||||
|
* on the JS thread, but only in non-batched mode.
|
||||||
|
*/
|
||||||
|
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* AnyThread
|
||||||
|
*/
|
||||||
|
|
||||||
|
RCTProfileBeginFlowEvent();
|
||||||
|
|
||||||
|
__weak RCTBatchedBridge *weakSelf = self;
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
|
RCTProfileEndFlowEvent();
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
|
RCTBatchedBridge *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RCT_IF_DEV(NSNumber *callID = _RCTProfileBeginFlowEvent();)
|
||||||
|
id call = @{
|
||||||
|
@"js_args": @{
|
||||||
|
@"module": module,
|
||||||
|
@"method": method,
|
||||||
|
@"args": args,
|
||||||
|
},
|
||||||
|
@"context": context ?: @0,
|
||||||
|
RCT_IF_DEV(@"call_id": callID,)
|
||||||
|
};
|
||||||
|
if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) {
|
||||||
|
strongSelf->_scheduledCallbacks[args[0]] = call;
|
||||||
|
} else {
|
||||||
|
[strongSelf->_scheduledCalls addObject:call];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTProfileEndEvent(@"enqueue_call", @"objc_call", call);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||||
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
||||||
|
|
||||||
|
RCTJavaScriptCallback processResponse = ^(id json, __unused NSError *error) {
|
||||||
|
if (!self.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
||||||
|
[self _handleBuffer:json context:context];
|
||||||
|
};
|
||||||
|
|
||||||
|
[_javaScriptExecutor executeJSCall:module
|
||||||
|
method:method
|
||||||
|
arguments:args
|
||||||
|
context:context
|
||||||
|
callback:processResponse];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Payload Processing
|
||||||
|
|
||||||
|
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context
|
||||||
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
|
if (buffer == nil || buffer == (id)kCFNull) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray *requestsArray = [RCTConvert NSArray:buffer];
|
||||||
|
|
||||||
|
#if RCT_DEBUG
|
||||||
|
|
||||||
|
if (![buffer isKindOfClass:[NSArray class]]) {
|
||||||
|
RCTLogError(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) {
|
||||||
|
id field = [requestsArray objectAtIndex:fieldIndex];
|
||||||
|
if (![field isKindOfClass:[NSArray class]]) {
|
||||||
|
RCTLogError(@"Field at index %zd in buffer must be an instance of NSArray, got %@", fieldIndex, NSStringFromClass([field class]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||||
|
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||||
|
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||||
|
|
||||||
|
NSUInteger numRequests = [moduleIDs count];
|
||||||
|
|
||||||
|
if (RCT_DEBUG && (numRequests != methodIDs.count || numRequests != paramsArrays.count)) {
|
||||||
|
RCTLogError(@"Invalid data message - all must be length: %zd", numRequests);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMapTable *buckets = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:_modules.count];
|
||||||
|
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||||
|
RCTModuleData *moduleData = _modules[[moduleIDs[i] integerValue]];
|
||||||
|
NSMutableOrderedSet *set = [buckets objectForKey:moduleData];
|
||||||
|
if (!set) {
|
||||||
|
set = [[NSMutableOrderedSet alloc] init];
|
||||||
|
[buckets setObject:set forKey:moduleData];
|
||||||
|
}
|
||||||
|
[set addObject:@(i)];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RCTModuleData *moduleData in buckets) {
|
||||||
|
RCTProfileBeginFlowEvent();
|
||||||
|
|
||||||
|
[moduleData dispatchBlock:^{
|
||||||
|
RCTProfileEndFlowEvent();
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
|
NSOrderedSet *calls = [buckets objectForKey:moduleData];
|
||||||
|
@autoreleasepool {
|
||||||
|
for (NSNumber *indexObj in calls) {
|
||||||
|
NSUInteger index = indexObj.unsignedIntegerValue;
|
||||||
|
[self _handleRequestNumber:index
|
||||||
|
moduleID:[moduleIDs[index] integerValue]
|
||||||
|
methodID:[methodIDs[index] integerValue]
|
||||||
|
params:paramsArrays[index]
|
||||||
|
context:context];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RCTProfileEndEvent(RCTCurrentThreadName(), @"objc_call,dispatch_async", @{ @"calls": @(calls.count) });
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case?
|
||||||
|
for (RCTModuleData *moduleData in _modules) {
|
||||||
|
if ([moduleData.instance respondsToSelector:@selector(batchDidComplete)]) {
|
||||||
|
[moduleData dispatchBlock:^{
|
||||||
|
[moduleData.instance batchDidComplete];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
||||||
|
moduleID:(NSUInteger)moduleID
|
||||||
|
methodID:(NSUInteger)methodID
|
||||||
|
params:(NSArray *)params
|
||||||
|
context:(NSNumber *)context
|
||||||
|
{
|
||||||
|
if (!self.isValid) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
||||||
|
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
|
RCTModuleData *moduleData = _modules[moduleID];
|
||||||
|
if (RCT_DEBUG && !moduleData) {
|
||||||
|
RCTLogError(@"No module found for id '%zd'", moduleID);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTModuleMethod *method = moduleData.methods[methodID];
|
||||||
|
if (RCT_DEBUG && !method) {
|
||||||
|
RCTLogError(@"Unknown methodID: %zd for module: %zd (%@)", methodID, moduleID, moduleData.name);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@try {
|
||||||
|
[method invokeWithBridge:self module:moduleData.instance arguments:params context:context];
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, moduleData.name, params, exception);
|
||||||
|
if (!RCT_DEBUG && [exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) {
|
||||||
|
@throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTProfileEndEvent(@"Invoke callback", @"objc_call", @{
|
||||||
|
@"module": method.moduleClassName,
|
||||||
|
@"method": method.JSMethodName,
|
||||||
|
@"selector": NSStringFromSelector(method.selector),
|
||||||
|
@"args": RCTJSONStringify(RCTNullIfNil(params), NULL),
|
||||||
|
});
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
|
||||||
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
|
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
|
||||||
|
for (RCTModuleData *moduleData in _frameUpdateObservers) {
|
||||||
|
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
|
||||||
|
if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) {
|
||||||
|
RCT_IF_DEV(NSString *name = [NSString stringWithFormat:@"[%@ didUpdateFrame:%f]", observer, displayLink.timestamp];)
|
||||||
|
RCTProfileBeginFlowEvent();
|
||||||
|
|
||||||
|
[moduleData dispatchBlock:^{
|
||||||
|
RCTProfileEndFlowEvent();
|
||||||
|
RCTProfileBeginEvent();
|
||||||
|
[observer didUpdateFrame:frameUpdate];
|
||||||
|
RCTProfileEndEvent(name, @"objc_call,fps", nil);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls];
|
||||||
|
NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor);
|
||||||
|
calls = [calls filteredArrayUsingPredicate:
|
||||||
|
[NSPredicate predicateWithBlock:
|
||||||
|
^BOOL(NSDictionary *call, __unused NSDictionary *bindings) {
|
||||||
|
return [call[@"context"] isEqualToNumber:currentExecutorID];
|
||||||
|
}]];
|
||||||
|
|
||||||
|
RCT_IF_DEV(
|
||||||
|
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
||||||
|
|
||||||
|
for (NSDictionary *call in calls) {
|
||||||
|
_RCTProfileEndFlowEvent(call[@"call_id"]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (calls.count > 0) {
|
||||||
|
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||||
|
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||||
|
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"processBatch"
|
||||||
|
arguments:@[[calls valueForKey:@"js_args"]]
|
||||||
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self.perfStats.jsGraph tick:displayLink.timestamp];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
||||||
|
|
||||||
|
[self.perfStats.uiGraph tick:displayLink.timestamp];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startProfiling
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
|
||||||
|
RCTLogError(@"To run the profiler you must be running from the dev server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
|
RCTProfileInit(self);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stopProfiling
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
|
NSString *log = RCTProfileEnd(self);
|
||||||
|
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||||
|
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
|
||||||
|
NSURL *URL = [NSURL URLWithString:URLString];
|
||||||
|
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
|
||||||
|
URLRequest.HTTPMethod = @"POST";
|
||||||
|
[URLRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||||
|
NSURLSessionTask *task =
|
||||||
|
[[NSURLSession sharedSession] uploadTaskWithRequest:URLRequest
|
||||||
|
fromData:[log dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
|
completionHandler:
|
||||||
|
^(__unused NSData *data, __unused NSURLResponse *response, NSError *error) {
|
||||||
|
if (error) {
|
||||||
|
RCTLogError(@"%@", error.localizedDescription);
|
||||||
|
} else {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[[[UIAlertView alloc] initWithTitle:@"Profile"
|
||||||
|
message:@"The profile has been generated, check the dev server log for instructions."
|
||||||
|
delegate:nil
|
||||||
|
cancelButtonTitle:@"OK"
|
||||||
|
otherButtonTitles:nil] show];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
[task resume];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -118,6 +118,11 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
|
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The block passed in the constructor with pre-initialized modules
|
||||||
|
*/
|
||||||
|
@property (nonatomic, copy, readonly) RCTBridgeModuleProviderBlock moduleProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload the bundle and reset executor & modules. Safe to call from any thread.
|
* Reload the bundle and reset executor & modules. Safe to call from any thread.
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -78,7 +78,7 @@ extern dispatch_queue_t RCTJSThread;
|
||||||
* and the bridge will populate the methodQueue property for you automatically
|
* and the bridge will populate the methodQueue property for you automatically
|
||||||
* when it initializes the module.
|
* when it initializes the module.
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, weak, readonly) dispatch_queue_t methodQueue;
|
@property (nonatomic, strong, readonly) dispatch_queue_t methodQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Place this macro in your class implementation to automatically register
|
* Place this macro in your class implementation to automatically register
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class CADisplayLink;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface containing the information about the last screen refresh.
|
* Interface containing the information about the last screen refresh.
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +26,8 @@
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, readonly) NSTimeInterval deltaTime;
|
@property (nonatomic, readonly) NSTimeInterval deltaTime;
|
||||||
|
|
||||||
|
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <QuartzCore/CADisplayLink.h>
|
||||||
|
|
||||||
|
#import "RCTFrameUpdate.h"
|
||||||
|
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
|
@implementation RCTFrameUpdate
|
||||||
|
|
||||||
|
RCT_NOT_IMPLEMENTED(-init)
|
||||||
|
|
||||||
|
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_timestamp = displayLink.timestamp;
|
||||||
|
_deltaTime = displayLink.duration;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "RCTJavaScriptExecutor.h"
|
||||||
|
|
||||||
|
@interface RCTModuleData : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, weak, readonly) id<RCTJavaScriptExecutor> javaScriptExecutor;
|
||||||
|
@property (nonatomic, strong, readonly) NSNumber *uid;
|
||||||
|
@property (nonatomic, strong, readonly) id<RCTBridgeModule> instance;
|
||||||
|
|
||||||
|
@property (nonatomic, strong, readonly) Class cls;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *name;
|
||||||
|
@property (nonatomic, copy, readonly) NSArray *methods;
|
||||||
|
@property (nonatomic, copy, readonly) NSDictionary *config;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) dispatch_queue_t queue;
|
||||||
|
|
||||||
|
- (instancetype)initWithExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||||
|
uid:(NSNumber *)uid
|
||||||
|
instance:(id<RCTBridgeModule>)instance NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
|
||||||
|
- (void)dispatchBlock:(dispatch_block_t)block;
|
||||||
|
- (void)dispatchBlock:(dispatch_block_t)block dispatchGroup:(dispatch_group_t)group;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,146 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTModuleData.h"
|
||||||
|
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTModuleMethod.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
|
||||||
|
@implementation RCTModuleData
|
||||||
|
|
||||||
|
- (instancetype)initWithExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||||
|
uid:(NSNumber *)uid
|
||||||
|
instance:(id<RCTBridgeModule>)instance
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_javaScriptExecutor = javaScriptExecutor;
|
||||||
|
_uid = uid;
|
||||||
|
_instance = instance;
|
||||||
|
_cls = [instance class];
|
||||||
|
_name = RCTBridgeModuleNameForClass(_cls);
|
||||||
|
|
||||||
|
[self loadMethods];
|
||||||
|
[self generateConfig];
|
||||||
|
[self setQueue];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_NOT_IMPLEMENTED(-init);
|
||||||
|
|
||||||
|
- (void)loadMethods
|
||||||
|
{
|
||||||
|
NSMutableArray *moduleMethods = [[NSMutableArray alloc] init];
|
||||||
|
unsigned int methodCount;
|
||||||
|
Method *methods = class_copyMethodList(object_getClass(_cls), &methodCount);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < methodCount; i++) {
|
||||||
|
Method method = methods[i];
|
||||||
|
SEL selector = method_getName(method);
|
||||||
|
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
|
||||||
|
IMP imp = method_getImplementation(method);
|
||||||
|
NSArray *entries = ((NSArray *(*)(id, SEL))imp)(_cls, selector);
|
||||||
|
RCTModuleMethod *moduleMethod =
|
||||||
|
[[RCTModuleMethod alloc] initWithObjCMethodName:entries[1]
|
||||||
|
JSMethodName:entries[0]
|
||||||
|
moduleClass:_cls];
|
||||||
|
|
||||||
|
[moduleMethods addObject:moduleMethod];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(methods);
|
||||||
|
|
||||||
|
_methods = [moduleMethods copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)generateConfig
|
||||||
|
{
|
||||||
|
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||||
|
config[@"moduleID"] = _uid;
|
||||||
|
config[@"methods"] = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
|
if ([_instance respondsToSelector:@selector(constantsToExport)]) {
|
||||||
|
id consts = [_instance constantsToExport];
|
||||||
|
if (consts) {
|
||||||
|
config[@"constants"] = consts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[_methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
|
||||||
|
config[@"methods"][method.JSMethodName] = @{
|
||||||
|
@"methodID": @(idx),
|
||||||
|
@"type": method.functionKind == RCTJavaScriptFunctionKindAsync ? @"remoteAsync" : @"remote",
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
|
||||||
|
_config = [config copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setQueue
|
||||||
|
{
|
||||||
|
dispatch_queue_t queue = nil;
|
||||||
|
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
|
||||||
|
if (implementsMethodQueue) {
|
||||||
|
queue = [_instance methodQueue];
|
||||||
|
}
|
||||||
|
if (!queue) {
|
||||||
|
|
||||||
|
// Need to cache queueNames because they aren't retained by dispatch_queue
|
||||||
|
static NSMutableDictionary *queueNames;
|
||||||
|
if (!queueNames) {
|
||||||
|
queueNames = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
NSString *queueName = queueNames[_name];
|
||||||
|
if (!queueName) {
|
||||||
|
queueName = [NSString stringWithFormat:@"com.facebook.React.%@Queue", _name];
|
||||||
|
queueNames[_name] = queueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new queue
|
||||||
|
queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
|
// assign it to the module
|
||||||
|
if (implementsMethodQueue) {
|
||||||
|
@try {
|
||||||
|
[(id)_instance setValue:queue forKey:@"methodQueue"];
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
RCTLogError(@"%@ is returning nil for it's methodQueue, which is not "
|
||||||
|
"permitted. You must either return a pre-initialized "
|
||||||
|
"queue, or @synthesize the methodQueue to let the bridge "
|
||||||
|
"create a queue for you.", _name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_queue = queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dispatchBlock:(dispatch_block_t)block
|
||||||
|
{
|
||||||
|
[self dispatchBlock:block dispatchGroup:NULL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dispatchBlock:(dispatch_block_t)block
|
||||||
|
dispatchGroup:(dispatch_group_t)group
|
||||||
|
{
|
||||||
|
if (_queue == RCTJSThread) {
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||||
|
} else if (_queue) {
|
||||||
|
if (group != NULL) {
|
||||||
|
dispatch_group_async(group, _queue, block);
|
||||||
|
} else {
|
||||||
|
dispatch_async(_queue, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class RCTBridge;
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, RCTJavaScriptFunctionKind) {
|
||||||
|
RCTJavaScriptFunctionKindNormal,
|
||||||
|
RCTJavaScriptFunctionKindAsync,
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface RCTModuleMethod : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, copy, readonly) NSString *moduleClassName;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *JSMethodName;
|
||||||
|
@property (nonatomic, assign, readonly) SEL selector;
|
||||||
|
@property (nonatomic, assign, readonly) RCTJavaScriptFunctionKind functionKind;
|
||||||
|
|
||||||
|
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
|
||||||
|
JSMethodName:(NSString *)JSMethodName
|
||||||
|
moduleClass:(Class)moduleClass NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (void)invokeWithBridge:(RCTBridge *)bridge
|
||||||
|
module:(id)module
|
||||||
|
arguments:(NSArray *)arguments
|
||||||
|
context:(NSNumber *)context;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,288 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTModuleMethod.h"
|
||||||
|
|
||||||
|
#import <objc/message.h>
|
||||||
|
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
|
@interface RCTBridge (RCTModuleMethod)
|
||||||
|
|
||||||
|
- (void)_invokeAndProcessModule:(NSString *)module
|
||||||
|
method:(NSString *)method
|
||||||
|
arguments:(NSArray *)args;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTModuleMethod
|
||||||
|
{
|
||||||
|
Class _moduleClass;
|
||||||
|
SEL _selector;
|
||||||
|
NSMethodSignature *_methodSignature;
|
||||||
|
NSArray *_argumentBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
|
||||||
|
JSMethodName:(NSString *)JSMethodName
|
||||||
|
moduleClass:(Class)moduleClass
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
static NSRegularExpression *typeRegex;
|
||||||
|
static NSRegularExpression *selectorRegex;
|
||||||
|
if (!typeRegex) {
|
||||||
|
NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
|
||||||
|
NSString *constPattern = @"(?:const)";
|
||||||
|
NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
|
||||||
|
NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
|
||||||
|
typeRegex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
|
||||||
|
|
||||||
|
selectorRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=:).*?(?=[a-zA-Z_]+:|$)" options:0 error:NULL];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableArray *argumentNames = [NSMutableArray array];
|
||||||
|
[typeRegex enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) {
|
||||||
|
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
|
||||||
|
[argumentNames addObject:argumentName];
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Remove the parameters' type and name
|
||||||
|
objCMethodName = [selectorRegex stringByReplacingMatchesInString:objCMethodName
|
||||||
|
options:0
|
||||||
|
range:NSMakeRange(0, objCMethodName.length)
|
||||||
|
withTemplate:@""];
|
||||||
|
// Remove any spaces since `selector : (Type)name` is a valid syntax
|
||||||
|
objCMethodName = [objCMethodName stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||||
|
|
||||||
|
_moduleClass = moduleClass;
|
||||||
|
_moduleClassName = NSStringFromClass(_moduleClass);
|
||||||
|
_selector = NSSelectorFromString(objCMethodName);
|
||||||
|
_JSMethodName = JSMethodName.length > 0 ? JSMethodName : ({
|
||||||
|
NSString *methodName = NSStringFromSelector(_selector);
|
||||||
|
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||||
|
if (colonRange.length) {
|
||||||
|
methodName = [methodName substringToIndex:colonRange.location];
|
||||||
|
}
|
||||||
|
methodName;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Get method signature
|
||||||
|
_methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||||
|
|
||||||
|
// Process arguments
|
||||||
|
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
|
||||||
|
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
||||||
|
|
||||||
|
#define RCT_ARG_BLOCK(_logic) \
|
||||||
|
[argumentBlocks addObject:^(__unused RCTBridge *bridge, __unused NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) { \
|
||||||
|
_logic \
|
||||||
|
[invocation setArgument:&value atIndex:index]; \
|
||||||
|
}]; \
|
||||||
|
|
||||||
|
void (^addBlockArgument)(void) = ^{
|
||||||
|
RCT_ARG_BLOCK(
|
||||||
|
|
||||||
|
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
|
RCTLogError(@"Argument %tu (%@) of %@.%@ should be a number", index,
|
||||||
|
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
|
__autoreleasing id value = (json ? ^(NSArray *args) {
|
||||||
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
|
arguments:@[json, args]];
|
||||||
|
} : ^(__unused NSArray *unused) {});
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
void (^defaultCase)(const char *) = ^(const char *argumentType) {
|
||||||
|
static const char *blockType = @encode(typeof(^{}));
|
||||||
|
if (!strcmp(argumentType, blockType)) {
|
||||||
|
addBlockArgument();
|
||||||
|
} else {
|
||||||
|
RCT_ARG_BLOCK( id value = json; )
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
||||||
|
const char *argumentType = [_methodSignature getArgumentTypeAtIndex:i];
|
||||||
|
|
||||||
|
NSString *argumentName = argumentNames[i - 2];
|
||||||
|
SEL selector = NSSelectorFromString([argumentName stringByAppendingString:@":"]);
|
||||||
|
if ([RCTConvert respondsToSelector:selector]) {
|
||||||
|
switch (argumentType[0]) {
|
||||||
|
|
||||||
|
#define RCT_CONVERT_CASE(_value, _type) \
|
||||||
|
case _value: { \
|
||||||
|
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||||
|
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_CONVERT_CASE(':', SEL)
|
||||||
|
RCT_CONVERT_CASE('*', const char *)
|
||||||
|
RCT_CONVERT_CASE('c', char)
|
||||||
|
RCT_CONVERT_CASE('C', unsigned char)
|
||||||
|
RCT_CONVERT_CASE('s', short)
|
||||||
|
RCT_CONVERT_CASE('S', unsigned short)
|
||||||
|
RCT_CONVERT_CASE('i', int)
|
||||||
|
RCT_CONVERT_CASE('I', unsigned int)
|
||||||
|
RCT_CONVERT_CASE('l', long)
|
||||||
|
RCT_CONVERT_CASE('L', unsigned long)
|
||||||
|
RCT_CONVERT_CASE('q', long long)
|
||||||
|
RCT_CONVERT_CASE('Q', unsigned long long)
|
||||||
|
RCT_CONVERT_CASE('f', float)
|
||||||
|
RCT_CONVERT_CASE('d', double)
|
||||||
|
RCT_CONVERT_CASE('B', BOOL)
|
||||||
|
RCT_CONVERT_CASE('@', id)
|
||||||
|
RCT_CONVERT_CASE('^', void *)
|
||||||
|
|
||||||
|
case '{': {
|
||||||
|
[argumentBlocks addObject:^(__unused RCTBridge *bridge, __unused NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) {
|
||||||
|
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||||
|
void *returnValue = malloc(methodSignature.methodReturnLength);
|
||||||
|
NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||||
|
[_invocation setTarget:[RCTConvert class]];
|
||||||
|
[_invocation setSelector:selector];
|
||||||
|
[_invocation setArgument:&json atIndex:2];
|
||||||
|
[_invocation invoke];
|
||||||
|
[_invocation getReturnValue:returnValue];
|
||||||
|
|
||||||
|
[invocation setArgument:returnValue atIndex:index];
|
||||||
|
|
||||||
|
free(returnValue);
|
||||||
|
}];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
defaultCase(argumentType);
|
||||||
|
}
|
||||||
|
} else if ([argumentName isEqualToString:@"RCTResponseSenderBlock"]) {
|
||||||
|
addBlockArgument();
|
||||||
|
} else if ([argumentName isEqualToString:@"RCTPromiseResolveBlock"]) {
|
||||||
|
RCTAssert(i == numberOfArguments - 2,
|
||||||
|
@"The RCTPromiseResolveBlock must be the second to last parameter in -[%@ %@]",
|
||||||
|
_moduleClassName, objCMethodName);
|
||||||
|
RCT_ARG_BLOCK(
|
||||||
|
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
|
RCTLogError(@"Argument %tu (%@) of %@.%@ must be a promise resolver ID", index,
|
||||||
|
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
|
__autoreleasing RCTPromiseResolveBlock value = (^(id result) {
|
||||||
|
NSArray *arguments = result ? @[result] : @[];
|
||||||
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
|
arguments:@[json, arguments]];
|
||||||
|
});
|
||||||
|
)
|
||||||
|
_functionKind = RCTJavaScriptFunctionKindAsync;
|
||||||
|
} else if ([argumentName isEqualToString:@"RCTPromiseRejectBlock"]) {
|
||||||
|
RCTAssert(i == numberOfArguments - 1,
|
||||||
|
@"The RCTPromiseRejectBlock must be the last parameter in -[%@ %@]",
|
||||||
|
_moduleClassName, objCMethodName);
|
||||||
|
RCT_ARG_BLOCK(
|
||||||
|
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
|
RCTLogError(@"Argument %tu (%@) of %@.%@ must be a promise rejecter ID", index,
|
||||||
|
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
|
__autoreleasing RCTPromiseRejectBlock value = (^(NSError *error) {
|
||||||
|
NSDictionary *errorJSON = RCTJSErrorFromNSError(error);
|
||||||
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
|
arguments:@[json, @[errorJSON]]];
|
||||||
|
});
|
||||||
|
)
|
||||||
|
_functionKind = RCTJavaScriptFunctionKindAsync;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Unknown argument type
|
||||||
|
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert"
|
||||||
|
" to support this type.", argumentName, [self methodName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_argumentBlocks = [argumentBlocks copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invokeWithBridge:(RCTBridge *)bridge
|
||||||
|
module:(id)module
|
||||||
|
arguments:(NSArray *)arguments
|
||||||
|
context:(NSNumber *)context
|
||||||
|
{
|
||||||
|
if (RCT_DEBUG) {
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
|
||||||
|
%@ on a module of class %@", [self methodName], [module class]);
|
||||||
|
|
||||||
|
// Safety check
|
||||||
|
if (arguments.count != _argumentBlocks.count) {
|
||||||
|
NSInteger actualCount = arguments.count;
|
||||||
|
NSInteger expectedCount = _argumentBlocks.count;
|
||||||
|
|
||||||
|
// Subtract the implicit Promise resolver and rejecter functions for implementations of async functions
|
||||||
|
if (_functionKind == RCTJavaScriptFunctionKindAsync) {
|
||||||
|
actualCount -= 2;
|
||||||
|
expectedCount -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTLogError(@"%@.%@ was called with %zd arguments, but expects %zd",
|
||||||
|
RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName,
|
||||||
|
actualCount, expectedCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create invocation (we can't re-use this as it wouldn't be thread-safe)
|
||||||
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature];
|
||||||
|
[invocation setArgument:&_selector atIndex:1];
|
||||||
|
[invocation retainArguments];
|
||||||
|
|
||||||
|
// Set arguments
|
||||||
|
NSUInteger index = 0;
|
||||||
|
for (id json in arguments) {
|
||||||
|
id arg = RCTNilIfNull(json);
|
||||||
|
void (^block)(RCTBridge *, NSNumber *, NSInvocation *, NSUInteger, id) = _argumentBlocks[index];
|
||||||
|
block(bridge, context, invocation, index + 2, arg);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke method
|
||||||
|
[invocation invokeWithTarget:module];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)methodName
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"-[%@ %@]", _moduleClass,
|
||||||
|
NSStringFromSelector(_selector)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p; exports %@ as %@();>",
|
||||||
|
[self class], self, [self methodName], _JSMethodName];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -18,6 +18,7 @@
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTDefines.h"
|
#import "RCTDefines.h"
|
||||||
|
#import "RCTModuleData.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
NSString *const RCTProfileDidStartProfiling = @"RCTProfileDidStartProfiling";
|
NSString *const RCTProfileDidStartProfiling = @"RCTProfileDidStartProfiling";
|
||||||
|
@ -98,12 +99,6 @@ NSDictionary *RCTProfileGetMemoryUsage(void)
|
||||||
|
|
||||||
#pragma mark - Module hooks
|
#pragma mark - Module hooks
|
||||||
|
|
||||||
@interface RCTBridge (Private)
|
|
||||||
|
|
||||||
- (void)dispatchBlock:(dispatch_block_t)block forModule:(id<RCTBridgeModule>)module;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
static const char *RCTProfileProxyClassName(Class);
|
static const char *RCTProfileProxyClassName(Class);
|
||||||
static const char *RCTProfileProxyClassName(Class class)
|
static const char *RCTProfileProxyClassName(Class class)
|
||||||
{
|
{
|
||||||
|
@ -152,9 +147,9 @@ static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
|
||||||
static void RCTProfileHookModules(RCTBridge *);
|
static void RCTProfileHookModules(RCTBridge *);
|
||||||
static void RCTProfileHookModules(RCTBridge *bridge)
|
static void RCTProfileHookModules(RCTBridge *bridge)
|
||||||
{
|
{
|
||||||
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
|
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
|
||||||
[bridge dispatchBlock:^{
|
[moduleData dispatchBlock:^{
|
||||||
Class moduleClass = object_getClass(module);
|
Class moduleClass = moduleData.cls;
|
||||||
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
|
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
|
||||||
|
|
||||||
unsigned int methodCount;
|
unsigned int methodCount;
|
||||||
|
@ -167,7 +162,7 @@ static void RCTProfileHookModules(RCTBridge *bridge)
|
||||||
}
|
}
|
||||||
IMP originalIMP = method_getImplementation(method);
|
IMP originalIMP = method_getImplementation(method);
|
||||||
const char *returnType = method_getTypeEncoding(method);
|
const char *returnType = method_getTypeEncoding(method);
|
||||||
class_addMethod(proxyClass, selector, RCTProfileMsgForward(module, selector), returnType);
|
class_addMethod(proxyClass, selector, RCTProfileMsgForward(moduleData.instance, selector), returnType);
|
||||||
class_addMethod(proxyClass, RCTProfileProxySelector(selector), originalIMP, returnType);
|
class_addMethod(proxyClass, RCTProfileProxySelector(selector), originalIMP, returnType);
|
||||||
}
|
}
|
||||||
free(methods);
|
free(methods);
|
||||||
|
@ -185,24 +180,24 @@ static void RCTProfileHookModules(RCTBridge *bridge)
|
||||||
}
|
}
|
||||||
|
|
||||||
objc_registerClassPair(proxyClass);
|
objc_registerClassPair(proxyClass);
|
||||||
object_setClass(module, proxyClass);
|
object_setClass(moduleData.instance, proxyClass);
|
||||||
} forModule:module];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RCTProfileUnhookModules(RCTBridge *);
|
void RCTProfileUnhookModules(RCTBridge *);
|
||||||
void RCTProfileUnhookModules(RCTBridge *bridge)
|
void RCTProfileUnhookModules(RCTBridge *bridge)
|
||||||
{
|
{
|
||||||
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
|
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
|
||||||
[bridge dispatchBlock:^{
|
[moduleData dispatchBlock:^{
|
||||||
RCTProfileLock(
|
RCTProfileLock(
|
||||||
Class proxyClass = object_getClass(module);
|
Class proxyClass = object_getClass(moduleData.instance);
|
||||||
if (module.class != proxyClass) {
|
if (moduleData.cls != proxyClass) {
|
||||||
object_setClass(module, module.class);
|
object_setClass(moduleData.instance, moduleData.cls);
|
||||||
objc_disposeClassPair(proxyClass);
|
objc_disposeClassPair(proxyClass);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} forModule:module];
|
}];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,3 +61,5 @@ RCT_EXTERN NSError *RCTErrorWithMessage(NSString *message);
|
||||||
// Convert nil values to NSNull, and vice-versa
|
// Convert nil values to NSNull, and vice-versa
|
||||||
RCT_EXTERN id RCTNullIfNil(id value);
|
RCT_EXTERN id RCTNullIfNil(id value);
|
||||||
RCT_EXTERN id RCTNilIfNull(id value);
|
RCT_EXTERN id RCTNilIfNull(id value);
|
||||||
|
|
||||||
|
RCT_EXTERN NSDictionary *RCTJSErrorFromNSError(NSError *error);
|
||||||
|
|
|
@ -285,3 +285,24 @@ id RCTNilIfNull(id value)
|
||||||
{
|
{
|
||||||
return value == (id)kCFNull ? nil : value;
|
return value == (id)kCFNull ? nil : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Can we just replace RCTMakeError with this function instead?
|
||||||
|
NSDictionary *RCTJSErrorFromNSError(NSError *error)
|
||||||
|
{
|
||||||
|
NSString *errorMessage;
|
||||||
|
NSArray *stackTrace = [NSThread callStackSymbols];
|
||||||
|
NSMutableDictionary *errorInfo =
|
||||||
|
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
errorMessage = error.localizedDescription ?: @"Unknown error from a native module";
|
||||||
|
errorInfo[@"domain"] = error.domain ?: RCTErrorDomain;
|
||||||
|
errorInfo[@"code"] = @(error.code);
|
||||||
|
} else {
|
||||||
|
errorMessage = @"Unknown error from a native module";
|
||||||
|
errorInfo[@"domain"] = RCTErrorDomain;
|
||||||
|
errorInfo[@"code"] = @-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RCTMakeError(errorMessage, nil, errorInfo);
|
||||||
|
}
|
||||||
|
|
|
@ -810,7 +810,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag
|
RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag
|
||||||
viewName:(NSString *)viewName
|
viewName:(NSString *)viewName
|
||||||
rootTag:(NSNumber *)rootTag
|
rootTag:(__unused NSNumber *)rootTag
|
||||||
props:(NSDictionary *)props)
|
props:(NSDictionary *)props)
|
||||||
{
|
{
|
||||||
RCTViewManager *manager = _viewManagers[viewName];
|
RCTViewManager *manager = _viewManagers[viewName];
|
||||||
|
@ -1222,7 +1222,7 @@ RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag
|
||||||
* this in order to determine if scrolling is appropriate.
|
* this in order to determine if scrolling is appropriate.
|
||||||
*/
|
*/
|
||||||
RCT_EXPORT_METHOD(setJSResponder:(NSNumber *)reactTag
|
RCT_EXPORT_METHOD(setJSResponder:(NSNumber *)reactTag
|
||||||
blockNativeResponder:(BOOL)blockNativeResponder)
|
blockNativeResponder:(__unused BOOL)blockNativeResponder)
|
||||||
{
|
{
|
||||||
[self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
_jsResponder = viewRegistry[reactTag];
|
_jsResponder = viewRegistry[reactTag];
|
||||||
|
|
|
@ -52,6 +52,10 @@
|
||||||
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; };
|
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; };
|
||||||
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; };
|
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; };
|
||||||
146459261B06C49500B389AA /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 146459251B06C49500B389AA /* RCTFPSGraph.m */; };
|
146459261B06C49500B389AA /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 146459251B06C49500B389AA /* RCTFPSGraph.m */; };
|
||||||
|
14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */; };
|
||||||
|
14C2CA741B3AC64300E6CBB2 /* RCTModuleData.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA731B3AC64300E6CBB2 /* RCTModuleData.m */; };
|
||||||
|
14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */; };
|
||||||
|
14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */; };
|
||||||
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; };
|
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; };
|
||||||
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; };
|
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; };
|
||||||
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
|
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
|
||||||
|
@ -187,6 +191,12 @@
|
||||||
14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = "<group>"; };
|
14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = "<group>"; };
|
||||||
146459241B06C49500B389AA /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFPSGraph.h; sourceTree = "<group>"; };
|
146459241B06C49500B389AA /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFPSGraph.h; sourceTree = "<group>"; };
|
||||||
146459251B06C49500B389AA /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = "<group>"; };
|
146459251B06C49500B389AA /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = "<group>"; };
|
||||||
|
14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleMethod.h; sourceTree = "<group>"; };
|
||||||
|
14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethod.m; sourceTree = "<group>"; };
|
||||||
|
14C2CA721B3AC64300E6CBB2 /* RCTModuleData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleData.h; sourceTree = "<group>"; };
|
||||||
|
14C2CA731B3AC64300E6CBB2 /* RCTModuleData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleData.m; sourceTree = "<group>"; };
|
||||||
|
14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFrameUpdate.m; sourceTree = "<group>"; };
|
||||||
|
14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBatchedBridge.m; sourceTree = "<group>"; };
|
||||||
14F362071AABD06A001CE568 /* RCTSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitch.h; sourceTree = "<group>"; };
|
14F362071AABD06A001CE568 /* RCTSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitch.h; sourceTree = "<group>"; };
|
||||||
14F362081AABD06A001CE568 /* RCTSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitch.m; sourceTree = "<group>"; };
|
14F362081AABD06A001CE568 /* RCTSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitch.m; sourceTree = "<group>"; };
|
||||||
14F362091AABD06A001CE568 /* RCTSwitchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitchManager.h; sourceTree = "<group>"; };
|
14F362091AABD06A001CE568 /* RCTSwitchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitchManager.h; sourceTree = "<group>"; };
|
||||||
|
@ -444,6 +454,12 @@
|
||||||
1403F2B21B0AE60700C2A9A4 /* RCTPerfStats.m */,
|
1403F2B21B0AE60700C2A9A4 /* RCTPerfStats.m */,
|
||||||
142014171B32094000CC17BA /* RCTPerformanceLogger.m */,
|
142014171B32094000CC17BA /* RCTPerformanceLogger.m */,
|
||||||
142014181B32094000CC17BA /* RCTPerformanceLogger.h */,
|
142014181B32094000CC17BA /* RCTPerformanceLogger.h */,
|
||||||
|
14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */,
|
||||||
|
14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */,
|
||||||
|
14C2CA721B3AC64300E6CBB2 /* RCTModuleData.h */,
|
||||||
|
14C2CA731B3AC64300E6CBB2 /* RCTModuleData.m */,
|
||||||
|
14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */,
|
||||||
|
14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */,
|
||||||
);
|
);
|
||||||
path = Base;
|
path = Base;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -526,12 +542,14 @@
|
||||||
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */,
|
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */,
|
||||||
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */,
|
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */,
|
||||||
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
|
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
|
||||||
|
14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */,
|
||||||
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */,
|
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */,
|
||||||
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */,
|
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */,
|
||||||
131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */,
|
131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */,
|
||||||
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */,
|
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */,
|
||||||
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
|
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
|
||||||
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,
|
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,
|
||||||
|
14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */,
|
||||||
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */,
|
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */,
|
||||||
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
||||||
13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */,
|
13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */,
|
||||||
|
@ -554,8 +572,10 @@
|
||||||
1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */,
|
1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */,
|
||||||
13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */,
|
13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */,
|
||||||
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */,
|
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */,
|
||||||
|
14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */,
|
||||||
13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */,
|
13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */,
|
||||||
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */,
|
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */,
|
||||||
|
14C2CA741B3AC64300E6CBB2 /* RCTModuleData.m in Sources */,
|
||||||
142014191B32094000CC17BA /* RCTPerformanceLogger.m in Sources */,
|
142014191B32094000CC17BA /* RCTPerformanceLogger.m in Sources */,
|
||||||
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */,
|
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */,
|
||||||
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */,
|
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue