Use JSC C API for better invocation speed

Summary:
This converts the existing JSEvaluateScript call for `require('<ModuleName>').<MethodName>.apply(null, <args>);` to native JSC C API methods which shaves off about 10-15% of invocation time on average, I used pretty primitive profiling methods to track the minimum, maximum and average invocation time so would appreciate any extra eyes on the performance.

If the argument count is zero the method is invoked directly with no arguments, if the argument count is 1 it's invoked directly with just that argument. If there is more than 1 argument then apply is used and the arguments are passed as the second parameter.

Ensured all existing tests pass and instruments leaks shows nothing is leaking.
Closes https://github.com/facebook/react-native/pull/1037
Github Author: Robert Payne <robertpayne@me.com>

Test Plan: Imported from GitHub, without a `Test Plan:` line.
This commit is contained in:
Robert Payne 2015-05-22 19:33:21 -07:00
Родитель 82a082a794
Коммит c91e2eb567
1 изменённых файлов: 67 добавлений и 10 удалений

Просмотреть файл

@ -291,21 +291,78 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
return;
}
NSError *error;
NSString *argsString = RCTJSONStringify(arguments, &error);
NSString *argsString = (arguments.count == 1) ? RCTJSONStringify(arguments[0], &error) : RCTJSONStringify(arguments, &error);
if (!argsString) {
RCTLogError(@"Cannot convert argument to string: %@", error);
onComplete(nil, error);
return;
}
NSString *execString = [NSString stringWithFormat:@"require('%@').%@.apply(null, %@);", name, method, argsString];
JSValueRef jsError = NULL;
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)execString);
JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, NULL, 0, &jsError);
JSStringRelease(execJSString);
JSValueRef errorJSRef = NULL;
JSValueRef resultJSRef = NULL;
JSGlobalContextRef contextJSRef = JSContextGetGlobalContext(strongSelf->_context.ctx);
JSObjectRef globalObjectJSRef = JSContextGetGlobalObject(strongSelf->_context.ctx);
if (!result) {
onComplete(nil, RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError));
// get require
JSStringRef requireNameJSStringRef = JSStringCreateWithUTF8CString("require");
JSValueRef requireJSRef = JSObjectGetProperty(contextJSRef, globalObjectJSRef, requireNameJSStringRef, &errorJSRef);
JSStringRelease(requireNameJSStringRef);
if (requireJSRef != NULL && errorJSRef == NULL) {
// get module
JSStringRef moduleNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)name);
JSValueRef moduleNameJSRef = JSValueMakeString(contextJSRef, moduleNameJSStringRef);
JSValueRef moduleJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)requireJSRef, NULL, 1, (const JSValueRef *)&moduleNameJSRef, &errorJSRef);
JSStringRelease(moduleNameJSStringRef);
if (moduleJSRef != NULL && errorJSRef == NULL) {
// get method
JSStringRef methodNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)method);
JSValueRef methodJSRef = JSObjectGetProperty(contextJSRef, (JSObjectRef)moduleJSRef, methodNameJSStringRef, &errorJSRef);
JSStringRelease(methodNameJSStringRef);
if (methodJSRef != NULL && errorJSRef == NULL) {
// direct method invoke with no arguments
if (arguments.count == 0) {
resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, 0, NULL, &errorJSRef);
}
// direct method invoke with 1 argument
else if(arguments.count == 1) {
JSStringRef argsJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)argsString);
JSValueRef argsJSRef = JSValueMakeFromJSONString(contextJSRef, argsJSStringRef);
resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, 1, &argsJSRef, &errorJSRef);
JSStringRelease(argsJSStringRef);
} else {
// apply invoke with array of arguments
// get apply
JSStringRef applyNameJSStringRef = JSStringCreateWithUTF8CString("apply");
JSValueRef applyJSRef = JSObjectGetProperty(contextJSRef, (JSObjectRef)methodJSRef, applyNameJSStringRef, &errorJSRef);
JSStringRelease(applyNameJSStringRef);
if (applyJSRef != NULL && errorJSRef == NULL) {
// invoke apply
JSStringRef argsJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)argsString);
JSValueRef argsJSRef = JSValueMakeFromJSONString(contextJSRef, argsJSStringRef);
JSValueRef args[2];
args[0] = JSValueMakeNull(contextJSRef);
args[1] = argsJSRef;
resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)applyJSRef, (JSObjectRef)methodJSRef, 2, args, &errorJSRef);
JSStringRelease(argsJSStringRef);
}
}
}
}
}
if (!resultJSRef) {
onComplete(nil, RCTNSErrorFromJSError(contextJSRef, errorJSRef));
return;
}
@ -315,8 +372,8 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
id objcValue;
// We often return `null` from JS when there is nothing for native side. JSONKit takes an extra hundred microseconds
// to handle this simple case, so we are adding a shortcut to make executeJSCall method even faster
if (!JSValueIsNull(strongSelf->_context.ctx, result)) {
JSStringRef jsJSONString = JSValueCreateJSONString(strongSelf->_context.ctx, result, 0, nil);
if (!JSValueIsNull(contextJSRef, resultJSRef)) {
JSStringRef jsJSONString = JSValueCreateJSONString(contextJSRef, resultJSRef, 0, nil);
if (jsJSONString) {
NSString *objcJSONString = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsJSONString);
JSStringRelease(jsJSONString);