mirror of
https://github.com/status-im/react-native.git
synced 2025-02-26 08:05:34 +00:00
Use JSStringCreateWithUTF8CString and skip NSString decoding when loading the bundle
Summary: public Benchmarking our startup path has shown we spend a lot of time decoding strings (iPhone 4S / iPhone 5): * reading a 2MB JS bundle: 35ms / 15ms * decoding is to an `NSString`: 186ms / 78ms * transforming that to a `JSString`: 29ms / 10ms Instead of going through an `NSString` transformation, we generate a null-terminated bundle (0.1ms / 0.05ms to copy the data) and use `JSStringCreateWithUTF8CString` (121ms / 53ms) to generate the string. That makes decoding 70% faster. Reviewed By: javache Differential Revision: D2541140 fb-gh-sync-id: 09a016b8edfd46a9b62682c76705564d2024e75e
This commit is contained in:
parent
8e2ec64763
commit
4a3857ef1d
@ -40,7 +40,7 @@
|
||||
- (void)testNativeLoggingHookExceptionBehavior
|
||||
{
|
||||
dispatch_semaphore_t doneSem = dispatch_semaphore_create(0);
|
||||
[_executor executeApplicationScript:@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);"
|
||||
[_executor executeApplicationScript:[@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);" dataUsingEncoding:NSUTF8StringEncoding]
|
||||
sourceURL:[NSURL URLWithString:@"file://"]
|
||||
onComplete:^(__unused id error){
|
||||
dispatch_semaphore_signal(doneSem);
|
||||
@ -128,7 +128,7 @@ static uint64_t _get_time_nanoseconds(void)
|
||||
} \
|
||||
";
|
||||
|
||||
[_executor executeApplicationScript:script sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) {
|
||||
[_executor executeApplicationScript:[script dataUsingEncoding:NSUTF8StringEncoding] sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) {
|
||||
NSMutableArray *params = [NSMutableArray new];
|
||||
id data = @1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -148,7 +148,7 @@ RCT_EXPORT_MODULE()
|
||||
});
|
||||
}
|
||||
|
||||
- (void)executeApplicationScript:(NSString *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
- (void)executeApplicationScript:(NSData *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
NSDictionary *message = @{
|
||||
@"method": @"executeApplicationScript",
|
||||
|
@ -116,8 +116,8 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
||||
dispatch_group_enter(initModulesAndLoadSource);
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
__block NSString *sourceCode;
|
||||
[self loadSource:^(NSError *error, NSString *source) {
|
||||
__block NSData *sourceCode;
|
||||
[self loadSource:^(NSError *error, NSData *source) {
|
||||
if (error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf stopLoadingWithError:error];
|
||||
@ -184,7 +184,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||
RCTPerformanceLoggerStart(RCTPLScriptDownload);
|
||||
int cookie = RCTProfileBeginAsyncEvent(0, @"JavaScript download", nil);
|
||||
|
||||
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
|
||||
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSData *source) {
|
||||
RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil);
|
||||
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
||||
|
||||
@ -195,12 +195,13 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||
|
||||
// Force JS __DEV__ value to match RCT_DEBUG
|
||||
if (shouldOverrideDev) {
|
||||
NSRange range = [source rangeOfString:@"__DEV__="];
|
||||
NSString *sourceString = [[NSString alloc] initWithData:source encoding:NSUTF8StringEncoding];
|
||||
NSRange range = [sourceString rangeOfString:@"__DEV__="];
|
||||
RCTAssert(range.location != NSNotFound, @"It looks like the implementation"
|
||||
"of __DEV__ has changed. Update -[RCTBatchedBridge loadSource:].");
|
||||
NSRange valueRange = {range.location + range.length, 2};
|
||||
if ([[source substringWithRange:valueRange] isEqualToString:@"!1"]) {
|
||||
source = [source stringByReplacingCharactersInRange:valueRange withString:@" 1"];
|
||||
if ([[sourceString substringWithRange:valueRange] isEqualToString:@"!1"]) {
|
||||
source = [[sourceString stringByReplacingCharactersInRange:valueRange withString:@" 1"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +356,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||
callback:onComplete];
|
||||
}
|
||||
|
||||
- (void)executeSourceCode:(NSString *)sourceCode
|
||||
- (void)executeSourceCode:(NSData *)sourceCode
|
||||
{
|
||||
if (!self.valid || !_javaScriptExecutor) {
|
||||
return;
|
||||
@ -363,7 +364,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||
|
||||
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = self.bundleURL;
|
||||
sourceCodeModule.scriptText = sourceCode;
|
||||
sourceCodeModule.scriptData = sourceCode;
|
||||
|
||||
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) {
|
||||
if (!self.isValid) {
|
||||
@ -585,7 +586,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enqueueApplicationScript:(NSString *)script
|
||||
- (void)enqueueApplicationScript:(NSData *)script
|
||||
url:(NSURL *)url
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
typedef void (^RCTSourceLoadBlock)(NSError *error, NSString *source);
|
||||
typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source);
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
|
@ -46,7 +46,7 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
||||
/**
|
||||
* Runs an application script, and notifies of the script load being complete via `onComplete`.
|
||||
*/
|
||||
- (void)executeApplicationScript:(NSString *)script
|
||||
- (void)executeApplicationScript:(NSData *)script
|
||||
sourceURL:(NSURL *)sourceURL
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
|
||||
|
@ -36,8 +36,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
NSString *filePath = scriptURL.path;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSError *error = nil;
|
||||
NSString *rawText = [NSString stringWithContentsOfFile:filePath usedEncoding:NULL error:&error];
|
||||
onComplete(error, rawText);
|
||||
NSData *source = [NSData dataWithContentsOfFile:filePath
|
||||
options:NSDataReadingMappedIfSafe
|
||||
error:&error];
|
||||
onComplete(error, source);
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -71,10 +73,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
}
|
||||
}
|
||||
NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
|
||||
// Handle HTTP errors
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode != 200) {
|
||||
NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
NSDictionary *userInfo;
|
||||
NSDictionary *errorDetails = RCTJSONParse(rawText, nil);
|
||||
if ([errorDetails isKindOfClass:[NSDictionary class]] &&
|
||||
@ -101,7 +103,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
onComplete(nil, rawText);
|
||||
onComplete(nil, data);
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
@ -494,7 +494,7 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
||||
}), 0, @"js_call", (@{@"module":name, @"method": method, @"args": arguments}))];
|
||||
}
|
||||
|
||||
- (void)executeApplicationScript:(NSString *)script
|
||||
- (void)executeApplicationScript:(NSData *)script
|
||||
sourceURL:(NSURL *)sourceURL
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
@ -508,9 +508,15 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
||||
return;
|
||||
}
|
||||
|
||||
// JSStringCreateWithUTF8CString expects a null terminated C string
|
||||
NSMutableData *nullTerminatedScript = [NSMutableData dataWithCapacity:script.length + 1];
|
||||
|
||||
[nullTerminatedScript appendData:script];
|
||||
[nullTerminatedScript appendBytes:"" length:1];
|
||||
|
||||
RCTPerformanceLoggerStart(RCTPLScriptExecution);
|
||||
JSValueRef jsError = NULL;
|
||||
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script);
|
||||
JSStringRef execJSString = JSStringCreateWithUTF8CString(nullTerminatedScript.bytes);
|
||||
JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString);
|
||||
JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError);
|
||||
JSStringRelease(jsURL);
|
||||
|
@ -130,7 +130,7 @@ RCT_EXPORT_MODULE()
|
||||
* debugger. So we have to use this (essentially) async API - and register
|
||||
* ourselves as the webview delegate to be notified when load is complete.
|
||||
*/
|
||||
- (void)executeApplicationScript:(NSString *)script
|
||||
- (void)executeApplicationScript:(NSData *)script
|
||||
sourceURL:(NSURL *)url
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
@ -142,6 +142,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
|
||||
RCTAssert(onComplete != nil, @"");
|
||||
NSString *scriptString = [[NSString alloc] initWithData:script encoding:NSUTF8StringEncoding];
|
||||
__weak RCTWebViewExecutor *weakSelf = self;
|
||||
_onApplicationScriptLoaded = ^(NSError *error){
|
||||
RCTWebViewExecutor *strongSelf = weakSelf;
|
||||
@ -163,23 +164,23 @@ RCT_EXPORT_MODULE()
|
||||
}];
|
||||
[_objectsToInject removeAllObjects];
|
||||
[scriptWithInjections appendString:@"/* END NATIVELY INJECTED OBJECTS */\n"];
|
||||
[scriptWithInjections appendString:script];
|
||||
script = scriptWithInjections;
|
||||
[scriptWithInjections appendString:scriptString];
|
||||
scriptString = scriptWithInjections;
|
||||
}
|
||||
|
||||
script = [_commentsRegex stringByReplacingMatchesInString:script
|
||||
options:0
|
||||
range:NSMakeRange(0, script.length)
|
||||
withTemplate:@""];
|
||||
script = [_scriptTagsRegex stringByReplacingMatchesInString:script
|
||||
options:0
|
||||
range:NSMakeRange(0, script.length)
|
||||
withTemplate:@"\\\\<$1\\\\>"];
|
||||
scriptString = [_commentsRegex stringByReplacingMatchesInString:scriptString
|
||||
options:0
|
||||
range:NSMakeRange(0, script.length)
|
||||
withTemplate:@""];
|
||||
scriptString = [_scriptTagsRegex stringByReplacingMatchesInString:scriptString
|
||||
options:0
|
||||
range:NSMakeRange(0, script.length)
|
||||
withTemplate:@"\\\\<$1\\\\>"];
|
||||
|
||||
NSString *runScript =
|
||||
[NSString
|
||||
stringWithFormat:@"<html><head></head><body><script type='text/javascript'>%@</script></body></html>",
|
||||
script
|
||||
scriptString
|
||||
];
|
||||
[_webView loadHTMLString:runScript baseURL:url];
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
@interface RCTSourceCode : NSObject <RCTBridgeModule>
|
||||
|
||||
@property (nonatomic, copy) NSString *scriptText;
|
||||
@property (nonatomic, copy) NSData *scriptData;
|
||||
@property (nonatomic, copy) NSURL *scriptURL;
|
||||
|
||||
@end
|
||||
|
@ -27,8 +27,10 @@ RCT_EXPORT_MODULE()
|
||||
RCT_EXPORT_METHOD(getScriptText:(RCTResponseSenderBlock)successCallback
|
||||
failureCallback:(RCTResponseErrorBlock)failureCallback)
|
||||
{
|
||||
if (RCT_DEV && self.scriptText && self.scriptURL) {
|
||||
successCallback(@[@{@"text": self.scriptText, @"url": self.scriptURL.absoluteString}]);
|
||||
if (RCT_DEV && self.scriptData && self.scriptURL) {
|
||||
NSString *scriptText = [[NSString alloc] initWithData:self.scriptData encoding:NSUTF8StringEncoding];
|
||||
|
||||
successCallback(@[@{@"text": scriptText, @"url": self.scriptURL.absoluteString}]);
|
||||
} else {
|
||||
failureCallback(RCTErrorWithMessage(@"Source code is not available"));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user