From abe0b349bcdb59155445889d9615c13294a37c91 Mon Sep 17 00:00:00 2001 From: Alexey Lang Date: Tue, 31 May 2016 12:50:48 -0700 Subject: [PATCH] Implement RCTJSCWrapper Reviewed By: tadeuzagallo Differential Revision: D3258713 fbshipit-source-id: 418eb9d350bf3541c976b631bd9799a1c578f5e5 --- React/Base/RCTPerformanceLogger.h | 2 + React/Base/RCTPerformanceLogger.m | 2 + React/Executors/RCTJSCExecutor.h | 26 +++- React/Executors/RCTJSCExecutor.mm | 177 +++++++++++++++----------- React/Executors/RCTJSCWrapper.h | 58 +++++++++ React/Executors/RCTJSCWrapper.mm | 112 ++++++++++++++++ React/React.xcodeproj/project.pbxproj | 6 + 7 files changed, 306 insertions(+), 77 deletions(-) create mode 100644 React/Executors/RCTJSCWrapper.h create mode 100644 React/Executors/RCTJSCWrapper.mm diff --git a/React/Base/RCTPerformanceLogger.h b/React/Base/RCTPerformanceLogger.h index 051254fcc..3cfd62b0f 100644 --- a/React/Base/RCTPerformanceLogger.h +++ b/React/Base/RCTPerformanceLogger.h @@ -24,6 +24,8 @@ typedef NS_ENUM(NSUInteger, RCTPLTag) { RCTPLNativeModulePrepareConfig, RCTPLNativeModuleInjectConfig, RCTPLNativeModuleMainThreadUsesCount, + RCTPLJSCWrapperOpenLibrary, + RCTPLJSCWrapperLoadFunctions, RCTPLJSCExecutorSetup, RCTPLBridgeStartup, RCTPLTTI, diff --git a/React/Base/RCTPerformanceLogger.m b/React/Base/RCTPerformanceLogger.m index 5223fc823..68e38f434 100644 --- a/React/Base/RCTPerformanceLogger.m +++ b/React/Base/RCTPerformanceLogger.m @@ -97,6 +97,8 @@ NSArray *RCTPerformanceLoggerLabels(void) @"NativeModulePrepareConfig", @"NativeModuleInjectConfig", @"NativeModuleMainThreadUsesCount", + @"JSCWrapperOpenLibrary", + @"JSCWrapperLoadFunctions", @"JSCExecutorSetup", @"BridgeStartup", @"RootViewTTI", diff --git a/React/Executors/RCTJSCExecutor.h b/React/Executors/RCTJSCExecutor.h index 48fd16c39..f5b91bf8f 100644 --- a/React/Executors/RCTJSCExecutor.h +++ b/React/Executors/RCTJSCExecutor.h @@ -25,17 +25,31 @@ RCT_EXTERN NSString *const RCTJSCThreadName; */ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification; +/** + * Uses a JavaScriptCore context as the execution engine. + */ +@interface RCTJSCExecutor : NSObject + +/** + * Sets a type of JSC library (system or custom) that's used + * to initialize RCTJSCWrapper. + * @default is NO. + */ ++ (void)setUseCustomJSCLibrary:(BOOL)useCustomLibrary; + +/** + * Gets a type of JSC library (system or custom) that's used + * to initialize RCTJSCWrapper. + * @default is NO. + */ ++ (BOOL)useCustomJSCLibrary; + /** * Create a NSError from a JSError object. * * If available, the error's userInfo property will contain the JS stacktrace under * the RCTJSStackTraceKey key. */ -RCT_EXTERN NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError); - -/** - * Uses a JavaScriptCore context as the execution engine. - */ -@interface RCTJSCExecutor : NSObject +- (NSError *)convertJSErrorToNSError:(JSValueRef)jsError context:(JSContextRef)context; @end diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 7841834e8..91e80d7b8 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -12,13 +12,8 @@ #import #import #import +#import -#ifdef WITH_FB_JSC_TUNING -#include -#include -#endif - -#import #import #import "RCTAssert.h" @@ -33,6 +28,7 @@ #import "RCTJSCProfiler.h" #import "RCTRedBox.h" #import "RCTSourceCode.h" +#import "RCTJSCWrapper.h" NSString *const RCTJSCThreadName = @"com.facebook.react.JavaScript"; @@ -137,6 +133,8 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init) JSStringRef _bundleURL; RandomAccessBundleData _randomAccessBundle; + + RCTJSCWrapper *_jscWrapper; } @synthesize valid = _valid; @@ -144,36 +142,36 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init) RCT_EXPORT_MODULE() -static NSString *RCTJSValueToNSString(JSContextRef context, JSValueRef value, JSValueRef *exception) +static NSString *RCTJSValueToNSString(RCTJSCWrapper *jscWrapper, JSContextRef context, JSValueRef value, JSValueRef *exception) { - JSStringRef JSString = JSValueToStringCopy(context, value, exception); + JSStringRef JSString = jscWrapper->JSValueToStringCopy(context, value, exception); if (!JSString) { return nil; } - CFStringRef string = JSStringCopyCFString(kCFAllocatorDefault, JSString); - JSStringRelease(JSString); + CFStringRef string = jscWrapper->JSStringCopyCFString(kCFAllocatorDefault, JSString); + jscWrapper->JSStringRelease(JSString); return (__bridge_transfer NSString *)string; } -static NSString *RCTJSValueToJSONString(JSContextRef context, JSValueRef value, JSValueRef *exception, unsigned indent) +static NSString *RCTJSValueToJSONString(RCTJSCWrapper *jscWrapper, JSContextRef context, JSValueRef value, JSValueRef *exception, unsigned indent) { - JSStringRef JSString = JSValueCreateJSONString(context, value, indent, exception); - CFStringRef string = JSStringCopyCFString(kCFAllocatorDefault, JSString); - JSStringRelease(JSString); + JSStringRef jsString = jscWrapper->JSValueCreateJSONString(context, value, indent, exception); + CFStringRef string = jscWrapper->JSStringCopyCFString(kCFAllocatorDefault, jsString); + jscWrapper->JSStringRelease(jsString); return (__bridge_transfer NSString *)string; } -NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError) +static NSError *RCTNSErrorFromJSError(RCTJSCWrapper *jscWrapper, JSContextRef context, JSValueRef jsError) { NSMutableDictionary *errorInfo = [NSMutableDictionary new]; - NSString *description = jsError ? RCTJSValueToNSString(context, jsError, NULL) : @"Unknown JS error"; + NSString *description = jsError ? RCTJSValueToNSString(jscWrapper, context, jsError, NULL) : @"Unknown JS error"; errorInfo[NSLocalizedDescriptionKey] = [@"Unhandled JS Exception: " stringByAppendingString:description]; - NSString *details = jsError ? RCTJSValueToJSONString(context, jsError, NULL, 0) : nil; + NSString *details = jsError ? RCTJSValueToJSONString(jscWrapper, context, jsError, NULL, 0) : nil; if (details) { errorInfo[NSLocalizedFailureReasonErrorKey] = details; @@ -219,6 +217,11 @@ NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError) return [NSError errorWithDomain:RCTErrorDomain code:1 userInfo:errorInfo]; } +- (NSError *)convertJSErrorToNSError:(JSValueRef)jsError context:(JSContextRef)context +{ + return RCTNSErrorFromJSError(_jscWrapper, context, jsError); +} + #if RCT_DEV static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) @@ -259,6 +262,18 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) } } +static BOOL useCustomJSCLibrary = NO; + ++ (void)setUseCustomJSCLibrary:(BOOL)useCustomLibrary +{ + useCustomJSCLibrary = useCustomLibrary; +} + ++ (BOOL)useCustomJSCLibrary +{ + return useCustomJSCLibrary; +} + - (instancetype)init { if (self = [super init]) { @@ -290,7 +305,7 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) } if (!_context) { - JSContext *context = [JSContext new]; + JSContext *context = [_jscWrapper->JSContext new]; _context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:_javaScriptThread]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification @@ -312,21 +327,33 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) { __weak RCTJSCExecutor *weakSelf = self; -#ifdef WITH_FB_JSC_TUNING [self executeBlockOnJavaScriptQueue:^{ RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf.valid) { return; } + strongSelf->_jscWrapper = RCTJSCWrapperCreate(useCustomJSCLibrary); + }]; + + + [self executeBlockOnJavaScriptQueue:^{ + RCTJSCExecutor *strongSelf = weakSelf; + if (!strongSelf.valid) { + return; + } + + if (strongSelf->_jscWrapper->configureJSContextForIOS == NULL) { + return; + } + NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); if (cachesPath) { std::string path = std::string([cachesPath UTF8String]); - configureJSContextForIOS(strongSelf.context.ctx, path); + strongSelf->_jscWrapper->configureJSContextForIOS(strongSelf.context.ctx, path); } }]; -#endif [self addSynchronousHookWithName:@"noop" usingBlock:^{}]; @@ -454,11 +481,12 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) return; } - JSStringRef execJSString = JSStringCreateWithUTF8CString(sourceCode.UTF8String); - JSStringRef jsURL = JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String); - JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, NULL); - JSStringRelease(jsURL); - JSStringRelease(execJSString); + RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; + JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString(sourceCode.UTF8String); + JSStringRef jsURL = jscWrapper->JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String); + jscWrapper->JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, NULL); + jscWrapper->JSStringRelease(jsURL); + jscWrapper->JSStringRelease(execJSString); }]; #endif } @@ -496,6 +524,12 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) _randomAccessBundle.bundle.reset(); _randomAccessBundle.table.reset(); + + if (_jscWrapper) { + RCTJSCWrapperRelease(_jscWrapper); + _jscWrapper = NULL; + } + if (_cookieMap) { CFRelease(_cookieMap); } @@ -539,41 +573,42 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) JSValueRef errorJSRef = NULL; JSValueRef resultJSRef = NULL; - JSGlobalContextRef contextJSRef = JSContextGetGlobalContext(strongSelf->_context.ctx); + RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; + JSGlobalContextRef contextJSRef = jscWrapper->JSContextGetGlobalContext(strongSelf->_context.ctx); JSContext *context = strongSelf->_context.context; - JSObjectRef globalObjectJSRef = JSContextGetGlobalObject(strongSelf->_context.ctx); + JSObjectRef globalObjectJSRef = jscWrapper->JSContextGetGlobalObject(strongSelf->_context.ctx); // get the BatchedBridge object - JSStringRef moduleNameJSStringRef = JSStringCreateWithUTF8CString("__fbBatchedBridge"); - JSValueRef moduleJSRef = JSObjectGetProperty(contextJSRef, globalObjectJSRef, moduleNameJSStringRef, &errorJSRef); - JSStringRelease(moduleNameJSStringRef); + JSStringRef moduleNameJSStringRef = jscWrapper->JSStringCreateWithUTF8CString("__fbBatchedBridge"); + JSValueRef moduleJSRef = jscWrapper->JSObjectGetProperty(contextJSRef, globalObjectJSRef, moduleNameJSStringRef, &errorJSRef); + jscWrapper->JSStringRelease(moduleNameJSStringRef); - if (moduleJSRef != NULL && errorJSRef == NULL && !JSValueIsUndefined(contextJSRef, moduleJSRef)) { + if (moduleJSRef != NULL && errorJSRef == NULL && !jscWrapper->JSValueIsUndefined(contextJSRef, moduleJSRef)) { // get method - JSStringRef methodNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)method); - JSValueRef methodJSRef = JSObjectGetProperty(contextJSRef, (JSObjectRef)moduleJSRef, methodNameJSStringRef, &errorJSRef); - JSStringRelease(methodNameJSStringRef); + JSStringRef methodNameJSStringRef = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)method); + JSValueRef methodJSRef = jscWrapper->JSObjectGetProperty(contextJSRef, (JSObjectRef)moduleJSRef, methodNameJSStringRef, &errorJSRef); + jscWrapper->JSStringRelease(methodNameJSStringRef); - if (methodJSRef != NULL && errorJSRef == NULL && !JSValueIsUndefined(contextJSRef, methodJSRef)) { + if (methodJSRef != NULL && errorJSRef == NULL && !jscWrapper->JSValueIsUndefined(contextJSRef, methodJSRef)) { JSValueRef jsArgs[arguments.count]; for (NSUInteger i = 0; i < arguments.count; i++) { - jsArgs[i] = [JSValue valueWithObject:arguments[i] inContext:context].JSValueRef; + jsArgs[i] = [jscWrapper->JSValue valueWithObject:arguments[i] inContext:context].JSValueRef; } - resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, arguments.count, jsArgs, &errorJSRef); + resultJSRef = jscWrapper->JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, arguments.count, jsArgs, &errorJSRef); } else { - if (!errorJSRef && JSValueIsUndefined(contextJSRef, methodJSRef)) { + if (!errorJSRef && jscWrapper->JSValueIsUndefined(contextJSRef, methodJSRef)) { error = RCTErrorWithMessage([NSString stringWithFormat:@"Unable to execute JS call: method %@ is undefined", method]); } } } else { - if (!errorJSRef && JSValueIsUndefined(contextJSRef, moduleJSRef)) { + if (!errorJSRef && jscWrapper->JSValueIsUndefined(contextJSRef, moduleJSRef)) { error = RCTErrorWithMessage(@"Unable to execute JS call: __fbBatchedBridge is undefined"); } } if (errorJSRef || error) { if (!error) { - error = RCTNSErrorFromJSError(contextJSRef, errorJSRef); + error = RCTNSErrorFromJSError(jscWrapper, contextJSRef, errorJSRef); } onComplete(nil, error); return; @@ -585,8 +620,8 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) 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(contextJSRef, resultJSRef)) { - objcValue = [[JSValue valueWithJSValueRef:resultJSRef inContext:context] toObject]; + if (!jscWrapper->JSValueIsNull(contextJSRef, resultJSRef)) { + objcValue = [[jscWrapper->JSValue valueWithJSValueRef:resultJSRef inContext:context] toObject]; } onComplete(objcValue, nil); @@ -624,8 +659,6 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) script = nullTerminatedScript; } - _bundleURL = JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); - __weak RCTJSCExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ @@ -637,15 +670,17 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) RCTPerformanceLoggerStart(RCTPLScriptExecution); JSValueRef jsError = NULL; - JSStringRef execJSString = JSStringCreateWithUTF8CString((const char *)script.bytes); - JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, _bundleURL, 0, &jsError); - JSStringRelease(execJSString); + RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; + JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes); + JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); + JSValueRef result = jscWrapper->JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, bundleURL, 0, &jsError); + jscWrapper->JSStringRelease(execJSString); RCTPerformanceLoggerEnd(RCTPLScriptExecution); if (onComplete) { NSError *error; if (!result) { - error = RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError); + error = RCTNSErrorFromJSError(jscWrapper, strongSelf->_context.ctx, jsError); } onComplete(error); } @@ -684,9 +719,11 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) if (!strongSelf || !strongSelf.isValid) { return; } - JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script); - JSValueRef valueToInject = JSValueMakeFromJSONString(strongSelf->_context.ctx, execJSString); - JSStringRelease(execJSString); + + RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; + JSStringRef execJSString = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)script); + JSValueRef valueToInject = jscWrapper->JSValueMakeFromJSONString(strongSelf->_context.ctx, execJSString); + jscWrapper->JSStringRelease(execJSString); if (!valueToInject) { NSString *errorDesc = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script]; @@ -699,17 +736,17 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) return; } - JSObjectRef globalObject = JSContextGetGlobalObject(strongSelf->_context.ctx); - JSStringRef JSName = JSStringCreateWithCFString((__bridge CFStringRef)objectName); - JSObjectSetProperty(strongSelf->_context.ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL); - JSStringRelease(JSName); + JSObjectRef globalObject = jscWrapper->JSContextGetGlobalObject(strongSelf->_context.ctx); + JSStringRef JSName = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)objectName); + jscWrapper->JSObjectSetProperty(strongSelf->_context.ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL); + jscWrapper->JSStringRelease(JSName); if (onComplete) { onComplete(nil); } }), 0, @"js_call,json_call", (@{@"objectName": objectName}))]; } -static bool readRandomAccessModule(const RandomAccessBundleData& bundleData, size_t offset, size_t size, char *data) +static bool readRandomAccessModule(const RandomAccessBundleData &bundleData, size_t offset, size_t size, char *data) { return fseek(bundleData.bundle.get(), offset + bundleData.baseOffset, SEEK_SET) == 0 && fread(data, 1, size, bundleData.bundle.get()) == size; @@ -726,17 +763,18 @@ static void executeRandomAccessModule(RCTJSCExecutor *executor, uint32_t moduleI char url[14]; // 10 = maximum decimal digits in a 32bit unsigned int + ".js" + null byte sprintf(url, "%" PRIu32 ".js", moduleID); - JSStringRef code = JSStringCreateWithUTF8CString(data.get()); + RCTJSCWrapper *jscWrapper = executor->_jscWrapper; + JSStringRef code = jscWrapper->JSStringCreateWithUTF8CString(data.get()); JSValueRef jsError = NULL; - JSStringRef sourceURL = JSStringCreateWithUTF8CString(url); - JSValueRef result = JSEvaluateScript(executor->_context.ctx, code, NULL, sourceURL, 0, &jsError); + JSStringRef sourceURL = jscWrapper->JSStringCreateWithUTF8CString(url); + JSValueRef result = jscWrapper->JSEvaluateScript(executor->_context.ctx, code, NULL, sourceURL, 0, &jsError); - JSStringRelease(code); - JSStringRelease(sourceURL); + jscWrapper->JSStringRelease(code); + jscWrapper->JSStringRelease(sourceURL); if (!result) { dispatch_async(dispatch_get_main_queue(), ^{ - RCTFatal(RCTNSErrorFromJSError(executor->_context.ctx, jsError)); + RCTFatal(RCTNSErrorFromJSError(jscWrapper, executor->_context.ctx, jsError)); [executor invalidate]; }); } @@ -780,7 +818,7 @@ static void executeRandomAccessModule(RCTJSCExecutor *executor, uint32_t moduleI }]; } -static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccessBundleData& randomAccessBundle) +static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccessBundleData &randomAccessBundle) { // read in magic header, number of entries, and length of the startup section uint32_t header[3]; @@ -846,13 +884,10 @@ static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccess RCT_EXPORT_METHOD(setContextName:(nonnull NSString *)name) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtautological-pointer-compare" - if (JSGlobalContextSetName != NULL) { -#pragma clang diagnostic pop - JSStringRef JSName = JSStringCreateWithCFString((__bridge CFStringRef)name); - JSGlobalContextSetName(_context.ctx, JSName); - JSStringRelease(JSName); + if (_jscWrapper->JSGlobalContextSetName != NULL) { + JSStringRef JSName = _jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)name); + _jscWrapper->JSGlobalContextSetName(_context.ctx, JSName); + _jscWrapper->JSStringRelease(JSName); } } diff --git a/React/Executors/RCTJSCWrapper.h b/React/Executors/RCTJSCWrapper.h new file mode 100644 index 000000000..fc0ca29a3 --- /dev/null +++ b/React/Executors/RCTJSCWrapper.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2016-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. + */ + +#import + +#import "RCTDefines.h" +#import + +typedef JSStringRef (*JSValueToStringCopyFuncType)(JSContextRef, JSValueRef, JSValueRef *); +typedef JSStringRef (*JSStringCreateWithCFStringFuncType)(CFStringRef); +typedef CFStringRef (*JSStringCopyCFStringFuncType)(CFAllocatorRef, JSStringRef); +typedef JSStringRef (*JSStringCreateWithUTF8CStringFuncType)(const char *); +typedef void (*JSStringReleaseFuncType)(JSStringRef); +typedef void (*JSGlobalContextSetNameFuncType)(JSGlobalContextRef, JSStringRef); +typedef JSGlobalContextRef (*JSContextGetGlobalContextFuncType)(JSContextRef); +typedef void (*JSObjectSetPropertyFuncType)(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSPropertyAttributes, JSValueRef *); +typedef JSObjectRef (*JSContextGetGlobalObjectFuncType)(JSContextRef); +typedef JSValueRef (*JSObjectGetPropertyFuncType)(JSContextRef, JSObjectRef, JSStringRef, JSValueRef *); +typedef JSValueRef (*JSValueMakeFromJSONStringFuncType)(JSContextRef, JSStringRef); +typedef JSValueRef (*JSObjectCallAsFunctionFuncType)(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef *, JSValueRef *); +typedef JSValueRef (*JSValueMakeNullFuncType)(JSContextRef); +typedef JSStringRef (*JSValueCreateJSONStringFuncType)(JSContextRef, JSValueRef, unsigned, JSValueRef *); +typedef bool (*JSValueIsUndefinedFuncType)(JSContextRef, JSValueRef); +typedef bool (*JSValueIsNullFuncType)(JSContextRef, JSValueRef); +typedef JSValueRef (*JSEvaluateScriptFuncType)(JSContextRef, JSStringRef, JSObjectRef, JSStringRef, int, JSValueRef *); +typedef void (*configureJSContextForIOSFuncType)(JSContextRef ctx, const std::string &cacheDir); + +typedef struct RCTJSCWrapper { + JSValueToStringCopyFuncType JSValueToStringCopy; + JSStringCreateWithCFStringFuncType JSStringCreateWithCFString; + JSStringCopyCFStringFuncType JSStringCopyCFString; + JSStringCreateWithUTF8CStringFuncType JSStringCreateWithUTF8CString; + JSStringReleaseFuncType JSStringRelease; + JSGlobalContextSetNameFuncType JSGlobalContextSetName; + JSContextGetGlobalContextFuncType JSContextGetGlobalContext; + JSObjectSetPropertyFuncType JSObjectSetProperty; + JSContextGetGlobalObjectFuncType JSContextGetGlobalObject; + JSObjectGetPropertyFuncType JSObjectGetProperty; + JSValueMakeFromJSONStringFuncType JSValueMakeFromJSONString; + JSObjectCallAsFunctionFuncType JSObjectCallAsFunction; + JSValueMakeNullFuncType JSValueMakeNull; + JSValueCreateJSONStringFuncType JSValueCreateJSONString; + JSValueIsUndefinedFuncType JSValueIsUndefined; + JSValueIsNullFuncType JSValueIsNull; + JSEvaluateScriptFuncType JSEvaluateScript; + Class JSContext; + Class JSValue; + configureJSContextForIOSFuncType configureJSContextForIOS; +} RCTJSCWrapper; + +RCT_EXTERN RCTJSCWrapper *RCTJSCWrapperCreate(BOOL useCustomJSC); +RCT_EXTERN void RCTJSCWrapperRelease(RCTJSCWrapper *wrapper); diff --git a/React/Executors/RCTJSCWrapper.mm b/React/Executors/RCTJSCWrapper.mm new file mode 100644 index 000000000..4da53aeaa --- /dev/null +++ b/React/Executors/RCTJSCWrapper.mm @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2016-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. + */ + +#import "RCTJSCWrapper.h" + +#import +#import + +#import "RCTLog.h" +#import "RCTPerformanceLogger.h" + +#include + +static void *RCTCustomLibraryHandler(void) +{ + static dispatch_once_t token; + static void *handler; + dispatch_once(&token, ^{ + const char *path = [[[NSBundle mainBundle] pathForResource:@"JavaScriptCore" + ofType:nil + inDirectory:@"JavaScriptCore.framework"] UTF8String]; + if (path) { + RCTPerformanceLoggerStart(RCTPLJSCWrapperOpenLibrary); + handler = dlopen(path, RTLD_LAZY); + RCTPerformanceLoggerEnd(RCTPLJSCWrapperOpenLibrary); + if (!handler) { + RCTLogWarn(@"Can't load custome JSC library: %s", dlerror()); + } + } + }); + return handler; +} + +static void RCTSetUpSystemLibraryPointers(RCTJSCWrapper *wrapper) +{ + wrapper->JSValueToStringCopy = JSValueToStringCopy; + wrapper->JSStringCreateWithCFString = JSStringCreateWithCFString; + wrapper->JSStringCopyCFString = JSStringCopyCFString; + wrapper->JSStringCreateWithUTF8CString = JSStringCreateWithUTF8CString; + wrapper->JSStringRelease = JSStringRelease; + wrapper->JSGlobalContextSetName = JSGlobalContextSetName; + wrapper->JSContextGetGlobalContext = JSContextGetGlobalContext; + wrapper->JSObjectSetProperty = JSObjectSetProperty; + wrapper->JSContextGetGlobalObject = JSContextGetGlobalObject; + wrapper->JSObjectGetProperty = JSObjectGetProperty; + wrapper->JSValueMakeFromJSONString = JSValueMakeFromJSONString; + wrapper->JSObjectCallAsFunction = JSObjectCallAsFunction; + wrapper->JSValueMakeNull = JSValueMakeNull; + wrapper->JSValueCreateJSONString = JSValueCreateJSONString; + wrapper->JSValueIsUndefined = JSValueIsUndefined; + wrapper->JSValueIsNull = JSValueIsNull; + wrapper->JSEvaluateScript = JSEvaluateScript; + wrapper->JSContext = [JSContext class]; + wrapper->JSValue = [JSValue class]; + wrapper->configureJSContextForIOS = NULL; +} + +static void RCTSetUpCustomLibraryPointers(RCTJSCWrapper *wrapper) +{ + void *libraryHandle = RCTCustomLibraryHandler(); + if (!libraryHandle) { + RCTSetUpSystemLibraryPointers(wrapper); + return; + } + + RCTPerformanceLoggerStart(RCTPLJSCWrapperLoadFunctions); + wrapper->JSValueToStringCopy = (JSValueToStringCopyFuncType)dlsym(libraryHandle, "JSValueToStringCopy"); + wrapper->JSStringCreateWithCFString = (JSStringCreateWithCFStringFuncType)dlsym(libraryHandle, "JSStringCreateWithCFString"); + wrapper->JSStringCopyCFString = (JSStringCopyCFStringFuncType)dlsym(libraryHandle, "JSStringCopyCFString"); + wrapper->JSStringCreateWithUTF8CString = (JSStringCreateWithUTF8CStringFuncType)dlsym(libraryHandle, "JSStringCreateWithUTF8CString"); + wrapper->JSStringRelease = (JSStringReleaseFuncType)dlsym(libraryHandle, "JSStringRelease"); + wrapper->JSGlobalContextSetName = (JSGlobalContextSetNameFuncType)dlsym(libraryHandle, "JSGlobalContextSetName"); + wrapper->JSContextGetGlobalContext = (JSContextGetGlobalContextFuncType)dlsym(libraryHandle, "JSContextGetGlobalContext"); + wrapper->JSObjectSetProperty = (JSObjectSetPropertyFuncType)dlsym(libraryHandle, "JSObjectSetProperty"); + wrapper->JSContextGetGlobalObject = (JSContextGetGlobalObjectFuncType)dlsym(libraryHandle, "JSContextGetGlobalObject"); + wrapper->JSObjectGetProperty = (JSObjectGetPropertyFuncType)dlsym(libraryHandle, "JSObjectGetProperty"); + wrapper->JSValueMakeFromJSONString = (JSValueMakeFromJSONStringFuncType)dlsym(libraryHandle, "JSValueMakeFromJSONString"); + wrapper->JSObjectCallAsFunction = (JSObjectCallAsFunctionFuncType)dlsym(libraryHandle, "JSObjectCallAsFunction"); + wrapper->JSValueMakeNull = (JSValueMakeNullFuncType)dlsym(libraryHandle, "JSValueMakeNull"); + wrapper->JSValueCreateJSONString = (JSValueCreateJSONStringFuncType)dlsym(libraryHandle, "JSValueCreateJSONString"); + wrapper->JSValueIsUndefined = (JSValueIsUndefinedFuncType)dlsym(libraryHandle, "JSValueIsUndefined"); + wrapper->JSValueIsNull = (JSValueIsNullFuncType)dlsym(libraryHandle, "JSValueIsNull"); + wrapper->JSEvaluateScript = (JSEvaluateScriptFuncType)dlsym(libraryHandle, "JSEvaluateScript"); + wrapper->JSContext = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSContext"); + wrapper->JSValue = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSValue"); + wrapper->configureJSContextForIOS = (configureJSContextForIOSFuncType)dlsym(libraryHandle, "configureJSContextForIOS"); + RCTPerformanceLoggerEnd(RCTPLJSCWrapperLoadFunctions); +} + +RCTJSCWrapper *RCTJSCWrapperCreate(BOOL useCustomJSC) +{ + RCTJSCWrapper *wrapper = (RCTJSCWrapper *)malloc(sizeof(RCTJSCWrapper)); + if (useCustomJSC && [UIDevice currentDevice].systemVersion.floatValue >= 8) { + RCTSetUpCustomLibraryPointers(wrapper); + } else { + RCTSetUpSystemLibraryPointers(wrapper); + } + return wrapper; +} + +void RCTJSCWrapperRelease(RCTJSCWrapper *wrapper) +{ + if (wrapper) { + free(wrapper); + } +} diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index f103966c3..70d247c46 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -94,6 +94,7 @@ 83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */; }; 83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; }; 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; }; + 85C199EE1CD2407900DAD810 /* RCTJSCWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 85C199ED1CD2407900DAD810 /* RCTJSCWrapper.mm */; }; E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; /* End PBXBuildFile section */ @@ -302,6 +303,8 @@ 83CBBACA1A6023D300E9B192 /* RCTConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTConvert.h; sourceTree = ""; }; 83CBBACB1A6023D300E9B192 /* RCTConvert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert.m; sourceTree = ""; }; 83F15A171B7CC46900F10295 /* UIView+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+Private.h"; sourceTree = ""; }; + 85C199EC1CD2407900DAD810 /* RCTJSCWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCWrapper.h; sourceTree = ""; }; + 85C199ED1CD2407900DAD810 /* RCTJSCWrapper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSCWrapper.mm; sourceTree = ""; }; ACDD3FDA1BC7430D00E7DE33 /* RCTBorderStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderStyle.h; sourceTree = ""; }; E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTextDecorationLineType.h; sourceTree = ""; }; E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAccessibilityManager.h; sourceTree = ""; }; @@ -324,6 +327,8 @@ children = ( 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */, 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */, + 85C199EC1CD2407900DAD810 /* RCTJSCWrapper.h */, + 85C199ED1CD2407900DAD810 /* RCTJSCWrapper.mm */, ); path = Executors; sourceTree = ""; @@ -723,6 +728,7 @@ 13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */, 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */, 13D033631C1837FE0021DC29 /* RCTClipboard.m in Sources */, + 85C199EE1CD2407900DAD810 /* RCTJSCWrapper.mm in Sources */, 14C2CA741B3AC64300E6CBB2 /* RCTModuleData.m in Sources */, 142014191B32094000CC17BA /* RCTPerformanceLogger.m in Sources */, 83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */,