Updates from Fri Feb 20

- [react-packager][streamline oss] Move open sourced JS source to react-native-github | Spencer Ahrens
- [react-packager] Bump version | Amjad Masad
- Converted remaining modules and removed RKBridgeModule | Nick Lockwood
This commit is contained in:
Spencer Ahrens 2015-02-20 10:33:17 -08:00
parent 3abf4fb6ae
commit 11eafb5563
7 changed files with 234 additions and 182 deletions

View File

@ -34,9 +34,11 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
/**
* The designated initializer. This creates a new bridge on top of the specified
* executor. The bridge should then be used for all subsequent communication
* with the JavaScript code running in the executor.
* with the JavaScript code running in the executor. You can optionally pass in
* a list of module instances to be used instead of the auto-instantiated versions.
*/
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
moduleInstances:(NSArray *)moduleInstances NS_DESIGNATED_INITIALIZER;
/**
* This method is used to call functions in the JavaScript application context.

View File

@ -12,6 +12,7 @@
#import "RCTInvalidating.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTSparseArray.h"
#import "RCTUtils.h"
/**
@ -81,18 +82,83 @@ typedef struct section RCTExportSection;
#endif
/**
* This function parses the exported methods inside RCTBridgeModules and
* generates a dictionary of arrays of RCTModuleMethod objects, keyed
* by module name.
* This function returns the module name for a given class.
*/
static NSDictionary *RCTExportedMethodsByModule(void)
static NSString *RCTModuleNameForClass(Class cls)
{
static NSMutableDictionary *methodsByModule;
return [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
}
/**
* This function instantiates a new module instance.
*/
static id<RCTBridgeModule> RCTCreateModuleInstance(Class cls, RCTBridge *bridge)
{
if ([cls instancesRespondToSelector:@selector(initWithBridge:)]) {
return [[cls alloc] initWithBridge:bridge];
} else {
return [[cls alloc] init];
}
}
/**
* This function scans all classes available at runtime and returns an array
* of all classes that implement the RTCBridgeModule protocol.
*/
static NSArray *RCTModuleNamesByID;
static NSArray *RCTBridgeModuleClassesByModuleID(void)
{
static NSArray *modules;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
modules = [NSMutableArray array];
RCTModuleNamesByID = [NSMutableArray array];
unsigned int classCount;
Class *classes = objc_copyClassList(&classCount);
for (unsigned int i = 0; i < classCount; i++) {
Class cls = classes[i];
if (!class_getSuperclass(cls)) {
// Class has no superclass - it's probably something weird
continue;
}
if (![cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
// Not an RCTBridgeModule
continue;
}
// Add module
[(NSMutableArray *)modules addObject:cls];
// Add module name
NSString *moduleName = RCTModuleNameForClass(cls);
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
}
free(classes);
modules = [modules copy];
RCTModuleNamesByID = [RCTModuleNamesByID copy];
});
return modules;
}
/**
* 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(&RCTExportedMethodsByModule, &info);
dladdr(&RCTExportedMethodsByModuleID, &info);
const RCTExportValue mach_header = (RCTExportValue)info.dli_fbase;
const RCTExportSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExport");
@ -101,7 +167,8 @@ static NSDictionary *RCTExportedMethodsByModule(void)
return;
}
methodsByModule = [NSMutableDictionary dictionary];
NSArray *classes = RCTBridgeModuleClassesByModuleID();
NSMutableDictionary *methodsByModuleClassName = [NSMutableDictionary dictionaryWithCapacity:[classes count]];
NSCharacterSet *plusMinusCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"+-"];
for (RCTExportValue addr = section->offset;
@ -122,11 +189,11 @@ static NSDictionary *RCTExportedMethodsByModule(void)
NSString *selectorName;
if (![scanner scanUpToString:@"]" intoString:&selectorName]) continue;
Class class = NSClassFromString(className);
if (class == Nil) continue;
Class moduleClass = NSClassFromString(className);
if (moduleClass == Nil) continue;
SEL selector = NSSelectorFromString(selectorName);
Method method = ([plusMinus characterAtIndex:0] == '+' ? class_getClassMethod : class_getInstanceMethod)(class, selector);
Method method = ([plusMinus characterAtIndex:0] == '+' ? class_getClassMethod : class_getInstanceMethod)(moduleClass, selector);
if (method == nil) continue;
unsigned int argumentCount = method_getNumberOfArguments(method);
@ -147,56 +214,17 @@ static NSDictionary *RCTExportedMethodsByModule(void)
arity:method_getNumberOfArguments(method) - 2
blockArgumentIndexes:blockArgumentIndexes];
NSString *moduleName = [class respondsToSelector:@selector(moduleName)] ? [class moduleName] : className;
NSArray *moduleMap = methodsByModule[moduleName];
methodsByModule[moduleName] = (moduleMap != nil) ? [moduleMap arrayByAddingObject:moduleMethod] : @[moduleMethod];
NSArray *methods = methodsByModuleClassName[className];
methodsByModuleClassName[className] = methods ? [methods arrayByAddingObject:moduleMethod] : @[moduleMethod];
}
methodsByModuleID = [[RCTSparseArray alloc] initWithCapacity:[classes count]];
[classes enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
methodsByModuleID[moduleID] = methodsByModuleClassName[NSStringFromClass(moduleClass)];
}];
});
return methodsByModule;
}
/**
* This function scans all classes available at runtime and returns a dictionary
* of classes that implement the RTCBridgeModule protocol, keyed by module name.
*/
static NSDictionary *RCTBridgeModuleClasses(void)
{
static NSMutableDictionary *modules;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
modules = [NSMutableDictionary dictionary];
unsigned int classCount;
Class *classes = objc_copyClassList(&classCount);
for (unsigned int i = 0; i < classCount; i++) {
Class cls = classes[i];
if (!class_getSuperclass(cls)) {
// Class has no superclass - it's probably something weird
continue;
}
if (![cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
// Not an RCTBridgeModule
continue;
}
// Get module name
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
// Check module name is unique
id existingClass = modules[moduleName];
RCTCAssert(existingClass == Nil, @"Attempted to register RCTBridgeModule class %@ for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
modules[moduleName] = cls;
}
free(classes);
});
return modules;
return methodsByModuleID;
}
/**
@ -225,48 +253,58 @@ static NSDictionary *RCTBridgeModuleClasses(void)
* },
* etc...
*/
static NSMutableDictionary *RCTRemoteModulesByID;
static NSDictionary *RCTRemoteModulesConfig()
static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
{
static NSMutableDictionary *remoteModules;
static NSMutableDictionary *remoteModuleConfigByClassName;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTRemoteModulesByID = [[NSMutableDictionary alloc] init];
remoteModuleConfigByClassName = [[NSMutableDictionary alloc] init];
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
remoteModules = [[NSMutableDictionary alloc] init];
[RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
NSArray *rawMethods = RCTExportedMethodsByModule()[moduleName];
NSMutableDictionary *methods = [NSMutableDictionary dictionaryWithCapacity:rawMethods.count];
[rawMethods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop) {
methods[method.JSMethodName] = @{
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
NSMutableDictionary *methodsByName = [NSMutableDictionary dictionaryWithCapacity:methods.count];
[methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop) {
methodsByName[method.JSMethodName] = @{
@"methodID": @(methodID),
@"type": @"remote",
};
}];
NSDictionary *module = @{
@"moduleID": @(remoteModules.count),
@"methods": methods
@"moduleID": @(moduleID),
@"methods": methodsByName
};
// Add static constants
if (RCTClassOverridesClassMethod(moduleClass, @selector(constantsToExport))) {
NSDictionary *constants = [moduleClass constantsToExport];
if (constants.count) {
module = [module mutableCopy];
((NSMutableDictionary *)module)[@"constants"] = constants;
NSMutableDictionary *mutableModule = [module mutableCopy];
mutableModule[@"constants"] = [moduleClass constantsToExport] ?: @{};
module = [mutableModule copy];
}
}
remoteModules[moduleName] = [module copy];
// Add module lookup
RCTRemoteModulesByID[module[@"moduleID"]] = moduleName;
remoteModuleConfigByClassName[NSStringFromClass(moduleClass)] = module;
}];
});
return remoteModules;
// Create config
NSMutableDictionary *moduleConfig = [[NSMutableDictionary alloc] init];
[modulesByName enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, id<RCTBridgeModule> module, BOOL *stop) {
// Add "psuedo-constants"
NSMutableDictionary *config = remoteModuleConfigByClassName[NSStringFromClass([module class])];
if (RCTClassOverridesInstanceMethod([module class], @selector(constantsToExport))) {
NSMutableDictionary *mutableConfig = [NSMutableDictionary dictionaryWithDictionary:config];
NSMutableDictionary *mutableConstants = [NSMutableDictionary dictionaryWithDictionary:config[@"constants"]];
[mutableConstants addEntriesFromDictionary:[module constantsToExport]];
mutableConfig[@"constants"] = mutableConstants; // There's no real need to copy this
config = mutableConfig; // Nor this - receiver is unlikely to mutate it
}
moduleConfig[moduleName] = config;
}];
return moduleConfig;
}
/**
@ -317,7 +355,7 @@ static NSDictionary *RCTLocalModulesConfig()
// @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
// Register individual methods from modules
for (Class cls in RCTBridgeModuleClasses().allValues) {
for (Class cls in RCTBridgeModuleClassesByModuleID()) {
if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
[JSMethods addObjectsFromArray:[cls JSMethods]];
}
@ -361,13 +399,15 @@ static NSDictionary *RCTLocalModulesConfig()
@implementation RCTBridge
{
NSMutableDictionary *_moduleInstances;
RCTSparseArray *_modulesByID;
NSMutableDictionary *_modulesByName;
id<RCTJavaScriptExecutor> _javaScriptExecutor;
}
static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
moduleInstances:(NSArray *)moduleInstances
{
if ((self = [super init])) {
_javaScriptExecutor = javaScriptExecutor;
@ -375,26 +415,36 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
// Register passed-in module instances
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
for (id<RCTBridgeModule> module in moduleInstances) {
preregisteredModules[RCTModuleNameForClass([module class])] = module;
}
// Instantiate modules
_moduleInstances = [[NSMutableDictionary alloc] init];
[RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
if (_moduleInstances[moduleName] == nil) {
id<RCTBridgeModule> moduleInstance;
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
moduleInstance = [[moduleClass alloc] initWithBridge:self];
_modulesByID = [[RCTSparseArray alloc] init];
_modulesByName = [[NSMutableDictionary alloc] initWithDictionary:preregisteredModules];
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
NSString *moduleName = RCTModuleNamesByID[moduleID];
// Check if module instance has already been registered for this name
if (_modulesByName[moduleName] != nil) {
// 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(RCTCreateModuleInstance(moduleClass, self) == nil,
@"Attempted to register RCTBridgeModule class %@ for the name '%@', \
but name was already registered by class %@", moduleClass,
moduleName, [_modulesByName[moduleName] class]);
}
} else {
moduleInstance = [[moduleClass alloc] init];
}
if (moduleInstance) {
// If nil, the module doesn't support auto-instantiation
_moduleInstances[moduleName] = moduleInstance;
}
// Module name hasn't been used before, so go ahead and instantiate
_modulesByID[moduleID] = _modulesByName[moduleName] = RCTCreateModuleInstance(moduleClass, self);
}
}];
// Inject module data into JS context
NSString *configJSON = RCTJSONStringify(@{
@"remoteModuleConfig": RCTRemoteModulesConfig(),
@"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName),
@"localModulesConfig": RCTLocalModulesConfig()
}, NULL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
@ -427,6 +477,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
if (_latestJSExecutor == _javaScriptExecutor) {
_latestJSExecutor = nil;
}
[_javaScriptExecutor invalidate];
_javaScriptExecutor = nil;
dispatch_sync(_shadowQueue, ^{
@ -434,12 +485,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
// TODO: is this still needed?
});
for (id target in _moduleInstances.objectEnumerator) {
for (id target in _modulesByID.allObjects) {
if ([target respondsToSelector:@selector(invalidate)]) {
[(id<RCTInvalidating>)target invalidate];
}
}
[_moduleInstances removeAllObjects];
[_modulesByID removeAllObjects];
}
/**
@ -457,11 +508,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
{
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
RCTAssert(moduleID, @"Module '%@' not registered.",
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
RCTAssert(methodID, @"Method '%@' not registered.", moduleDotMethod);
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
[self _invokeAndProcessModule:@"BatchedBridge"
method:@"callFunctionReturnFlushedQueue"
@ -564,7 +615,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
for (NSUInteger i = 0; i < numRequests; i++) {
@autoreleasepool {
[self _handleRequestNumber:i
moduleID:moduleIDs[i]
moduleID:[moduleIDs[i] integerValue]
methodID:[methodIDs[i] integerValue]
params:paramsArrays[i]];
}
@ -572,17 +623,17 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
// TODO: only used by RCTUIManager - can we eliminate this special case?
dispatch_async(_shadowQueue, ^{
for (id target in _moduleInstances.objectEnumerator) {
if ([target respondsToSelector:@selector(batchDidComplete)]) {
[target batchDidComplete];
for (id module in _modulesByID.allObjects) {
if ([module respondsToSelector:@selector(batchDidComplete)]) {
[module batchDidComplete];
}
}
});
}
- (BOOL)_handleRequestNumber:(NSUInteger)i
moduleID:(NSNumber *)moduleID
methodID:(NSInteger)methodID
moduleID:(NSUInteger)moduleID
methodID:(NSUInteger)methodID
params:(NSArray *)params
{
if (![params isKindOfClass:[NSArray class]]) {
@ -590,15 +641,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return NO;
}
NSString *moduleName = RCTRemoteModulesByID[moduleID];
if (!moduleName) {
RCTLogError(@"Unknown moduleID: %@", moduleID);
return NO;
}
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
if (methodID >= methods.count) {
RCTLogError(@"Unknown methodID: %zd for module: %@", methodID, moduleName);
RCTLogError(@"Unknown methodID: %zd for module: %zd (%@)", methodID, moduleID, RCTModuleNamesByID[moduleID]);
return NO;
}
@ -608,7 +653,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
RCTLogError(@"Expected %tu arguments but got %tu invoking %@.%@",
methodArity,
params.count,
moduleName,
RCTModuleNamesByID[moduleID],
method.JSMethodName);
return NO;
}
@ -624,8 +669,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}
// TODO: we should just store module instances by index, since that's how we look them up anyway
id target = strongSelf->_moduleInstances[moduleName];
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
id target = strongSelf->_modulesByID[moduleID];
RCTAssert(target != nil, @"No module found for name '%@'", RCTModuleNamesByID[moduleID]);
SEL selector = method.selector;
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
@ -753,9 +798,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)registerRootView:(RCTRootView *)rootView
{
// TODO: only used by RCTUIManager - can we eliminate this special case?
for (id target in _moduleInstances.objectEnumerator) {
if ([target respondsToSelector:@selector(registerRootView:)]) {
[target registerRootView:rootView];
for (id module in _modulesByID.allObjects) {
if ([module respondsToSelector:@selector(registerRootView:)]) {
[module registerRootView:rootView];
}
}
}

View File

@ -79,14 +79,12 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
((CGFloat *)&result)[i] = [json[i] doubleValue]; \
} \
} \
} else { \
if (![json isKindOfClass:[NSDictionary class]]) { \
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
} else { \
} else if ([json isKindOfClass:[NSDictionary class]]) { \
for (NSUInteger i = 0; i < count; i++) { \
((CGFloat *)&result)[i] = [json[fields[i]] doubleValue]; \
} \
} \
} else if (json) { \
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
} \
return result; \
} \

View File

@ -125,7 +125,7 @@ static Class _globalExecutorClass;
// Choose local executor if specified, followed by global, followed by default
_executor = [[_executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class] alloc] init];
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor];
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor moduleInstances:nil];
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[self addGestureRecognizer:_touchHandler];

View File

@ -26,4 +26,6 @@
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSNumber *idx, BOOL *stop))block;
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, NSNumber *idx, BOOL *stop))block;
- (void)removeAllObjects;
@end

View File

@ -98,6 +98,11 @@
}];
}
- (void)removeAllObjects
{
[_storage removeAllObjects];
}
- (id)copyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithSparseArray:self];

View File

@ -1,6 +1,6 @@
{
"name": "react-packager",
"version": "0.0.2",
"version": "0.1.0",
"description": "",
"main": "index.js",
"jest": {