[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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
|
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
|
||||
* 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
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
* 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.
|
||||
*/
|
||||
|
@ -22,6 +26,8 @@
|
|||
*/
|
||||
@property (nonatomic, readonly) NSTimeInterval deltaTime;
|
||||
|
||||
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@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 "RCTBridge.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTModuleData.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *const RCTProfileDidStartProfiling = @"RCTProfileDidStartProfiling";
|
||||
|
@ -98,12 +99,6 @@ NSDictionary *RCTProfileGetMemoryUsage(void)
|
|||
|
||||
#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 class)
|
||||
{
|
||||
|
@ -152,9 +147,9 @@ static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
|
|||
static void RCTProfileHookModules(RCTBridge *);
|
||||
static void RCTProfileHookModules(RCTBridge *bridge)
|
||||
{
|
||||
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
|
||||
[bridge dispatchBlock:^{
|
||||
Class moduleClass = object_getClass(module);
|
||||
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
|
||||
[moduleData dispatchBlock:^{
|
||||
Class moduleClass = moduleData.cls;
|
||||
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
|
||||
|
||||
unsigned int methodCount;
|
||||
|
@ -167,7 +162,7 @@ static void RCTProfileHookModules(RCTBridge *bridge)
|
|||
}
|
||||
IMP originalIMP = method_getImplementation(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);
|
||||
}
|
||||
free(methods);
|
||||
|
@ -185,24 +180,24 @@ static void RCTProfileHookModules(RCTBridge *bridge)
|
|||
}
|
||||
|
||||
objc_registerClassPair(proxyClass);
|
||||
object_setClass(module, proxyClass);
|
||||
} forModule:module];
|
||||
object_setClass(moduleData.instance, proxyClass);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
void RCTProfileUnhookModules(RCTBridge *);
|
||||
void RCTProfileUnhookModules(RCTBridge *bridge)
|
||||
{
|
||||
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
|
||||
[bridge dispatchBlock:^{
|
||||
RCTProfileLock(
|
||||
Class proxyClass = object_getClass(module);
|
||||
if (module.class != proxyClass) {
|
||||
object_setClass(module, module.class);
|
||||
objc_disposeClassPair(proxyClass);
|
||||
}
|
||||
);
|
||||
} forModule:module];
|
||||
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);
|
||||
}
|
||||
);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -61,3 +61,5 @@ RCT_EXTERN NSError *RCTErrorWithMessage(NSString *message);
|
|||
// Convert nil values to NSNull, and vice-versa
|
||||
RCT_EXTERN id RCTNullIfNil(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;
|
||||
}
|
||||
|
||||
// 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
|
||||
viewName:(NSString *)viewName
|
||||
rootTag:(NSNumber *)rootTag
|
||||
rootTag:(__unused NSNumber *)rootTag
|
||||
props:(NSDictionary *)props)
|
||||
{
|
||||
RCTViewManager *manager = _viewManagers[viewName];
|
||||
|
@ -1222,7 +1222,7 @@ RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag
|
|||
* this in order to determine if scrolling is appropriate.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(setJSResponder:(NSNumber *)reactTag
|
||||
blockNativeResponder:(BOOL)blockNativeResponder)
|
||||
blockNativeResponder:(__unused BOOL)blockNativeResponder)
|
||||
{
|
||||
[self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
_jsResponder = viewRegistry[reactTag];
|
||||
|
|
|
@ -52,6 +52,10 @@
|
|||
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; };
|
||||
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.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 */; };
|
||||
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -444,6 +454,12 @@
|
|||
1403F2B21B0AE60700C2A9A4 /* RCTPerfStats.m */,
|
||||
142014171B32094000CC17BA /* RCTPerformanceLogger.m */,
|
||||
142014181B32094000CC17BA /* RCTPerformanceLogger.h */,
|
||||
14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */,
|
||||
14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */,
|
||||
14C2CA721B3AC64300E6CBB2 /* RCTModuleData.h */,
|
||||
14C2CA731B3AC64300E6CBB2 /* RCTModuleData.m */,
|
||||
14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */,
|
||||
14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */,
|
||||
);
|
||||
path = Base;
|
||||
sourceTree = "<group>";
|
||||
|
@ -526,12 +542,14 @@
|
|||
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */,
|
||||
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */,
|
||||
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
|
||||
14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */,
|
||||
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */,
|
||||
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */,
|
||||
131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */,
|
||||
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */,
|
||||
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
|
||||
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,
|
||||
14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */,
|
||||
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */,
|
||||
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
||||
13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */,
|
||||
|
@ -554,8 +572,10 @@
|
|||
1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */,
|
||||
13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */,
|
||||
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */,
|
||||
14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */,
|
||||
13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */,
|
||||
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */,
|
||||
14C2CA741B3AC64300E6CBB2 /* RCTModuleData.m in Sources */,
|
||||
142014191B32094000CC17BA /* RCTPerformanceLogger.m in Sources */,
|
||||
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */,
|
||||
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */,
|
||||
|
|
Loading…
Reference in New Issue