Use arrays for module method data

Summary: public

Use arrays instead of dictionaries for encoding module method information.

This further reduces UIExplorer startup JSON from 16104 bytes to 14119 (12% reduction)

Reviewed By: javache

Differential Revision: D2570057

fb-gh-sync-id: 4a53a9ead4365a136e7caeb650375733e1c24c0e
This commit is contained in:
Nick Lockwood 2015-10-23 10:14:26 -07:00 committed by facebook-github-bot-8
parent dad45aa8f2
commit cae4761006
7 changed files with 161 additions and 79 deletions

View File

@ -136,19 +136,25 @@ _Pragma("clang diagnostic pop")
NSString *injectedStuff;
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
NSDictionary *constants = testModuleConfig[@"constants"];
NSDictionary *methods = testModuleConfig[@"methods"];
__block NSNumber *testModuleID = nil;
__block NSDictionary *testConstants = nil;
__block NSNumber *testMethodID = nil;
NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"];
[remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, BOOL *stop) {
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"TestModule"]) {
testModuleID = @(i);
testConstants = moduleConfig[1];
testMethodID = @([moduleConfig[2] indexOfObject:@"testMethod"]);
*stop = YES;
}
}];
XCTAssertNotNil(moduleConfig);
XCTAssertNotNil(remoteModuleConfig);
XCTAssertNotNil(testModuleConfig);
XCTAssertNotNil(constants);
XCTAssertEqualObjects(constants[@"eleventyMillion"], @42);
XCTAssertNotNil(methods);
XCTAssertNotNil(methods[@"testMethod"]);
XCTAssertNotNil(testModuleID);
XCTAssertNotNil(testConstants);
XCTAssertEqualObjects(testConstants[@"eleventyMillion"], @42);
XCTAssertNotNil(testMethodID);
}
- (void)testCallNativeMethod
@ -158,13 +164,19 @@ _Pragma("clang diagnostic pop")
NSString *injectedStuff;
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
NSNumber *testModuleID = testModuleConfig[@"moduleID"];
NSDictionary *methods = testModuleConfig[@"methods"];
NSDictionary *testMethod = methods[@"testMethod"];
NSNumber *testMethodID = testMethod[@"methodID"];
__block NSNumber *testModuleID = nil;
__block NSDictionary *testConstants = nil;
__block NSNumber *testMethodID = nil;
NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"];
[remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, __unused BOOL *stop) {
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"TestModule"]) {
testModuleID = @(i);
testConstants = moduleConfig[1];
testMethodID = @([moduleConfig[2] indexOfObject:@"testMethod"]);
*stop = YES;
}
}];
NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42];
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567];

View File

@ -30,7 +30,6 @@ let MIN_TIME_BETWEEN_FLUSHES_MS = 5;
let SPY_MODE = false;
let MethodTypes = keyMirror({
local: null,
remote: null,
remoteAsync: null,
});
@ -62,15 +61,18 @@ class MessageQueue {
'flushedQueue',
].forEach((fn) => this[fn] = this[fn].bind(this));
this._genModules(remoteModules);
let modulesConfig = this._genModulesConfig(remoteModules);
this._genModules(modulesConfig);
localModules && this._genLookupTables(
localModules, this._moduleTable, this._methodTable);
this._genModulesConfig(localModules),this._moduleTable, this._methodTable
);
this._debugInfo = {};
this._remoteModuleTable = {};
this._remoteMethodTable = {};
this._genLookupTables(
remoteModules, this._remoteModuleTable, this._remoteMethodTable);
modulesConfig, this._remoteModuleTable, this._remoteMethodTable
);
}
/**
@ -182,43 +184,118 @@ class MessageQueue {
/**
* Private helper methods
*/
_genLookupTables(localModules, moduleTable, methodTable) {
let moduleNames = Object.keys(localModules);
for (var i = 0, l = moduleNames.length; i < l; i++) {
let moduleName = moduleNames[i];
let methods = localModules[moduleName].methods || {};
let moduleID = localModules[moduleName].moduleID;
/**
* Converts the old, object-based module structure to the new
* array-based structure. TODO (t8823865) Removed this
* functin once Android has been updated.
*/
_genModulesConfig(modules /* array or object */) {
if (Array.isArray(modules)) {
return modules;
} else {
let moduleArray = [];
let moduleNames = Object.keys(modules);
for (var i = 0, l = moduleNames.length; i < l; i++) {
let moduleName = moduleNames[i];
let moduleConfig = modules[moduleName];
let module = [moduleName];
if (moduleConfig.constants) {
module.push(moduleConfig.constants);
}
let methodsConfig = moduleConfig.methods;
if (methodsConfig) {
let methods = [];
let asyncMethods = [];
let methodNames = Object.keys(methodsConfig);
for (var j = 0, ll = methodNames.length; j < ll; j++) {
let methodName = methodNames[j];
let methodConfig = methodsConfig[methodName];
methods[methodConfig.methodID] = methodName;
if (methodConfig.type === MethodTypes.remoteAsync) {
asyncMethods.push(methodConfig.methodID);
}
}
if (methods.length) {
module.push(methods);
if (asyncMethods.length) {
module.push(asyncMethods);
}
}
}
moduleArray[moduleConfig.moduleID] = module;
}
return moduleArray;
}
}
_genLookupTables(modulesConfig, moduleTable, methodTable) {
for (var moduleID = 0, l = modulesConfig.length; moduleID < l; moduleID++) {
let module = modulesConfig[moduleID];
if (!module) {
continue;
}
let moduleName = module[0];
moduleTable[moduleID] = moduleName;
methodTable[moduleID] = {};
let methodNames = Object.keys(methods);
for (var j = 0, k = methodNames.length; j < k; j++) {
let methodName = methodNames[j];
let methodConfig = methods[methodName];
methodTable[moduleID][methodConfig.methodID] = methodName;
if (module.length > 1) {
let methodsIndex = 1;
if (!Array.isArray(module[1])) {
methodsIndex = 2;
}
if (module.length > methodsIndex) {
let methods = module[methodsIndex];
for (var methodID = 0, ll = methods.length; methodID < ll; methodID++) {
methodTable[moduleID][methodID] = methods[methodID];
}
}
}
}
}
_genModules(remoteModules) {
let moduleNames = Object.keys(remoteModules);
for (var i = 0, l = moduleNames.length; i < l; i++) {
let moduleName = moduleNames[i];
let moduleConfig = remoteModules[moduleName];
for (var i = 0, l = remoteModules.length; i < l; i++) {
let module = remoteModules[i];
if (!module) {
continue;
}
let moduleName = module[0];
let constants = null;
let methods = null;
let asyncMethods = null;
if (module.length > 0) {
let methodsIndex = 1;
if (!Array.isArray(module[1])) {
constants = module[1];
methodsIndex = 2;
}
if (module.length > methodsIndex) {
methods = module[methodsIndex];
if (module.length > methodsIndex) {
asyncMethods = module[methodsIndex];
}
}
}
let moduleConfig = {
moduleID: i,
constants,
methods,
asyncMethods,
};
this.RemoteModules[moduleName] = this._genModule({}, moduleConfig);
}
}
_genModule(module, moduleConfig) {
let methods = moduleConfig.methods || {};
let methodNames = Object.keys(methods);
for (var i = 0, l = methodNames.length; i < l; i++) {
let methodName = methodNames[i];
let methodConfig = methods[methodName];
let methods = moduleConfig.methods || [];
let asyncMethods = moduleConfig.asyncMethods || [];
for (var methodID = 0, l = methods.length; methodID < l; methodID++) {
let methodName = methods[methodID];
let isAsync = (asyncMethods.indexOf(methodID) !== -1);
module[methodName] = this._genMethod(
moduleConfig.moduleID,
methodConfig.methodID,
methodConfig.type || MethodTypes.remote
methodID,
isAsync ? MethodTypes.remoteAsync : MethodTypes.remote
);
}
Object.assign(module, moduleConfig.constants);
@ -226,10 +303,6 @@ class MessageQueue {
}
_genMethod(module, method, type) {
if (type === MethodTypes.local) {
return null;
}
let fn = null;
let self = this;
if (type === MethodTypes.remoteAsync) {

View File

@ -308,12 +308,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
- (NSString *)moduleConfig
{
NSMutableDictionary *config = [NSMutableDictionary new];
NSMutableArray *config = [NSMutableArray new];
for (RCTModuleData *moduleData in _moduleDataByID) {
NSDictionary *moduleConfig = moduleData.config;
if (moduleConfig) {
config[moduleData.name] = moduleConfig;
}
[config addObject:moduleData.config];
if ([moduleData.instance conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
[_frameUpdateObservers addObject:moduleData];

View File

@ -20,7 +20,7 @@
@property (nonatomic, strong, readonly) Class moduleClass;
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, copy, readonly) NSArray *methods;
@property (nonatomic, copy, readonly) NSDictionary *config;
@property (nonatomic, copy, readonly) NSArray *config;
@property (nonatomic, strong) dispatch_queue_t queue;

View File

@ -12,6 +12,7 @@
#import "RCTBridge.h"
#import "RCTModuleMethod.h"
#import "RCTLog.h"
#import "RCTUtils.h"
@implementation RCTModuleData
{
@ -79,37 +80,36 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
return _methods;
}
- (NSDictionary *)config
- (NSArray *)config
{
if (_constants.count == 0 && self.methods.count == 0) {
return nil; // Nothing to export
return (id)kCFNull; // Nothing to export
}
NSMutableDictionary *config = [NSMutableDictionary new];
config[@"moduleID"] = _moduleID;
if (_constants) {
config[@"constants"] = _constants;
}
NSMutableDictionary *methodconfig = [NSMutableDictionary new];
[self.methods enumerateObjectsUsingBlock:^(id<RCTBridgeMethod> method, NSUInteger idx, __unused BOOL *stop) {
NSMutableArray *methods = self.methods.count ? [NSMutableArray new] : nil;
NSMutableArray *asyncMethods = nil;
for (id<RCTBridgeMethod> method in self.methods) {
[methods addObject:method.JSMethodName];
if (method.functionType == RCTFunctionTypePromise) {
methodconfig[method.JSMethodName] = @{
@"methodID": @(idx),
@"type": @"remoteAsync",
};
} else {
methodconfig[method.JSMethodName] = @{
@"methodID": @(idx),
};
if (!asyncMethods) {
asyncMethods = [NSMutableArray new];
}
[asyncMethods addObject:@(methods.count)];
}
}];
if (methodconfig.count) {
config[@"methods"] = [methodconfig copy];
}
return [config copy];
NSMutableArray *config = [NSMutableArray new];
[config addObject:_name];
if (_constants.count) {
[config addObject:_constants];
}
if (methods) {
[config addObject:methods];
if (asyncMethods) {
[config addObject:asyncMethods];
}
}
return config;
}
- (dispatch_queue_t)queue

View File

@ -31,7 +31,6 @@ typedef NS_ENUM(NSUInteger, RCTNullability) {
@property (nonatomic, readonly) Class moduleClass;
@property (nonatomic, readonly) SEL selector;
@property (nonatomic, readonly) RCTFunctionType functionType;
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
JSMethodName:(NSString *)JSMethodName

View File

@ -54,6 +54,7 @@ typedef BOOL (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id);
}
@synthesize JSMethodName = _JSMethodName;
@synthesize functionType = _functionType;
static void RCTLogArgumentError(RCTModuleMethod *method, NSUInteger index,
id valueOrType, const char *issue)