2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
#import "RCTBridge.h"
|
|
|
|
|
|
|
|
#import <dlfcn.h>
|
|
|
|
#import <objc/message.h>
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
#import <mach-o/dyld.h>
|
|
|
|
#import <mach-o/getsect.h>
|
|
|
|
|
2015-04-02 14:33:21 +00:00
|
|
|
#import "RCTContextExecutor.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTConvert.h"
|
|
|
|
#import "RCTEventDispatcher.h"
|
2015-04-02 14:33:21 +00:00
|
|
|
#import "RCTJavaScriptLoader.h"
|
|
|
|
#import "RCTKeyCommands.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTLog.h"
|
2015-04-20 11:55:05 +00:00
|
|
|
#import "RCTProfile.h"
|
2015-04-07 14:36:26 +00:00
|
|
|
#import "RCTRedBox.h"
|
2015-04-02 14:33:21 +00:00
|
|
|
#import "RCTRootView.h"
|
2015-05-04 17:35:49 +00:00
|
|
|
#import "RCTSourceCode.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTSparseArray.h"
|
|
|
|
#import "RCTUtils.h"
|
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
|
|
|
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
2015-05-07 11:29:31 +00:00
|
|
|
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
2015-04-11 22:08:00 +00:00
|
|
|
|
2015-04-26 02:18:39 +00:00
|
|
|
dispatch_queue_t const RCTJSThread = nil;
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
/**
|
|
|
|
* Must be kept in sync with `MessageQueue.js`.
|
|
|
|
*/
|
|
|
|
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
|
|
|
RCTBridgeFieldRequestModuleIDs = 0,
|
|
|
|
RCTBridgeFieldMethodIDs,
|
|
|
|
RCTBridgeFieldParamss,
|
|
|
|
RCTBridgeFieldResponseCBIDs,
|
|
|
|
RCTBridgeFieldResponseReturnValues,
|
|
|
|
RCTBridgeFieldFlushDateMillis
|
|
|
|
};
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
#ifdef __LP64__
|
|
|
|
typedef uint64_t RCTHeaderValue;
|
|
|
|
typedef struct section_64 RCTHeaderSection;
|
|
|
|
#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
|
|
|
|
#else
|
|
|
|
typedef uint32_t RCTHeaderValue;
|
|
|
|
typedef struct section RCTHeaderSection;
|
|
|
|
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
|
|
|
#endif
|
2015-04-02 14:33:21 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
#define RCTAssertJSThread() \
|
|
|
|
RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTContextExecutor"] || \
|
|
|
|
[[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \
|
|
|
|
@"This method must be called on JS thread")
|
|
|
|
|
2015-04-10 00:11:13 +00:00
|
|
|
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
|
|
|
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
/**
|
|
|
|
* This function returns the module name for a given class.
|
|
|
|
*/
|
2015-04-07 14:36:26 +00:00
|
|
|
NSString *RCTBridgeModuleNameForClass(Class cls)
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-04-08 12:42:43 +00:00
|
|
|
NSString *name = nil;
|
|
|
|
if ([cls respondsToSelector:@selector(moduleName)]) {
|
|
|
|
name = [cls valueForKey:@"moduleName"];
|
|
|
|
}
|
|
|
|
if ([name length] == 0) {
|
|
|
|
name = NSStringFromClass(cls);
|
|
|
|
}
|
|
|
|
if ([name hasPrefix:@"RK"]) {
|
|
|
|
name = [name stringByReplacingCharactersInRange:(NSRange){0,@"RK".length} withString:@"RCT"];
|
|
|
|
}
|
|
|
|
return name;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-02-24 17:06:57 +00:00
|
|
|
/**
|
|
|
|
* This function scans all classes available at runtime and returns an array
|
|
|
|
* of all JSMethods registered.
|
|
|
|
*/
|
|
|
|
static NSArray *RCTJSMethods(void)
|
|
|
|
{
|
|
|
|
static NSArray *JSMethods;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
NSMutableSet *uniqueMethods = [NSMutableSet set];
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
Dl_info info;
|
|
|
|
dladdr(&RCTJSMethods, &info);
|
|
|
|
|
|
|
|
const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
|
|
|
|
const RCTHeaderSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTImport");
|
|
|
|
|
|
|
|
if (section) {
|
|
|
|
for (RCTHeaderValue addr = section->offset;
|
|
|
|
addr < section->offset + section->size;
|
|
|
|
addr += sizeof(const char **)) {
|
|
|
|
|
|
|
|
// Get data entry
|
|
|
|
NSString *entry = @(*(const char **)(mach_header + addr));
|
|
|
|
[uniqueMethods addObject:entry];
|
2015-02-24 17:06:57 +00:00
|
|
|
}
|
2015-04-08 12:42:43 +00:00
|
|
|
}
|
2015-02-24 17:06:57 +00:00
|
|
|
|
|
|
|
JSMethods = [uniqueMethods allObjects];
|
|
|
|
});
|
|
|
|
|
|
|
|
return JSMethods;
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
/**
|
2015-04-08 12:42:43 +00:00
|
|
|
* This function scans all exported modules available at runtime and returns an
|
|
|
|
* array. As a backup, it also scans all classes that implement the
|
|
|
|
* RTCBridgeModule protocol to ensure they've been exported. This scanning
|
|
|
|
* functionality is disabled in release mode to improve startup performance.
|
2015-02-20 04:10:52 +00:00
|
|
|
*/
|
2015-05-04 17:35:49 +00:00
|
|
|
static NSDictionary *RCTModuleIDsByName;
|
2015-02-20 04:10:52 +00:00
|
|
|
static NSArray *RCTModuleNamesByID;
|
2015-04-08 12:42:43 +00:00
|
|
|
static NSArray *RCTModuleClassesByID;
|
2015-02-20 04:10:52 +00:00
|
|
|
static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|
|
|
{
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-04-08 12:42:43 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTModuleIDsByName = [[NSMutableDictionary alloc] init];
|
|
|
|
RCTModuleNamesByID = [[NSMutableArray alloc] init];
|
|
|
|
RCTModuleClassesByID = [[NSMutableArray alloc] init];
|
2015-04-08 12:42:43 +00:00
|
|
|
|
|
|
|
Dl_info info;
|
|
|
|
dladdr(&RCTBridgeModuleClassesByModuleID, &info);
|
|
|
|
|
|
|
|
const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
|
|
|
|
const RCTHeaderSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExportModule");
|
|
|
|
|
|
|
|
if (section) {
|
|
|
|
for (RCTHeaderValue addr = section->offset;
|
|
|
|
addr < section->offset + section->size;
|
|
|
|
addr += sizeof(const char **)) {
|
|
|
|
|
|
|
|
// Get data entry
|
|
|
|
NSString *entry = @(*(const char **)(mach_header + addr));
|
2015-04-21 12:26:51 +00:00
|
|
|
NSArray *parts = [[entry substringWithRange:(NSRange){2, entry.length - 3}]
|
|
|
|
componentsSeparatedByString:@" "];
|
2015-04-08 12:42:43 +00:00
|
|
|
|
|
|
|
// Parse class name
|
|
|
|
NSString *moduleClassName = parts[0];
|
|
|
|
NSRange categoryRange = [moduleClassName rangeOfString:@"("];
|
|
|
|
if (categoryRange.length) {
|
|
|
|
moduleClassName = [moduleClassName substringToIndex:categoryRange.location];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get class
|
|
|
|
Class cls = NSClassFromString(moduleClassName);
|
2015-04-11 22:08:00 +00:00
|
|
|
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
|
|
@"%@ does not conform to the RCTBridgeModule protocol",
|
|
|
|
NSStringFromClass(cls));
|
2015-04-08 12:42:43 +00:00
|
|
|
|
|
|
|
// Register module
|
2015-05-04 17:35:49 +00:00
|
|
|
NSString *moduleName = RCTBridgeModuleNameForClass(cls);
|
|
|
|
((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count);
|
|
|
|
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
2015-04-08 12:42:43 +00:00
|
|
|
[(NSMutableArray *)RCTModuleClassesByID addObject:cls];
|
|
|
|
}
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-21 12:26:51 +00:00
|
|
|
if (RCT_DEBUG) {
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-21 12:26:51 +00:00
|
|
|
// We may be able to get rid of this check in future, once people
|
|
|
|
// get used to the new registration system. That would potentially
|
|
|
|
// allow you to create modules that are not automatically registered
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-21 12:26:51 +00:00
|
|
|
static unsigned int classCount;
|
|
|
|
Class *classes = objc_copyClassList(&classCount);
|
|
|
|
for (unsigned int i = 0; i < classCount; i++)
|
2015-04-08 12:42:43 +00:00
|
|
|
{
|
2015-04-21 12:26:51 +00:00
|
|
|
Class cls = classes[i];
|
|
|
|
Class superclass = cls;
|
|
|
|
while (superclass)
|
2015-04-08 12:42:43 +00:00
|
|
|
{
|
2015-04-21 12:26:51 +00:00
|
|
|
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
|
|
|
{
|
|
|
|
if (![RCTModuleClassesByID containsObject:cls]) {
|
|
|
|
RCTLogError(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", NSStringFromClass(cls));
|
|
|
|
}
|
|
|
|
break;
|
2015-04-08 12:42:43 +00:00
|
|
|
}
|
2015-04-21 12:26:51 +00:00
|
|
|
superclass = class_getSuperclass(superclass);
|
2015-04-08 12:42:43 +00:00
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-04-28 11:53:55 +00:00
|
|
|
free(classes);
|
2015-04-08 12:42:43 +00:00
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
});
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
return RCTModuleClassesByID;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@class RCTBatchedBridge;
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
@interface RCTBridge ()
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
|
|
|
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
|
|
|
@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher;
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
- (void)_invokeAndProcessModule:(NSString *)module
|
|
|
|
method:(NSString *)method
|
2015-04-20 09:09:11 +00:00
|
|
|
arguments:(NSArray *)args
|
|
|
|
context:(NSNumber *)context;
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@interface RCTBatchedBridge : RCTBridge <RCTInvalidating>
|
|
|
|
|
|
|
|
@property (nonatomic, weak) RCTBridge *parentBridge;
|
|
|
|
|
|
|
|
- (instancetype)initWithParentBridge:(RCTBridge *)bridge;
|
2015-05-03 01:43:34 +00:00
|
|
|
|
2015-04-17 11:02:37 +00:00
|
|
|
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
|
|
|
method:(NSString *)method
|
2015-04-20 09:09:11 +00:00
|
|
|
arguments:(NSArray *)args
|
|
|
|
context:(NSNumber *)context;
|
2015-05-03 01:43:34 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This private class is used as a container for exported method info
|
|
|
|
*/
|
|
|
|
@interface RCTModuleMethod : NSObject
|
|
|
|
|
|
|
|
@property (nonatomic, copy, readonly) NSString *moduleClassName;
|
|
|
|
@property (nonatomic, copy, readonly) NSString *JSMethodName;
|
2015-04-20 11:55:05 +00:00
|
|
|
@property (nonatomic, assign, readonly) SEL selector;
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RCTModuleMethod
|
|
|
|
{
|
|
|
|
BOOL _isClassMethod;
|
|
|
|
Class _moduleClass;
|
|
|
|
SEL _selector;
|
|
|
|
NSMethodSignature *_methodSignature;
|
|
|
|
NSArray *_argumentBlocks;
|
|
|
|
NSString *_methodName;
|
2015-04-20 19:06:02 +00:00
|
|
|
dispatch_block_t _methodQueue;
|
2015-03-01 23:33:55 +00:00
|
|
|
}
|
|
|
|
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
|
|
|
{
|
2015-04-08 15:52:48 +00:00
|
|
|
NSRange colonRange = [methodName rangeOfString:@":"];
|
|
|
|
if (colonRange.length) {
|
|
|
|
methodName = [methodName substringToIndex:colonRange.location];
|
|
|
|
}
|
|
|
|
return methodName;
|
|
|
|
}
|
|
|
|
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
- (instancetype)initWithReactMethodName:(NSString *)reactMethodName
|
|
|
|
objCMethodName:(NSString *)objCMethodName
|
|
|
|
JSMethodName:(NSString *)JSMethodName
|
2015-03-01 23:33:55 +00:00
|
|
|
{
|
|
|
|
if ((self = [super init])) {
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
_methodName = reactMethodName;
|
|
|
|
NSArray *parts = [[reactMethodName substringWithRange:(NSRange){2, reactMethodName.length - 3}] componentsSeparatedByString:@" "];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
// Parse class and method
|
|
|
|
_moduleClassName = parts[0];
|
|
|
|
NSRange categoryRange = [_moduleClassName rangeOfString:@"("];
|
2015-04-08 12:42:43 +00:00
|
|
|
if (categoryRange.length) {
|
2015-03-01 23:33:55 +00:00
|
|
|
_moduleClassName = [_moduleClassName substringToIndex:categoryRange.location];
|
|
|
|
}
|
|
|
|
|
2015-04-08 15:52:48 +00:00
|
|
|
NSArray *argumentNames = nil;
|
|
|
|
if ([parts[1] hasPrefix:@"__rct_export__"]) {
|
|
|
|
// New format
|
|
|
|
NSString *selectorString = [parts[1] substringFromIndex:14];
|
|
|
|
_selector = NSSelectorFromString(selectorString);
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
_JSMethodName = JSMethodName ?: RCTStringUpToFirstArgument(selectorString);
|
2015-04-08 15:52:48 +00:00
|
|
|
|
|
|
|
static NSRegularExpression *regExp;
|
|
|
|
if (!regExp) {
|
|
|
|
NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
|
|
|
|
NSString *constPattern = @"(?:const)";
|
|
|
|
NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
|
|
|
|
NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
|
|
|
|
regExp = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
|
|
|
|
}
|
|
|
|
|
|
|
|
argumentNames = [NSMutableArray array];
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
[regExp enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
|
|
|
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
|
2015-04-08 15:52:48 +00:00
|
|
|
[(NSMutableArray *)argumentNames addObject:argumentName];
|
|
|
|
}];
|
|
|
|
} else {
|
|
|
|
// Old format
|
|
|
|
NSString *selectorString = parts[1];
|
|
|
|
_selector = NSSelectorFromString(selectorString);
|
|
|
|
_JSMethodName = JSMethodName ?: RCTStringUpToFirstArgument(selectorString);
|
|
|
|
}
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
// Extract class and method details
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
_isClassMethod = [reactMethodName characterAtIndex:0] == '+';
|
2015-03-01 23:33:55 +00:00
|
|
|
_moduleClass = NSClassFromString(_moduleClassName);
|
|
|
|
|
2015-04-21 12:26:51 +00:00
|
|
|
if (RCT_DEBUG) {
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
|
2015-04-21 12:26:51 +00:00
|
|
|
// Sanity check
|
|
|
|
RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
|
|
@"You are attempting to export the method %@, but %@ does not \
|
|
|
|
conform to the RCTBridgeModule Protocol", objCMethodName, _moduleClassName);
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
// Get method signature
|
|
|
|
_methodSignature = _isClassMethod ?
|
2015-04-11 22:08:00 +00:00
|
|
|
[_moduleClass methodSignatureForSelector:_selector] :
|
|
|
|
[_moduleClass instanceMethodSignatureForSelector:_selector];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
// Process arguments
|
|
|
|
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
|
|
|
|
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
2015-04-08 15:52:48 +00:00
|
|
|
|
|
|
|
#define RCT_ARG_BLOCK(_logic) \
|
2015-04-20 09:09:11 +00:00
|
|
|
[argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) { \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
_logic \
|
|
|
|
[invocation setArgument:&value atIndex:index]; \
|
|
|
|
}]; \
|
2015-04-08 15:52:48 +00:00
|
|
|
|
|
|
|
void (^addBlockArgument)(void) = ^{
|
|
|
|
RCT_ARG_BLOCK(
|
2015-04-21 16:48:29 +00:00
|
|
|
|
|
|
|
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
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"
|
2015-04-20 09:09:11 +00:00
|
|
|
arguments:@[json, args]
|
|
|
|
context:context];
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
} : ^(NSArray *unused) {});
|
|
|
|
)
|
2015-04-08 15:52:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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; )
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
|
|
|
const char *argumentType = [_methodSignature getArgumentTypeAtIndex:i];
|
|
|
|
|
2015-04-08 15:52:48 +00:00
|
|
|
BOOL useFallback = YES;
|
|
|
|
if (argumentNames) {
|
|
|
|
NSString *argumentName = argumentNames[i - 2];
|
|
|
|
SEL selector = NSSelectorFromString([argumentName stringByAppendingString:@":"]);
|
|
|
|
if ([RCTConvert respondsToSelector:selector]) {
|
|
|
|
useFallback = NO;
|
|
|
|
switch (argumentType[0]) {
|
|
|
|
|
|
|
|
#define RCT_CONVERT_CASE(_value, _type) \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
case _value: { \
|
2015-04-27 10:58:30 +00:00
|
|
|
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
|
|
|
break; \
|
|
|
|
}
|
2015-04-11 22:08:00 +00:00
|
|
|
|
|
|
|
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 *)
|
2015-04-27 10:58:30 +00:00
|
|
|
|
|
|
|
case '{': {
|
|
|
|
[argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) {
|
|
|
|
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
|
2015-05-02 21:11:09 +00:00
|
|
|
void *returnValue = malloc(methodSignature.methodReturnLength);
|
2015-04-27 10:58:30 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-04-08 15:52:48 +00:00
|
|
|
default:
|
|
|
|
defaultCase(argumentType);
|
|
|
|
}
|
|
|
|
} else if ([argumentName isEqualToString:@"RCTResponseSenderBlock"]) {
|
|
|
|
addBlockArgument();
|
|
|
|
useFallback = NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useFallback) {
|
|
|
|
switch (argumentType[0]) {
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
#define RCT_CASE(_value, _class, _logic) \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
case _value: { \
|
|
|
|
RCT_ARG_BLOCK( \
|
2015-04-21 16:48:29 +00:00
|
|
|
if (RCT_DEBUG && json && ![json isKindOfClass:[_class class]]) { \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
RCTLogError(@"Argument %tu (%@) of %@.%@ should be of type %@", index, \
|
|
|
|
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, [_class class]); \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
_logic \
|
|
|
|
) \
|
|
|
|
break; \
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
RCT_CASE(':', NSString, SEL value = NSSelectorFromString(json); )
|
|
|
|
RCT_CASE('*', NSString, const char *value = [json UTF8String]; )
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
#define RCT_SIMPLE_CASE(_value, _type, _selector) \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
case _value: { \
|
|
|
|
RCT_ARG_BLOCK( \
|
2015-04-21 16:48:29 +00:00
|
|
|
if (RCT_DEBUG && json && ![json respondsToSelector:@selector(_selector)]) { \
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \
|
|
|
|
index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
_type value = [json _selector]; \
|
|
|
|
) \
|
|
|
|
break; \
|
|
|
|
}
|
2015-04-08 15:52:48 +00:00
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
RCT_SIMPLE_CASE('c', char, charValue)
|
|
|
|
RCT_SIMPLE_CASE('C', unsigned char, unsignedCharValue)
|
|
|
|
RCT_SIMPLE_CASE('s', short, shortValue)
|
|
|
|
RCT_SIMPLE_CASE('S', unsigned short, unsignedShortValue)
|
|
|
|
RCT_SIMPLE_CASE('i', int, intValue)
|
|
|
|
RCT_SIMPLE_CASE('I', unsigned int, unsignedIntValue)
|
|
|
|
RCT_SIMPLE_CASE('l', long, longValue)
|
|
|
|
RCT_SIMPLE_CASE('L', unsigned long, unsignedLongValue)
|
|
|
|
RCT_SIMPLE_CASE('q', long long, longLongValue)
|
|
|
|
RCT_SIMPLE_CASE('Q', unsigned long long, unsignedLongLongValue)
|
|
|
|
RCT_SIMPLE_CASE('f', float, floatValue)
|
|
|
|
RCT_SIMPLE_CASE('d', double, doubleValue)
|
|
|
|
RCT_SIMPLE_CASE('B', BOOL, boolValue)
|
2015-04-08 15:52:48 +00:00
|
|
|
|
2015-04-27 10:58:30 +00:00
|
|
|
case '{':
|
|
|
|
RCTLogMustFix(@"Cannot convert JSON to struct %s", argumentType);
|
|
|
|
break;
|
|
|
|
|
2015-04-08 15:52:48 +00:00
|
|
|
default:
|
|
|
|
defaultCase(argumentType);
|
2015-03-01 23:33:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-08 15:52:48 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
_argumentBlocks = [argumentBlocks copy];
|
|
|
|
}
|
2015-04-08 15:52:48 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)invokeWithBridge:(RCTBridge *)bridge
|
|
|
|
module:(id)module
|
|
|
|
arguments:(NSArray *)arguments
|
2015-04-20 09:09:11 +00:00
|
|
|
context:(NSNumber *)context
|
2015-03-01 23:33:55 +00:00
|
|
|
{
|
2015-04-21 12:26:51 +00:00
|
|
|
if (RCT_DEBUG) {
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-21 12:26:51 +00:00
|
|
|
// Sanity check
|
|
|
|
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
|
|
|
|
%@ on a module of class %@", _methodName, [module class]);
|
|
|
|
|
|
|
|
// Safety check
|
|
|
|
if (arguments.count != _argumentBlocks.count) {
|
|
|
|
RCTLogError(@"%@.%@ was called with %zd arguments, but expects %zd",
|
|
|
|
RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName,
|
|
|
|
arguments.count, _argumentBlocks.count);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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];
|
2015-04-08 15:52:48 +00:00
|
|
|
[invocation retainArguments];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
// Set arguments
|
|
|
|
NSUInteger index = 0;
|
|
|
|
for (id json in arguments) {
|
|
|
|
id arg = (json == [NSNull null]) ? nil : json;
|
2015-04-20 09:09:11 +00:00
|
|
|
void (^block)(RCTBridge *, NSNumber *, NSInvocation *, NSUInteger, id) = _argumentBlocks[index];
|
|
|
|
block(bridge, context, invocation, index + 2, arg);
|
2015-04-08 15:52:48 +00:00
|
|
|
index++;
|
2015-03-01 23:33:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke method
|
|
|
|
[invocation invokeWithTarget:_isClassMethod ? [module class] : module];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)description
|
|
|
|
{
|
|
|
|
return [NSString stringWithFormat:@"<%@: %p; exports %@ as %@;>", NSStringFromClass(self.class), self, _methodName, _JSMethodName];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
/**
|
|
|
|
* This function parses the exported methods inside RCTBridgeModules and
|
|
|
|
* generates an array of arrays of RCTModuleMethod objects, keyed
|
|
|
|
* by module index.
|
|
|
|
*/
|
|
|
|
static RCTSparseArray *RCTExportedMethodsByModuleID(void)
|
|
|
|
{
|
|
|
|
static RCTSparseArray *methodsByModuleID;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
|
|
|
Dl_info info;
|
|
|
|
dladdr(&RCTExportedMethodsByModuleID, &info);
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
|
|
|
|
const RCTHeaderSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExport");
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
if (section == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSArray *classes = RCTBridgeModuleClassesByModuleID();
|
|
|
|
NSMutableDictionary *methodsByModuleClassName = [NSMutableDictionary dictionaryWithCapacity:[classes count]];
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
for (RCTHeaderValue addr = section->offset;
|
2015-02-20 04:10:52 +00:00
|
|
|
addr < section->offset + section->size;
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
addr += sizeof(const char **) * 3) {
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
// Get data entry
|
|
|
|
const char **entries = (const char **)(mach_header + addr);
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
// Create method
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
RCTModuleMethod *moduleMethod;
|
|
|
|
if (entries[2] == NULL) {
|
|
|
|
|
|
|
|
// Legacy support for RCT_EXPORT()
|
|
|
|
moduleMethod = [[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
|
|
|
|
objCMethodName:@(entries[0])
|
|
|
|
JSMethodName:strlen(entries[1]) ? @(entries[1]) : nil];
|
|
|
|
} else {
|
|
|
|
moduleMethod = [[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
|
|
|
|
objCMethodName:strlen(entries[1]) ? @(entries[1]) : nil
|
|
|
|
JSMethodName:strlen(entries[2]) ? @(entries[2]) : nil];
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
// Cache method
|
|
|
|
NSArray *methods = methodsByModuleClassName[moduleMethod.moduleClassName];
|
|
|
|
methodsByModuleClassName[moduleMethod.moduleClassName] =
|
2015-04-11 22:08:00 +00:00
|
|
|
methods ? [methods arrayByAddingObject:moduleMethod] : @[moduleMethod];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
methodsByModuleID = [[RCTSparseArray alloc] initWithCapacity:[classes count]];
|
|
|
|
[classes enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
|
|
|
methodsByModuleID[moduleID] = methodsByModuleClassName[NSStringFromClass(moduleClass)];
|
|
|
|
}];
|
|
|
|
});
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
return methodsByModuleID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This constructs the remote modules configuration data structure,
|
|
|
|
* which represents the native modules and methods that will be called
|
|
|
|
* by JS. A numeric ID is assigned to each module and method, which will
|
|
|
|
* be used to communicate via the bridge. The structure of each
|
|
|
|
* module is as follows:
|
|
|
|
*
|
|
|
|
* "ModuleName1": {
|
|
|
|
* "moduleID": 0,
|
|
|
|
* "methods": {
|
|
|
|
* "methodName1": {
|
|
|
|
* "methodID": 0,
|
|
|
|
* "type": "remote"
|
|
|
|
* },
|
|
|
|
* "methodName2": {
|
|
|
|
* "methodID": 1,
|
|
|
|
* "type": "remote"
|
|
|
|
* },
|
|
|
|
* etc...
|
|
|
|
* },
|
|
|
|
* "constants": {
|
|
|
|
* ...
|
|
|
|
* }
|
|
|
|
* },
|
|
|
|
* etc...
|
|
|
|
*/
|
|
|
|
static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
|
|
|
|
{
|
|
|
|
static NSMutableDictionary *remoteModuleConfigByClassName;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
|
|
|
remoteModuleConfigByClassName = [[NSMutableDictionary alloc] init];
|
|
|
|
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
|
|
|
|
|
|
|
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
|
|
|
|
NSMutableDictionary *methodsByName = [NSMutableDictionary dictionaryWithCapacity:methods.count];
|
2015-02-24 17:06:57 +00:00
|
|
|
[methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *_stop) {
|
2015-02-20 04:10:52 +00:00
|
|
|
methodsByName[method.JSMethodName] = @{
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
@"methodID": @(methodID),
|
|
|
|
@"type": @"remote",
|
|
|
|
};
|
2015-02-20 04:10:52 +00:00
|
|
|
}];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
NSDictionary *module = @{
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
@"moduleID": @(moduleID),
|
|
|
|
@"methods": methodsByName
|
|
|
|
};
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
remoteModuleConfigByClassName[NSStringFromClass(moduleClass)] = module;
|
|
|
|
}];
|
|
|
|
});
|
|
|
|
|
|
|
|
// Create config
|
|
|
|
NSMutableDictionary *moduleConfig = [[NSMutableDictionary alloc] init];
|
|
|
|
[modulesByName enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, id<RCTBridgeModule> module, BOOL *stop) {
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
// Add constants
|
2015-02-20 04:10:52 +00:00
|
|
|
NSMutableDictionary *config = remoteModuleConfigByClassName[NSStringFromClass([module class])];
|
2015-03-01 23:33:55 +00:00
|
|
|
if ([module respondsToSelector:@selector(constantsToExport)]) {
|
|
|
|
NSDictionary *constants = [module constantsToExport];
|
|
|
|
if (constants) {
|
|
|
|
NSMutableDictionary *mutableConfig = [NSMutableDictionary dictionaryWithDictionary:config];
|
|
|
|
mutableConfig[@"constants"] = constants; // There's no real need to copy this
|
|
|
|
config = mutableConfig; // Nor this - receiver is unlikely to mutate it
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
moduleConfig[moduleName] = config;
|
|
|
|
}];
|
|
|
|
|
|
|
|
return moduleConfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* As above, but for local modules/methods, which represent JS classes
|
|
|
|
* and methods that will be called by the native code via the bridge.
|
|
|
|
* Structure is essentially the same as for remote modules:
|
|
|
|
*
|
|
|
|
* "ModuleName1": {
|
|
|
|
* "moduleID": 0,
|
|
|
|
* "methods": {
|
|
|
|
* "methodName1": {
|
|
|
|
* "methodID": 0,
|
|
|
|
* "type": "local"
|
|
|
|
* },
|
|
|
|
* "methodName2": {
|
|
|
|
* "methodID": 1,
|
|
|
|
* "type": "local"
|
|
|
|
* },
|
|
|
|
* etc...
|
|
|
|
* }
|
|
|
|
* },
|
|
|
|
* etc...
|
|
|
|
*/
|
|
|
|
static NSMutableDictionary *RCTLocalModuleIDs;
|
|
|
|
static NSMutableDictionary *RCTLocalMethodIDs;
|
2015-04-28 15:02:56 +00:00
|
|
|
static NSMutableArray *RCTLocalModuleNames;
|
|
|
|
static NSMutableArray *RCTLocalMethodNames;
|
2015-02-20 04:10:52 +00:00
|
|
|
static NSDictionary *RCTLocalModulesConfig()
|
|
|
|
{
|
|
|
|
static NSMutableDictionary *localModules;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTLocalModuleIDs = [[NSMutableDictionary alloc] init];
|
|
|
|
RCTLocalMethodIDs = [[NSMutableDictionary alloc] init];
|
2015-04-28 15:02:56 +00:00
|
|
|
RCTLocalModuleNames = [[NSMutableArray alloc] init];
|
|
|
|
RCTLocalMethodNames = [[NSMutableArray alloc] init];
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
localModules = [[NSMutableDictionary alloc] init];
|
2015-02-24 17:06:57 +00:00
|
|
|
for (NSString *moduleDotMethod in RCTJSMethods()) {
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
NSArray *parts = [moduleDotMethod componentsSeparatedByString:@"."];
|
2015-04-11 22:08:00 +00:00
|
|
|
RCTAssert(parts.count == 2, @"'%@' is not a valid JS method definition - expected 'Module.method' format.", moduleDotMethod);
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
// Add module if it doesn't already exist
|
|
|
|
NSString *moduleName = parts[0];
|
|
|
|
NSDictionary *module = localModules[moduleName];
|
|
|
|
if (!module) {
|
|
|
|
module = @{
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
@"moduleID": @(localModules.count),
|
|
|
|
@"methods": [[NSMutableDictionary alloc] init]
|
|
|
|
};
|
2015-02-20 04:10:52 +00:00
|
|
|
localModules[moduleName] = module;
|
2015-05-04 17:35:49 +00:00
|
|
|
[RCTLocalModuleNames addObject:moduleName];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
// Add method if it doesn't already exist
|
|
|
|
NSString *methodName = parts[1];
|
|
|
|
NSMutableDictionary *methods = module[@"methods"];
|
|
|
|
if (!methods[methodName]) {
|
|
|
|
methods[methodName] = @{
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
@"methodID": @(methods.count),
|
|
|
|
@"type": @"local"
|
|
|
|
};
|
2015-05-04 17:35:49 +00:00
|
|
|
[RCTLocalMethodNames addObject:methodName];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
// Add module and method lookup
|
|
|
|
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
|
|
|
|
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
|
|
|
|
}
|
|
|
|
});
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
return localModules;
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@interface RCTFrameUpdate (Private)
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
|
2015-04-15 14:07:19 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@implementation RCTFrameUpdate
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink
|
|
|
|
{
|
|
|
|
if ((self = [super init])) {
|
|
|
|
_timestamp = displayLink.timestamp;
|
|
|
|
_deltaTime = displayLink.duration;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@implementation RCTBridge
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|
|
|
|
|
|
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
|
|
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
|
|
|
launchOptions:(NSDictionary *)launchOptions
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertMainThread();
|
|
|
|
|
2015-04-15 14:07:19 +00:00
|
|
|
if ((self = [super init])) {
|
2015-05-04 17:35:49 +00:00
|
|
|
/**
|
|
|
|
* Pre register modules
|
|
|
|
*/
|
|
|
|
RCTLocalModulesConfig();
|
|
|
|
|
|
|
|
_bundleURL = bundleURL;
|
|
|
|
_moduleProvider = block;
|
|
|
|
_launchOptions = [launchOptions copy];
|
|
|
|
[self bindKeys];
|
|
|
|
[self setUp];
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)dealloc
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
/**
|
|
|
|
* This runs only on the main thread, but crashes the subclass
|
|
|
|
* RCTAssertMainThread();
|
|
|
|
*/
|
2015-05-13 15:25:00 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
2015-05-04 17:35:49 +00:00
|
|
|
[self invalidate];
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)bindKeys
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertMainThread();
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(reload)
|
|
|
|
name:RCTReloadNotification
|
|
|
|
object:nil];
|
|
|
|
|
|
|
|
#if TARGET_IPHONE_SIMULATOR
|
|
|
|
|
|
|
|
__weak RCTBridge *weakSelf = self;
|
|
|
|
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
|
|
|
|
|
|
|
// reload in current mode
|
|
|
|
[commands registerKeyCommandWithInput:@"r"
|
|
|
|
modifierFlags:UIKeyModifierCommand
|
|
|
|
action:^(UIKeyCommand *command) {
|
|
|
|
[weakSelf reload];
|
|
|
|
}];
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)reload
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-12 14:44:46 +00:00
|
|
|
/**
|
|
|
|
* AnyThread
|
|
|
|
*/
|
2015-05-04 17:35:49 +00:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[self invalidate];
|
|
|
|
[self setUp];
|
|
|
|
});
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)setUp
|
|
|
|
{
|
|
|
|
RCTAssertMainThread();
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
_batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self];
|
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-14 22:56:48 +00:00
|
|
|
- (BOOL)isLoading
|
|
|
|
{
|
|
|
|
return _batchedBridge.loading;
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (BOOL)isValid
|
|
|
|
{
|
|
|
|
return _batchedBridge.isValid;
|
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)invalidate
|
|
|
|
{
|
|
|
|
RCTAssertMainThread();
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
[_batchedBridge invalidate];
|
|
|
|
_batchedBridge = nil;
|
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
+ (void)logMessage:(NSString *)message level:(NSString *)level
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
if (!_latestJSExecutor.isValid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[_latestJSExecutor executeJSCall:@"RCTLog"
|
|
|
|
method:@"logIfNoNativeHook"
|
|
|
|
arguments:@[level, message]
|
|
|
|
context:RCTGetExecutorID(_latestJSExecutor)
|
|
|
|
callback:^(id json, NSError *error) {}];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)modules
|
|
|
|
{
|
|
|
|
return _batchedBridge.modules;
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-05-07 16:10:47 +00:00
|
|
|
- (RCTEventDispatcher *)eventDispatcher
|
|
|
|
{
|
|
|
|
return _eventDispatcher ?: _batchedBridge.eventDispatcher;
|
|
|
|
}
|
|
|
|
|
2015-05-12 14:44:38 +00:00
|
|
|
#define RCT_INNER_BRIDGE_ONLY(...) \
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)__VA_ARGS__ \
|
|
|
|
{ \
|
|
|
|
RCTLogMustFix(@"Called method \"%@\" on top level bridge. This method should \
|
|
|
|
only be called from bridge instance in a bridge module", @(__func__)); \
|
|
|
|
}
|
|
|
|
|
2015-05-12 14:44:38 +00:00
|
|
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
|
|
|
{
|
|
|
|
[self.batchedBridge enqueueJSCall:moduleDotMethod args:args];
|
|
|
|
}
|
|
|
|
|
|
|
|
RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context)
|
2015-05-04 17:35:49 +00:00
|
|
|
|
2015-04-15 14:07:19 +00:00
|
|
|
@end
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@implementation RCTBatchedBridge
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
BOOL _loading;
|
|
|
|
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTSparseArray *_modulesByID;
|
2015-04-18 17:43:20 +00:00
|
|
|
RCTSparseArray *_queuesByID;
|
2015-04-20 19:06:02 +00:00
|
|
|
dispatch_queue_t _methodQueue;
|
2015-02-24 17:06:57 +00:00
|
|
|
NSDictionary *_modulesByName;
|
2015-05-04 17:35:49 +00:00
|
|
|
CADisplayLink *_mainDisplayLink;
|
|
|
|
CADisplayLink *_jsDisplayLink;
|
2015-04-15 14:07:19 +00:00
|
|
|
NSMutableSet *_frameUpdateObservers;
|
2015-04-17 11:02:37 +00:00
|
|
|
NSMutableArray *_scheduledCalls;
|
2015-04-20 09:09:11 +00:00
|
|
|
RCTSparseArray *_scheduledCallbacks;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@synthesize valid = _valid;
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
if (self = [super init]) {
|
|
|
|
RCTAssertMainThread();
|
2015-04-26 02:18:39 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
_parentBridge = bridge;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set Initial State
|
|
|
|
*/
|
|
|
|
_valid = YES;
|
|
|
|
_loading = YES;
|
|
|
|
_frameUpdateObservers = [[NSMutableSet alloc] init];
|
|
|
|
_scheduledCalls = [[NSMutableArray alloc] init];
|
|
|
|
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
|
|
|
_queuesByID = [[RCTSparseArray alloc] init];
|
|
|
|
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize executor to allow enqueueing calls
|
|
|
|
*/
|
|
|
|
Class executorClass = self.executorClass ?: [RCTContextExecutor class];
|
|
|
|
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
|
|
|
_latestJSExecutor = _javaScriptExecutor;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Setup event dispatcher before initializing modules to allow init calls
|
|
|
|
*/
|
|
|
|
self.eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize and register bridge modules *before* adding the display link
|
|
|
|
* so we don't have threading issues
|
|
|
|
*/
|
|
|
|
_methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL);
|
|
|
|
[self registerModules];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start the application script
|
|
|
|
*/
|
|
|
|
[self initJS];
|
2015-03-26 01:59:42 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
2015-04-18 17:43:20 +00:00
|
|
|
|
2015-05-05 09:31:20 +00:00
|
|
|
- (NSURL *)bundleURL
|
|
|
|
{
|
|
|
|
return _parentBridge.bundleURL;
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (NSDictionary *)launchOptions
|
2015-03-26 01:59:42 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
return _parentBridge.launchOptions;
|
|
|
|
}
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
/**
|
|
|
|
* Override to ensure that we won't create another nested bridge
|
|
|
|
*/
|
|
|
|
- (void)setUp {}
|
|
|
|
|
|
|
|
- (void)reload
|
|
|
|
{
|
|
|
|
[_parentBridge reload];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (Class)executorClass
|
|
|
|
{
|
|
|
|
return _parentBridge.executorClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setExecutorClass:(Class)executorClass
|
|
|
|
{
|
|
|
|
RCTAssertMainThread();
|
|
|
|
|
|
|
|
_parentBridge.executorClass = executorClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isLoading
|
|
|
|
{
|
|
|
|
return _loading;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isValid
|
|
|
|
{
|
|
|
|
return _valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)registerModules
|
|
|
|
{
|
|
|
|
RCTAssertMainThread();
|
2015-04-22 14:03:55 +00:00
|
|
|
|
2015-03-26 01:59:42 +00:00
|
|
|
// Register passed-in module instances
|
|
|
|
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
2015-05-04 17:35:49 +00:00
|
|
|
for (id<RCTBridgeModule> module in _parentBridge.moduleProvider ? _parentBridge.moduleProvider() : nil) {
|
2015-04-07 14:36:26 +00:00
|
|
|
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
|
2015-03-26 01:59:42 +00:00
|
|
|
}
|
2015-02-24 17:06:57 +00:00
|
|
|
|
2015-03-26 01:59:42 +00:00
|
|
|
// Instantiate modules
|
|
|
|
_modulesByID = [[RCTSparseArray alloc] init];
|
|
|
|
NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy];
|
|
|
|
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
|
|
|
NSString *moduleName = RCTModuleNamesByID[moduleID];
|
|
|
|
// Check if module instance has already been registered for this name
|
2015-04-18 17:43:20 +00:00
|
|
|
id<RCTBridgeModule> module = modulesByName[moduleName];
|
|
|
|
if (module) {
|
2015-03-26 01:59:42 +00:00
|
|
|
// 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,
|
2015-04-18 17:43:20 +00:00
|
|
|
@"Attempted to register RCTBridgeModule class %@ for the name "
|
|
|
|
"'%@', but name was already registered by class %@", moduleClass,
|
2015-03-26 01:59:42 +00:00
|
|
|
moduleName, [modulesByName[moduleName] class]);
|
|
|
|
}
|
2015-04-18 17:43:20 +00:00
|
|
|
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]);
|
|
|
|
}
|
2015-03-26 01:59:42 +00:00
|
|
|
} else {
|
|
|
|
// Module name hasn't been used before, so go ahead and instantiate
|
2015-04-18 17:43:20 +00:00
|
|
|
module = [[moduleClass alloc] init];
|
|
|
|
}
|
|
|
|
if (module) {
|
|
|
|
// Store module instance
|
|
|
|
_modulesByID[moduleID] = modulesByName[moduleName] = module;
|
2015-03-01 23:33:55 +00:00
|
|
|
}
|
2015-03-26 01:59:42 +00:00
|
|
|
}];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-03-26 01:59:42 +00:00
|
|
|
// Store modules
|
|
|
|
_modulesByName = [modulesByName copy];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-03-26 01:59:42 +00:00
|
|
|
// Set bridge
|
|
|
|
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
|
|
|
|
if ([module respondsToSelector:@selector(setBridge:)]) {
|
|
|
|
module.bridge = self;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-20 19:06:02 +00:00
|
|
|
// Get method queues
|
2015-04-18 17:43:20 +00:00
|
|
|
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
|
|
|
|
if ([module respondsToSelector:@selector(methodQueue)]) {
|
2015-04-20 19:06:02 +00:00
|
|
|
dispatch_queue_t queue = [module methodQueue];
|
|
|
|
if (queue) {
|
|
|
|
_queuesByID[moduleID] = queue;
|
2015-04-26 02:18:39 +00:00
|
|
|
} else {
|
|
|
|
_queuesByID[moduleID] = [NSNull null];
|
2015-04-20 19:06:02 +00:00
|
|
|
}
|
2015-04-18 17:43:20 +00:00
|
|
|
}
|
2015-05-04 17:35:49 +00:00
|
|
|
|
|
|
|
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
|
|
|
[_frameUpdateObservers addObject:module];
|
|
|
|
}
|
2015-04-18 17:43:20 +00:00
|
|
|
}];
|
2015-05-04 17:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)initJS
|
|
|
|
{
|
|
|
|
RCTAssertMainThread();
|
2015-04-18 17:43:20 +00:00
|
|
|
|
2015-03-26 01:59:42 +00:00
|
|
|
// Inject module data into JS context
|
|
|
|
NSString *configJSON = RCTJSONStringify(@{
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
@"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName),
|
|
|
|
@"localModulesConfig": RCTLocalModulesConfig()
|
|
|
|
}, NULL);
|
2015-03-26 01:59:42 +00:00
|
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
2015-04-11 22:08:00 +00:00
|
|
|
[_javaScriptExecutor injectJSONText:configJSON
|
|
|
|
asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
|
|
|
|
dispatch_semaphore_signal(semaphore);
|
|
|
|
}];
|
2015-04-02 14:33:21 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
|
|
|
|
|
|
|
|
NSURL *bundleURL = _parentBridge.bundleURL;
|
2015-04-02 20:34:50 +00:00
|
|
|
if (_javaScriptExecutor == nil) {
|
2015-04-11 22:08:00 +00:00
|
|
|
|
2015-04-02 20:34:50 +00:00
|
|
|
/**
|
2015-04-11 22:08:00 +00:00
|
|
|
* HACK (tadeu): If it failed to connect to the debugger, set loading to NO
|
|
|
|
* so we can attempt to reload again.
|
2015-04-02 20:34:50 +00:00
|
|
|
*/
|
2015-04-11 22:08:00 +00:00
|
|
|
_loading = NO;
|
|
|
|
|
2015-05-12 14:44:46 +00:00
|
|
|
} 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 {
|
2015-04-11 22:08:00 +00:00
|
|
|
|
2015-04-02 14:33:21 +00:00
|
|
|
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
2015-05-04 17:35:49 +00:00
|
|
|
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
2015-05-07 11:29:31 +00:00
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
_loading = NO;
|
2015-05-04 17:35:49 +00:00
|
|
|
if (!self.isValid) {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-07 11:29:31 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
|
|
|
sourceCodeModule.scriptURL = bundleURL;
|
|
|
|
sourceCodeModule.scriptText = script;
|
2015-05-07 11:29:31 +00:00
|
|
|
if (error) {
|
2015-04-21 16:48:29 +00:00
|
|
|
|
2015-05-07 11:29:31 +00:00
|
|
|
NSArray *stack = [error userInfo][@"stack"];
|
2015-04-08 12:42:43 +00:00
|
|
|
if (stack) {
|
|
|
|
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
|
|
|
withStack:stack];
|
|
|
|
} else {
|
|
|
|
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
|
|
|
withDetails:[error localizedFailureReason]];
|
|
|
|
}
|
2015-04-21 16:48:29 +00:00
|
|
|
|
2015-05-13 15:25:27 +00:00
|
|
|
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
2015-05-07 11:29:31 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
2015-05-13 15:25:27 +00:00
|
|
|
object:_parentBridge
|
2015-05-07 11:29:31 +00:00
|
|
|
userInfo:userInfo];
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
} else {
|
2015-05-07 11:29:31 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
2015-04-21 16:48:29 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
if (!loadError) {
|
2015-05-07 11:29:31 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
/**
|
|
|
|
* Register the display link to start sending js calls after everything
|
|
|
|
* is setup
|
|
|
|
*/
|
2015-05-05 12:15:53 +00:00
|
|
|
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
|
|
|
|
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
|
2015-05-04 17:35:49 +00:00
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
|
|
|
object:_parentBridge
|
|
|
|
userInfo:@{ @"bridge": self }];
|
|
|
|
}
|
|
|
|
}];
|
2015-04-08 12:42:43 +00:00
|
|
|
}
|
|
|
|
}];
|
2015-03-26 01:59:42 +00:00
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-02-24 17:06:57 +00:00
|
|
|
- (NSDictionary *)modules
|
|
|
|
{
|
2015-04-11 22:08:00 +00:00
|
|
|
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. "
|
|
|
|
"You may be trying to access a module too early in the startup procedure.");
|
2015-02-24 17:06:57 +00:00
|
|
|
|
|
|
|
return _modulesByName;
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
#pragma mark - RCTInvalidating
|
|
|
|
|
|
|
|
- (void)invalidate
|
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
if (!self.isValid) {
|
2015-04-10 14:28:10 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertMainThread();
|
2015-04-02 14:33:21 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
_valid = NO;
|
2015-02-20 04:10:52 +00:00
|
|
|
if (_latestJSExecutor == _javaScriptExecutor) {
|
|
|
|
_latestJSExecutor = nil;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-05-07 10:53:35 +00:00
|
|
|
void (^mainThreadInvalidate)(void) = ^{
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-07 10:53:35 +00:00
|
|
|
[_mainDisplayLink invalidate];
|
|
|
|
_mainDisplayLink = nil;
|
2015-05-04 17:35:49 +00:00
|
|
|
|
|
|
|
// Invalidate modules
|
|
|
|
for (id target in _modulesByID.allObjects) {
|
|
|
|
if ([target respondsToSelector:@selector(invalidate)]) {
|
|
|
|
[(id<RCTInvalidating>)target invalidate];
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
// Release modules (breaks retain cycle if module has strong bridge reference)
|
|
|
|
_frameUpdateObservers = nil;
|
|
|
|
_modulesByID = nil;
|
|
|
|
_queuesByID = nil;
|
|
|
|
_modulesByName = nil;
|
2015-05-07 10:53:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!_javaScriptExecutor) {
|
|
|
|
|
|
|
|
// No JS thread running
|
|
|
|
mainThreadInvalidate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* JS Thread deallocations
|
|
|
|
*/
|
|
|
|
[_javaScriptExecutor invalidate];
|
|
|
|
_javaScriptExecutor = nil;
|
|
|
|
|
|
|
|
[_jsDisplayLink invalidate];
|
|
|
|
_jsDisplayLink = nil;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main Thread deallocations
|
|
|
|
*/
|
|
|
|
mainThreadInvalidate();
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
}];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* - TODO (#5906496): When we build a `MessageQueue.m`, handling all the requests could
|
|
|
|
* cause both a queue of "responses". We would flush them here. However, we
|
|
|
|
* currently just expect each objc block to handle its own response sending
|
|
|
|
* using a `RCTResponseSenderBlock`.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma mark - RCTBridge methods
|
|
|
|
|
|
|
|
/**
|
2015-04-28 15:02:56 +00:00
|
|
|
* Public. Can be invoked from any thread.
|
2015-02-20 04:10:52 +00:00
|
|
|
*/
|
|
|
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
|
|
|
{
|
|
|
|
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
|
|
|
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
|
|
|
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
|
|
|
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-28 15:02:56 +00:00
|
|
|
[self _invokeAndProcessModule:@"BatchedBridge"
|
|
|
|
method:@"callFunctionReturnFlushedQueue"
|
2015-04-30 16:50:22 +00:00
|
|
|
arguments:@[moduleID ?: @0, methodID ?: @0, args ?: @[]]
|
2015-04-28 15:02:56 +00:00
|
|
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-04-17 11:02:37 +00:00
|
|
|
/**
|
|
|
|
* Private hack to support `setTimeout(fn, 0)`
|
|
|
|
*/
|
|
|
|
- (void)_immediatelyCallTimer:(NSNumber *)timer
|
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
2015-04-17 11:02:37 +00:00
|
|
|
NSString *moduleDotMethod = @"RCTJSTimers.callTimers";
|
|
|
|
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
|
|
|
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
|
|
|
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
|
|
|
|
|
|
|
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
|
|
|
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
dispatch_block_t block = ^{
|
|
|
|
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
|
|
|
method:@"callFunctionReturnFlushedQueue"
|
|
|
|
arguments:@[moduleID, methodID, @[@[timer]]]
|
|
|
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
|
|
|
};
|
2015-04-17 11:02:37 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
|
|
|
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
|
|
|
} else {
|
|
|
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
2015-04-17 11:02:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
|
|
|
{
|
|
|
|
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
|
2015-05-04 17:35:49 +00:00
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileBeginEvent();
|
2015-05-04 17:35:49 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError);
|
2015-02-20 04:10:52 +00:00
|
|
|
if (scriptLoadError) {
|
|
|
|
onComplete(scriptLoadError);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileBeginEvent();
|
|
|
|
NSNumber *context = RCTGetExecutorID(_javaScriptExecutor);
|
2015-02-20 04:10:52 +00:00
|
|
|
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
|
|
|
|
method:@"flushedQueue"
|
|
|
|
arguments:@[]
|
2015-04-20 09:09:11 +00:00
|
|
|
context:context
|
2015-03-01 23:33:55 +00:00
|
|
|
callback:^(id json, NSError *error) {
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileEndEvent(@"FetchApplicationScriptCallbacks", @"js_call,init", @{
|
|
|
|
@"json": json ?: [NSNull null],
|
|
|
|
@"error": error ?: [NSNull null],
|
|
|
|
});
|
|
|
|
|
2015-04-20 09:09:11 +00:00
|
|
|
[self _handleBuffer:json context:context];
|
2015-04-20 11:55:05 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
onComplete(error);
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Payload Generation
|
|
|
|
|
2015-04-26 02:18:39 +00:00
|
|
|
- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
|
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
|
|
|
id queue = nil;
|
|
|
|
if (moduleID) {
|
|
|
|
queue = _queuesByID[moduleID];
|
|
|
|
}
|
|
|
|
|
2015-04-26 02:18:39 +00:00
|
|
|
if (queue == [NSNull null]) {
|
|
|
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
|
|
|
} else {
|
|
|
|
dispatch_async(queue ?: _methodQueue, block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-28 15:02:56 +00:00
|
|
|
/**
|
|
|
|
* Called by enqueueJSCall from any thread, or from _immediatelyCallTimer,
|
|
|
|
* on the JS thread, but only in non-batched mode.
|
|
|
|
*/
|
2015-04-20 09:09:11 +00:00
|
|
|
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
/**
|
|
|
|
* AnyThread
|
|
|
|
*/
|
2015-04-17 11:02:37 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
__weak RCTBatchedBridge *weakSelf = self;
|
2015-04-28 15:02:56 +00:00
|
|
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
|
|
|
RCTProfileBeginEvent();
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTBatchedBridge *strongSelf = weakSelf;
|
2015-05-02 20:12:12 +00:00
|
|
|
if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) {
|
2015-04-28 15:02:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
id call = @{
|
|
|
|
@"module": module,
|
|
|
|
@"method": method,
|
|
|
|
@"args": args,
|
|
|
|
@"context": context ?: @0,
|
|
|
|
};
|
2015-04-17 11:02:37 +00:00
|
|
|
|
2015-04-28 15:02:56 +00:00
|
|
|
if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) {
|
2015-05-02 20:12:12 +00:00
|
|
|
strongSelf->_scheduledCallbacks[args[0]] = call;
|
2015-04-28 15:02:56 +00:00
|
|
|
} else {
|
2015-05-02 20:12:12 +00:00
|
|
|
[strongSelf->_scheduledCalls addObject:call];
|
2015-04-28 15:02:56 +00:00
|
|
|
}
|
2015-04-17 11:02:37 +00:00
|
|
|
|
2015-04-28 15:02:56 +00:00
|
|
|
RCTProfileEndEvent(@"enqueue_call", @"objc_call", call);
|
|
|
|
}];
|
2015-04-17 11:02:37 +00:00
|
|
|
}
|
|
|
|
|
2015-04-20 09:09:11 +00:00
|
|
|
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
2015-04-17 11:02:37 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
2015-04-10 00:11:13 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
|
|
|
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
2015-05-04 17:35:49 +00:00
|
|
|
if (!self.isValid) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-10 00:11:13 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
2015-04-20 09:09:11 +00:00
|
|
|
[self _handleBuffer:json context:context];
|
2015-02-20 04:10:52 +00:00
|
|
|
};
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
[_javaScriptExecutor executeJSCall:module
|
|
|
|
method:method
|
|
|
|
arguments:args
|
2015-04-20 09:09:11 +00:00
|
|
|
context:context
|
2015-02-20 04:10:52 +00:00
|
|
|
callback:processResponse];
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Payload Processing
|
|
|
|
|
2015-04-20 09:09:11 +00:00
|
|
|
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if (buffer == nil || buffer == (id)kCFNull) {
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-21 16:48:29 +00:00
|
|
|
NSArray *requestsArray = [RCTConvert NSArray:buffer];
|
|
|
|
|
|
|
|
#if RCT_DEBUG
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if (![buffer isKindOfClass:[NSArray class]]) {
|
|
|
|
RCTLogError(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
NSUInteger bufferRowCount = [requestsArray count];
|
|
|
|
NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1;
|
2015-04-21 16:48:29 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if (bufferRowCount != expectedFieldsCount) {
|
|
|
|
RCTLogError(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-21 16:48:29 +00:00
|
|
|
#endif
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
|
|
|
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
|
|
|
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
NSUInteger numRequests = [moduleIDs count];
|
2015-04-21 16:48:29 +00:00
|
|
|
|
|
|
|
if (RCT_DEBUG && (numRequests != methodIDs.count || numRequests != paramsArrays.count)) {
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTLogError(@"Invalid data message - all must be length: %zd", numRequests);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-18 17:43:20 +00:00
|
|
|
// TODO: if we sort the requests by module, we could dispatch once per
|
|
|
|
// module instead of per request, which would reduce the call overhead.
|
2015-02-20 04:10:52 +00:00
|
|
|
for (NSUInteger i = 0; i < numRequests; i++) {
|
|
|
|
@autoreleasepool {
|
|
|
|
[self _handleRequestNumber:i
|
|
|
|
moduleID:[moduleIDs[i] integerValue]
|
|
|
|
methodID:[methodIDs[i] integerValue]
|
2015-04-20 09:09:11 +00:00
|
|
|
params:paramsArrays[i]
|
|
|
|
context:context];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-18 17:43:20 +00:00
|
|
|
// TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case?
|
|
|
|
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
|
|
|
|
if ([module respondsToSelector:@selector(batchDidComplete)]) {
|
2015-04-26 02:18:39 +00:00
|
|
|
[self dispatchBlock:^{
|
2015-02-20 04:10:52 +00:00
|
|
|
[module batchDidComplete];
|
2015-04-26 02:18:39 +00:00
|
|
|
} forModule:moduleID];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-04-18 17:43:20 +00:00
|
|
|
}];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
|
|
|
moduleID:(NSUInteger)moduleID
|
|
|
|
methodID:(NSUInteger)methodID
|
|
|
|
params:(NSArray *)params
|
2015-04-20 09:09:11 +00:00
|
|
|
context:(NSNumber *)context
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
|
|
|
if (!self.isValid) {
|
|
|
|
return NO;
|
|
|
|
}
|
2015-04-21 16:48:29 +00:00
|
|
|
|
|
|
|
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
// Look up method
|
2015-02-20 04:10:52 +00:00
|
|
|
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
|
2015-04-21 16:48:29 +00:00
|
|
|
|
|
|
|
if (RCT_DEBUG && methodID >= methods.count) {
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTLogError(@"Unknown methodID: %zd for module: %zd (%@)", methodID, moduleID, RCTModuleNamesByID[moduleID]);
|
|
|
|
return NO;
|
|
|
|
}
|
2015-04-21 16:48:29 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTModuleMethod *method = methods[methodID];
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
// Look up module
|
|
|
|
id module = self->_modulesByID[moduleID];
|
2015-04-21 16:48:29 +00:00
|
|
|
if (RCT_DEBUG && !module) {
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
__weak RCTBatchedBridge *weakSelf = self;
|
2015-04-26 02:18:39 +00:00
|
|
|
[self dispatchBlock:^{
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileBeginEvent();
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTBatchedBridge *strongSelf = weakSelf;
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if (!strongSelf.isValid) {
|
|
|
|
// strongSelf has been invalidated since the dispatch_async call and this
|
|
|
|
// invocation should not continue.
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-05-07 00:23:57 +00:00
|
|
|
@try {
|
2015-04-20 09:09:11 +00:00
|
|
|
[method invokeWithBridge:strongSelf module:module arguments:params context:context];
|
2015-05-07 00:23:57 +00:00
|
|
|
}
|
|
|
|
@catch (NSException *exception) {
|
|
|
|
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception);
|
|
|
|
if (!RCT_DEBUG && [exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) {
|
|
|
|
@throw exception;
|
2015-04-09 17:39:28 +00:00
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-04-20 11:55:05 +00:00
|
|
|
|
|
|
|
RCTProfileEndEvent(@"Invoke callback", @"objc_call", @{
|
|
|
|
@"module": method.moduleClassName,
|
|
|
|
@"method": method.JSMethodName,
|
|
|
|
@"selector": NSStringFromSelector(method.selector),
|
2015-05-12 14:44:38 +00:00
|
|
|
@"args": RCTJSONStringify(params ?: [NSNull null], NULL),
|
2015-04-20 11:55:05 +00:00
|
|
|
});
|
2015-04-26 02:18:39 +00:00
|
|
|
} forModule:@(moduleID)];
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
return YES;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-04-22 14:03:55 +00:00
|
|
|
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertJSThread();
|
|
|
|
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
2015-05-04 17:35:49 +00:00
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileBeginEvent();
|
2015-04-17 11:02:37 +00:00
|
|
|
|
2015-04-15 14:07:19 +00:00
|
|
|
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
|
|
|
|
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
|
|
|
|
if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) {
|
2015-05-04 17:35:49 +00:00
|
|
|
[self dispatchBlock:^{
|
|
|
|
[observer didUpdateFrame:frameUpdate];
|
|
|
|
} forModule:RCTModuleIDsByName[RCTBridgeModuleNameForClass([observer class])]];
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-17 11:02:37 +00:00
|
|
|
|
2015-04-20 09:09:11 +00:00
|
|
|
NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls];
|
|
|
|
NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor);
|
|
|
|
calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) {
|
|
|
|
return [call[@"context"] isEqualToNumber:currentExecutorID];
|
|
|
|
}]];
|
2015-04-17 11:02:37 +00:00
|
|
|
if (calls.count > 0) {
|
|
|
|
_scheduledCalls = [[NSMutableArray alloc] init];
|
2015-04-20 09:09:11 +00:00
|
|
|
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
2015-04-17 11:02:37 +00:00
|
|
|
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
2015-04-20 09:09:11 +00:00
|
|
|
method:@"processBatch"
|
|
|
|
arguments:@[calls]
|
|
|
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
2015-04-17 11:02:37 +00:00
|
|
|
}
|
|
|
|
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertMainThread();
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)startProfiling
|
2015-04-02 14:33:21 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertMainThread();
|
2015-04-02 14:33:21 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
|
|
|
|
RCTLogError(@"To run the profiler you must be running from the dev server");
|
2015-02-20 04:10:52 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
[_mainDisplayLink invalidate];
|
|
|
|
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
|
|
|
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
RCTProfileInit();
|
2015-04-17 11:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)stopProfiling
|
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
RCTAssertMainThread();
|
|
|
|
|
|
|
|
[_mainDisplayLink invalidate];
|
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
NSString *log = RCTProfileEnd();
|
2015-05-04 17:35:49 +00:00
|
|
|
NSURL *bundleURL = _parentBridge.bundleURL;
|
|
|
|
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
|
2015-04-17 11:02:37 +00:00
|
|
|
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:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
|
|
if (error) {
|
|
|
|
RCTLogError(@"%@", error.localizedDescription);
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
[task resume];
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
@end
|