2015-03-23 20:28:42 +00:00
/ * *
* Copyright ( c ) 2015 - 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 .
* /
2015-02-20 04:10:52 +00:00
2015-12-16 10:49:27 +00:00
# import "RCTJSCExecutor.h"
2015-02-20 04:10:52 +00:00
# import < pthread . h >
# import < JavaScriptCore / JavaScriptCore . h >
2015-07-21 02:30:59 +00:00
# import < UIKit / UIDevice . h >
2015-02-20 04:10:52 +00:00
# import "RCTAssert.h"
2015-12-15 13:39:30 +00:00
# import "RCTBridge+Private.h"
2015-04-21 12:26:51 +00:00
# import "RCTDefines.h"
2015-08-28 17:11:02 +00:00
# import "RCTDevMenu.h"
2015-02-20 04:10:52 +00:00
# import "RCTLog.h"
2015-04-20 11:55:05 +00:00
# import "RCTProfile.h"
2015-06-19 21:59:42 +00:00
# import "RCTPerformanceLogger.h"
2015-02-20 04:10:52 +00:00
# import "RCTUtils.h"
2015-12-15 11:11:30 +00:00
# import "RCTJSCProfiler.h"
2015-12-29 00:43:21 +00:00
# import "RCTBundleURLProcessor.h"
2015-07-20 16:14:53 +00:00
2015-11-05 20:20:15 +00:00
static NSString * const RCTJSCProfilerEnabledDefaultsKey = @ "RCTJSCProfilerEnabled" ;
2015-12-29 00:43:08 +00:00
static NSString * const RCTHotLoadingEnabledDefaultsKey = @ "RCTHotLoadingEnabled" ;
2015-09-15 09:49:04 +00:00
2015-04-10 14:28:10 +00:00
@ interface RCTJavaScriptContext : NSObject < RCTInvalidating >
2015-10-19 10:49:43 +00:00
@ property ( nonatomic , strong , readonly ) JSContext * context ;
2015-04-10 14:28:10 +00:00
@ property ( nonatomic , assign , readonly ) JSGlobalContextRef ctx ;
2015-10-19 10:49:43 +00:00
- ( instancetype ) initWithJSContext : ( JSContext * ) context NS_DESIGNATED _INITIALIZER ;
2015-04-10 14:28:10 +00:00
@ end
@ implementation RCTJavaScriptContext
{
2015-12-09 12:37:40 +00:00
RCTJavaScriptContext * _selfReference ;
2015-04-10 14:28:10 +00:00
}
2015-10-19 10:49:43 +00:00
- ( instancetype ) initWithJSContext : ( JSContext * ) context
2015-04-10 14:28:10 +00:00
{
if ( ( self = [ super init ] ) ) {
2015-10-19 10:49:43 +00:00
_context = context ;
2015-12-09 12:37:40 +00:00
/ * *
2015-12-16 10:49:27 +00:00
* Explicitly introduce a retain cycle here - The RCTJSCExecutor might
2015-12-09 12:37:40 +00:00
* be deallocated while there ' s still work enqueued in the JS thread , so
* we wouldn ' t be able kill the JSContext . Instead we create this retain
* cycle , and enqueue the - invalidate message in this object , it then
* releases the JSContext , breaks the cycle and stops the runloop .
* /
_selfReference = self ;
2015-04-10 14:28:10 +00:00
}
return self ;
}
2015-08-24 10:14:33 +00:00
RCT_NOT _IMPLEMENTED ( - ( instancetype ) init )
2015-10-19 10:49:43 +00:00
- ( JSGlobalContextRef ) ctx
{
return _context . JSGlobalContextRef ;
}
2015-04-10 14:28:10 +00:00
- ( BOOL ) isValid
{
2015-10-19 10:49:43 +00:00
return _context ! = nil ;
2015-04-10 14:28:10 +00:00
}
- ( void ) invalidate
{
2015-04-20 12:40:42 +00:00
if ( self . isValid ) {
2015-10-19 10:49:43 +00:00
_context = nil ;
2015-12-09 12:37:40 +00:00
_selfReference = nil ;
2015-04-20 12:40:42 +00:00
}
2015-04-10 14:28:10 +00:00
}
2015-04-27 10:47:48 +00:00
- ( void ) dealloc
{
CFRunLoopStop ( [ [ NSRunLoop currentRunLoop ] getCFRunLoop ] ) ;
}
2015-04-10 14:28:10 +00:00
@ end
2015-12-16 10:49:27 +00:00
@ implementation RCTJSCExecutor
2015-02-20 04:10:52 +00:00
{
2015-04-10 14:28:10 +00:00
RCTJavaScriptContext * _context ;
2015-02-20 04:10:52 +00:00
NSThread * _javaScriptThread ;
}
2015-06-12 17:43:22 +00:00
@ synthesize valid = _valid ;
2015-08-28 17:11:02 +00:00
@ synthesize bridge = _bridge ;
2015-06-12 17:43:22 +00:00
2015-06-09 22:42:10 +00:00
RCT_EXPORT _MODULE ( )
2015-11-05 20:20:15 +00:00
static NSString * RCTJSValueToNSString ( JSContextRef context , JSValueRef value , JSValueRef * exception )
2015-02-20 04:10:52 +00:00
{
2015-11-05 20:20:15 +00:00
JSStringRef JSString = JSValueToStringCopy ( context , value , exception ) ;
if ( ! JSString ) {
return nil ;
}
2015-02-20 04:10:52 +00:00
CFStringRef string = JSStringCopyCFString ( kCFAllocatorDefault , JSString ) ;
JSStringRelease ( JSString ) ;
return ( __bridge _transfer NSString * ) string ;
}
2015-11-05 20:20:15 +00:00
static NSString * RCTJSValueToJSONString ( JSContextRef context , JSValueRef value , JSValueRef * exception , unsigned indent )
2015-02-20 04:10:52 +00:00
{
2015-11-05 20:20:15 +00:00
JSStringRef JSString = JSValueCreateJSONString ( context , value , indent , exception ) ;
2015-02-20 04:10:52 +00:00
CFStringRef string = JSStringCopyCFString ( kCFAllocatorDefault , JSString ) ;
JSStringRelease ( JSString ) ;
return ( __bridge _transfer NSString * ) string ;
}
static NSError * RCTNSErrorFromJSError ( JSContextRef context , JSValueRef jsError )
{
2015-12-18 01:26:12 +00:00
NSString * errorMessage = jsError ? RCTJSValueToNSString ( context , jsError , NULL ) : @ "Unknown JS error" ;
NSString * details = jsError ? RCTJSValueToJSONString ( context , jsError , NULL , 2 ) : @ "No details" ;
2015-02-20 04:10:52 +00:00
return [ NSError errorWithDomain : @ "JS" code : 1 userInfo : @ { NSLocalizedDescriptionKey : errorMessage , NSLocalizedFailureReasonErrorKey : details } ] ;
}
2015-08-25 08:31:22 +00:00
# if RCT_DEV
2015-09-10 16:03:03 +00:00
static void RCTInstallJSCProfiler ( RCTBridge * bridge , JSContextRef context )
{
2015-12-15 11:11:30 +00:00
if ( RCTJSCProfilerIsSupported ( ) ) {
[ bridge . devMenu addItem : [ RCTDevMenuItem toggleItemWithKey : RCTJSCProfilerEnabledDefaultsKey title : @ "Start Profiling" selectedTitle : @ "Stop Profiling" handler : ^ ( BOOL shouldStart ) {
if ( shouldStart ! = RCTJSCProfilerIsProfiling ( context ) ) {
2015-09-15 09:49:04 +00:00
if ( shouldStart ) {
2015-12-15 11:11:30 +00:00
RCTJSCProfilerStart ( context ) ;
2015-09-15 09:49:04 +00:00
} else {
2015-12-15 11:11:30 +00:00
NSString * outputFile = RCTJSCProfilerStop ( context ) ;
NSData * profileData = [ NSData dataWithContentsOfFile : outputFile options : NSDataReadingMappedIfSafe error : NULL ] ;
2015-09-11 13:35:25 +00:00
RCTProfileSendResult ( bridge , @ "cpu-profile" , profileData ) ;
2015-09-10 16:03:03 +00:00
}
2015-12-15 11:11:30 +00:00
}
} ] ] ;
2015-09-10 16:03:03 +00:00
}
}
2015-12-29 00:43:08 +00:00
static void RCTInstallHotLoading ( RCTBridge * bridge , RCTJSCExecutor * executor )
{
2015-12-29 00:43:21 +00:00
[ bridge . devMenu addItem : [ RCTDevMenuItem toggleItemWithKey : RCTHotLoadingEnabledDefaultsKey title : @ "Enable Hot Loading" selectedTitle : @ "Disable Hot Loading" handler : ^ ( BOOL enabledOnCurrentBundle ) {
2015-12-29 00:43:08 +00:00
[ executor executeBlockOnJavaScriptQueue : ^ {
2015-12-29 00:43:21 +00:00
NSString * enabledQS = [ [ RCTBundleURLProcessor sharedProcessor ] getQueryStringValue : @ "hot" ] ;
BOOL enabledOnConfig = ( enabledQS ! = nil && [ enabledQS isEqualToString : @ "true" ] ) ? YES : NO ;
// reload bundle when user change Hot Loading setting
if ( enabledOnConfig ! = enabledOnCurrentBundle ) {
[ [ RCTBundleURLProcessor sharedProcessor ] setQueryStringValue : enabledOnCurrentBundle ? @ "true" : @ "false" forAttribute : @ "hot" ] ;
[ bridge reload ] ;
}
if ( enabledOnCurrentBundle ) {
[ bridge enqueueJSCall : @ "HMRClient.enable" args : @ [ @ YES ] ] ;
}
2015-12-29 00:43:08 +00:00
} ] ;
} ] ] ;
}
2015-08-25 08:31:22 +00:00
# endif
2015-02-20 04:10:52 +00:00
+ ( void ) runRunLoopThread
{
@ autoreleasepool {
// copy thread name to pthread name
2015-08-24 10:14:33 +00:00
pthread_setname _np ( [ NSThread currentThread ] . name . UTF8String ) ;
2015-02-20 04:10:52 +00:00
// Set up a dummy runloop source to avoid spinning
CFRunLoopSourceContext noSpinCtx = { 0 , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL } ;
CFRunLoopSourceRef noSpinSource = CFRunLoopSourceCreate ( NULL , 0 , & noSpinCtx ) ;
CFRunLoopAddSource ( CFRunLoopGetCurrent ( ) , noSpinSource , kCFRunLoopDefaultMode ) ;
CFRelease ( noSpinSource ) ;
// run the run loop
2015-08-24 10:14:33 +00:00
while ( kCFRunLoopRunStopped ! = CFRunLoopRunInMode ( kCFRunLoopDefaultMode , ( ( NSDate * ) [ NSDate distantFuture ] ) . timeIntervalSinceReferenceDate , NO ) ) {
2015-03-26 16:44:58 +00:00
RCTAssert ( NO , @ "not reached assertion" ) ; // runloop spun . that ' s bad .
2015-02-20 04:10:52 +00:00
}
}
}
- ( instancetype ) init
{
2015-04-27 10:47:48 +00:00
NSThread * javaScriptThread = [ [ NSThread alloc ] initWithTarget : [ self class ]
selector : @ selector ( runRunLoopThread )
object : nil ] ;
2015-08-24 10:14:33 +00:00
javaScriptThread . name = @ "com.facebook.React.JavaScript" ;
2015-11-12 13:19:59 +00:00
if ( [ javaScriptThread respondsToSelector : @ selector ( setQualityOfService : ) ] ) {
2015-11-13 01:00:30 +00:00
[ javaScriptThread setQualityOfService : NSOperationQualityOfServiceUserInteractive ] ;
2015-11-12 13:19:59 +00:00
} else {
javaScriptThread . threadPriority = [ NSThread mainThread ] . threadPriority ;
}
2015-04-27 10:47:48 +00:00
[ javaScriptThread start ] ;
2015-02-20 04:10:52 +00:00
2015-10-19 10:49:43 +00:00
return [ self initWithJavaScriptThread : javaScriptThread context : nil ] ;
2015-02-20 04:10:52 +00:00
}
2015-03-25 00:37:03 +00:00
- ( instancetype ) initWithJavaScriptThread : ( NSThread * ) javaScriptThread
2015-10-19 10:49:43 +00:00
context : ( JSContext * ) context
2015-02-20 04:10:52 +00:00
{
2015-04-27 10:47:48 +00:00
RCTAssert ( javaScriptThread ! = nil ,
2015-12-16 10:49:27 +00:00
@ "Can't initialize RCTJSCExecutor without a javaScriptThread" ) ;
2015-04-27 10:47:48 +00:00
2015-02-20 04:10:52 +00:00
if ( ( self = [ super init ] ) ) {
2015-06-12 17:43:22 +00:00
_valid = YES ;
2015-02-20 04:10:52 +00:00
_javaScriptThread = javaScriptThread ;
2015-12-16 10:49:27 +00:00
__weak RCTJSCExecutor * weakSelf = self ;
2015-02-20 04:10:52 +00:00
[ self executeBlockOnJavaScriptQueue : ^ {
2015-12-16 10:49:27 +00:00
RCTJSCExecutor * strongSelf = weakSelf ;
2015-04-10 14:28:10 +00:00
if ( ! strongSelf ) {
return ;
}
2015-02-20 04:10:52 +00:00
// Assumes that no other JS tasks are scheduled before .
if ( context ) {
2015-10-19 10:49:43 +00:00
strongSelf -> _context = [ [ RCTJavaScriptContext alloc ] initWithJSContext : context ] ;
2015-02-20 04:10:52 +00:00
}
} ] ;
}
return self ;
}
2015-10-19 10:49:43 +00:00
- ( instancetype ) initWithJavaScriptThread : ( NSThread * ) javaScriptThread
globalContextRef : ( JSGlobalContextRef ) contextRef
{
JSContext * context = contextRef ? [ JSContext contextWithJSGlobalContextRef : contextRef ] : nil ;
return [ self initWithJavaScriptThread : javaScriptThread context : context ] ;
}
2015-06-09 22:42:10 +00:00
- ( void ) setUp
{
2015-12-16 10:49:27 +00:00
__weak RCTJSCExecutor * weakSelf = self ;
2015-06-09 22:42:10 +00:00
[ self executeBlockOnJavaScriptQueue : ^ {
2015-12-16 10:49:27 +00:00
RCTJSCExecutor * strongSelf = weakSelf ;
2015-06-12 17:43:22 +00:00
if ( ! strongSelf . isValid ) {
2015-06-09 22:42:10 +00:00
return ;
}
2015-12-15 13:32:24 +00:00
2015-06-09 22:42:10 +00:00
if ( ! strongSelf -> _context ) {
2015-11-02 15:58:47 +00:00
JSContext * context = [ JSContext new ] ;
2015-10-19 10:49:43 +00:00
strongSelf -> _context = [ [ RCTJavaScriptContext alloc ] initWithJSContext : context ] ;
2015-06-09 22:42:10 +00:00
}
2015-10-19 15:02:50 +00:00
2015-12-10 12:27:45 +00:00
__weak RCTBridge * weakBridge = strongSelf -> _bridge ;
2015-12-15 13:32:24 +00:00
JSContext * context = strongSelf -> _context . context ;
context [ @ "noop" ] = ^ { } ;
context [ @ "nativeLoggingHook" ] = ^ ( NSString * message , NSNumber * logLevel ) {
RCTLogLevel level = RCTLogLevelInfo ;
if ( logLevel ) {
level = MAX ( level , logLevel . integerValue ) ;
}
_RCTLogJavaScriptInternal ( level , message ) ;
} ;
2015-12-10 12:27:45 +00:00
2015-12-15 13:32:24 +00:00
context [ @ "nativeRequireModuleConfig" ] = ^ NSString * ( NSString * moduleName ) {
2015-12-10 12:27:45 +00:00
if ( ! weakSelf . valid ) {
return nil ;
}
NSArray * config = [ weakBridge configForModuleName : moduleName ] ;
if ( config ) {
return RCTJSONStringify ( config , NULL ) ;
}
return nil ;
} ;
2015-12-15 13:32:24 +00:00
context [ @ "nativeFlushQueueImmediate" ] = ^ ( NSArray < NSArray * > * calls ) {
2015-10-19 15:02:50 +00:00
if ( ! weakSelf . valid || ! calls ) {
return ;
}
2015-12-10 12:27:45 +00:00
[ weakBridge handleBuffer : calls batchEnded : NO ] ;
2015-10-19 15:02:50 +00:00
} ;
2015-12-15 22:51:10 +00:00
context [ @ "nativePerformanceNow" ] = ^ {
return @ ( CACurrentMediaTime ( ) * 1000 ) ;
2015-11-02 16:13:42 +00:00
} ;
2015-06-09 22:42:10 +00:00
# if RCT_DEV
2015-12-10 12:27:45 +00:00
2015-11-02 13:30:16 +00:00
if ( RCTProfileIsProfiling ( ) ) {
2015-12-15 13:32:24 +00:00
context [ @ "__RCTProfileIsProfiling" ] = @ YES ;
2015-11-02 13:30:16 +00:00
}
2015-12-01 13:06:45 +00:00
CFMutableDictionaryRef cookieMap = CFDictionaryCreateMutable ( NULL , 0 , NULL , NULL ) ;
2015-12-15 13:32:24 +00:00
context [ @ "nativeTraceBeginAsyncSection" ] = ^ ( uint64_t tag , NSString * name , NSUInteger cookie ) {
2015-12-01 13:06:45 +00:00
NSUInteger newCookie = RCTProfileBeginAsyncEvent ( tag , name , nil ) ;
CFDictionarySetValue ( cookieMap , ( const void * ) cookie , ( const void * ) newCookie ) ;
} ;
2015-12-15 13:32:24 +00:00
context [ @ "nativeTraceEndAsyncSection" ] = ^ ( uint64_t tag , NSString * name , NSUInteger cookie ) {
2015-12-01 13:06:45 +00:00
NSUInteger newCookie = ( NSUInteger ) CFDictionaryGetValue ( cookieMap , ( const void * ) cookie ) ;
RCTProfileEndAsyncEvent ( tag , @ "js,async" , newCookie , name , nil ) ;
CFDictionaryRemoveValue ( cookieMap , ( const void * ) cookie ) ;
} ;
2015-12-15 13:32:24 +00:00
context [ @ "nativeTraceBeginSection" ] = ^ ( NSNumber * tag , NSString * profileName ) {
static int profileCounter = 1 ;
if ( ! profileName ) {
profileName = [ NSString stringWithFormat : @ "Profile %d" , profileCounter + + ] ;
}
RCT_PROFILE _BEGIN _EVENT ( tag . longLongValue , profileName , nil ) ;
} ;
context [ @ "nativeTraceEndSection" ] = ^ ( NSNumber * tag ) {
RCT_PROFILE _END _EVENT ( tag . longLongValue , @ "console" , nil ) ;
} ;
2015-06-09 22:42:10 +00:00
2015-09-10 16:03:03 +00:00
RCTInstallJSCProfiler ( _bridge , strongSelf -> _context . ctx ) ;
2015-12-29 00:43:22 +00:00
if ( [ self . bridge . delegate respondsToSelector : @ selector ( isHotLoadingEnabled ) ] && [ self . bridge . delegate isHotLoadingEnabled ] ) {
RCTInstallHotLoading ( _bridge , strongSelf ) ;
}
2015-07-20 16:14:53 +00:00
2015-06-09 22:42:10 +00:00
for ( NSString * event in @ [ RCTProfileDidStartProfiling , RCTProfileDidEndProfiling ] ) {
2015-06-12 17:43:22 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : strongSelf
2015-06-09 22:42:10 +00:00
selector : @ selector ( toggleProfilingFlag : )
name : event
object : nil ] ;
}
2015-12-10 12:27:45 +00:00
2015-06-09 22:42:10 +00:00
# endif
2015-12-10 12:27:45 +00:00
2015-06-09 22:42:10 +00:00
} ] ;
}
2015-06-02 13:15:53 +00:00
- ( void ) toggleProfilingFlag : ( NSNotification * ) notification
{
2015-10-01 21:08:13 +00:00
[ self executeBlockOnJavaScriptQueue : ^ {
BOOL enabled = [ notification . name isEqualToString : RCTProfileDidStartProfiling ] ;
2015-12-12 00:35:36 +00:00
[ _bridge enqueueJSCall : @ "Systrace.setEnabled" args : @ [ enabled ? @ YES : @ NO ] ] ;
2015-10-01 21:08:13 +00:00
} ] ;
2015-06-02 13:15:53 +00:00
}
2015-02-20 04:10:52 +00:00
- ( void ) invalidate
{
2015-06-12 17:43:22 +00:00
if ( ! self . isValid ) {
return ;
}
_valid = NO ;
2015-06-02 13:15:53 +00:00
# if RCT_DEV
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
# endif
2015-06-15 14:53:45 +00:00
[ _context performSelector : @ selector ( invalidate )
onThread : _javaScriptThread
withObject : nil
waitUntilDone : NO ] ;
2015-07-20 09:34:11 +00:00
_context = nil ;
2015-02-20 04:10:52 +00:00
}
- ( void ) dealloc
{
2015-04-16 15:42:15 +00:00
[ self invalidate ] ;
2015-02-20 04:10:52 +00:00
}
2015-12-08 23:57:34 +00:00
- ( void ) flushedQueue : ( RCTJavaScriptCallback ) onComplete
{
// TODO : Make this function handle first class instead of dynamically dispatching it . #9317773
[ self _executeJSCall : @ "flushedQueue" arguments : @ [ ] callback : onComplete ] ;
}
- ( void ) callFunctionOnModule : ( NSString * ) module
method : ( NSString * ) method
arguments : ( NSArray * ) args
callback : ( RCTJavaScriptCallback ) onComplete
{
// TODO : Make this function handle first class instead of dynamically dispatching it . #9317773
[ self _executeJSCall : @ "callFunctionReturnFlushedQueue" arguments : @ [ module , method , args ] callback : onComplete ] ;
}
- ( void ) invokeCallbackID : ( NSNumber * ) cbID
arguments : ( NSArray * ) args
callback : ( RCTJavaScriptCallback ) onComplete
{
// TODO : Make this function handle first class instead of dynamically dispatching it . #9317773
[ self _executeJSCall : @ "invokeCallbackAndReturnFlushedQueue" arguments : @ [ cbID , args ] callback : onComplete ] ;
}
- ( void ) _executeJSCall : ( NSString * ) method
arguments : ( NSArray * ) arguments
callback : ( RCTJavaScriptCallback ) onComplete
2015-02-20 04:10:52 +00:00
{
RCTAssert ( onComplete ! = nil , @ "onComplete block should not be nil" ) ;
2015-12-16 10:49:27 +00:00
__weak RCTJSCExecutor * weakSelf = self ;
2015-04-20 11:55:05 +00:00
[ self executeBlockOnJavaScriptQueue : RCTProfileBlock ( ( ^ {
2015-12-16 10:49:27 +00:00
RCTJSCExecutor * strongSelf = weakSelf ;
2015-07-14 23:16:21 +00:00
if ( ! strongSelf || ! strongSelf . isValid ) {
2015-04-10 14:28:10 +00:00
return ;
}
2015-02-20 04:10:52 +00:00
NSError * error ;
2015-05-23 02:33:21 +00:00
NSString * argsString = ( arguments . count = = 1 ) ? RCTJSONStringify ( arguments [ 0 ] , & error ) : RCTJSONStringify ( arguments , & error ) ;
2015-02-20 04:10:52 +00:00
if ( ! argsString ) {
RCTLogError ( @ "Cannot convert argument to string: %@" , error ) ;
onComplete ( nil , error ) ;
return ;
}
2015-05-23 02:33:21 +00:00
JSValueRef errorJSRef = NULL ;
JSValueRef resultJSRef = NULL ;
JSGlobalContextRef contextJSRef = JSContextGetGlobalContext ( strongSelf -> _context . ctx ) ;
JSObjectRef globalObjectJSRef = JSContextGetGlobalObject ( strongSelf -> _context . ctx ) ;
2015-12-08 23:57:34 +00:00
// get the BatchedBridge object
JSStringRef moduleNameJSStringRef = JSStringCreateWithUTF8CString ( "__fbBatchedBridge" ) ;
JSValueRef moduleJSRef = JSObjectGetProperty ( contextJSRef , globalObjectJSRef , moduleNameJSStringRef , & errorJSRef ) ;
JSStringRelease ( moduleNameJSStringRef ) ;
2015-05-23 02:33:21 +00:00
2015-12-08 23:57:34 +00:00
if ( moduleJSRef ! = NULL && errorJSRef = = NULL && ! JSValueIsUndefined ( contextJSRef , moduleJSRef ) ) {
// get method
JSStringRef methodNameJSStringRef = JSStringCreateWithCFString ( ( __bridge CFStringRef ) method ) ;
JSValueRef methodJSRef = JSObjectGetProperty ( contextJSRef , ( JSObjectRef ) moduleJSRef , methodNameJSStringRef , & errorJSRef ) ;
JSStringRelease ( methodNameJSStringRef ) ;
2015-05-23 02:33:21 +00:00
2015-12-18 01:26:12 +00:00
if ( methodJSRef ! = NULL && errorJSRef = = NULL && ! JSValueIsUndefined ( contextJSRef , methodJSRef ) ) {
2015-12-08 23:57:34 +00:00
// direct method invoke with no arguments
if ( arguments . count = = 0 ) {
resultJSRef = JSObjectCallAsFunction ( contextJSRef , ( JSObjectRef ) methodJSRef , ( JSObjectRef ) moduleJSRef , 0 , NULL , & errorJSRef ) ;
}
2015-05-23 02:33:21 +00:00
2015-12-08 23:57:34 +00:00
// 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 ) ;
2015-05-23 02:33:21 +00:00
2015-12-08 23:57:34 +00:00
} else {
// apply invoke with array of arguments
JSStringRef applyNameJSStringRef = JSStringCreateWithUTF8CString ( "apply" ) ;
JSValueRef applyJSRef = JSObjectGetProperty ( contextJSRef , ( JSObjectRef ) methodJSRef , applyNameJSStringRef , & errorJSRef ) ;
JSStringRelease ( applyNameJSStringRef ) ;
2015-05-27 15:21:14 +00:00
2015-12-08 23:57:34 +00:00
if ( applyJSRef ! = NULL && errorJSRef = = NULL ) {
// invoke apply
2015-05-23 02:33:21 +00:00
JSStringRef argsJSStringRef = JSStringCreateWithCFString ( ( __bridge CFStringRef ) argsString ) ;
JSValueRef argsJSRef = JSValueMakeFromJSONString ( contextJSRef , argsJSStringRef ) ;
2015-12-08 23:57:34 +00:00
JSValueRef args [ 2 ] ;
args [ 0 ] = JSValueMakeNull ( contextJSRef ) ;
args [ 1 ] = argsJSRef ;
2015-05-23 02:33:21 +00:00
2015-12-08 23:57:34 +00:00
resultJSRef = JSObjectCallAsFunction ( contextJSRef , ( JSObjectRef ) applyJSRef , ( JSObjectRef ) methodJSRef , 2 , args , & errorJSRef ) ;
JSStringRelease ( argsJSStringRef ) ;
2015-05-23 02:33:21 +00:00
}
}
2015-12-18 01:26:12 +00:00
} else {
if ( ! errorJSRef && JSValueIsUndefined ( contextJSRef , methodJSRef ) ) {
error = RCTErrorWithMessage ( [ NSString stringWithFormat : @ "Unable to execute JS call: method %@ is undefined" , method ] ) ;
}
}
} else {
if ( ! errorJSRef && JSValueIsUndefined ( contextJSRef , moduleJSRef ) ) {
error = RCTErrorWithMessage ( @ "Unable to execute JS call: __fbBatchedBridge is undefined" ) ;
2015-05-23 02:33:21 +00:00
}
}
2015-02-20 04:10:52 +00:00
2015-12-18 01:26:12 +00:00
if ( errorJSRef || error ) {
if ( ! error ) {
error = RCTNSErrorFromJSError ( contextJSRef , errorJSRef ) ;
}
onComplete ( nil , error ) ;
2015-02-20 04:10:52 +00:00
return ;
}
// Looks like making lots of JSC API calls is slower than communicating by using a JSON
// string . Also it ensures that data stuctures don ' t have cycles and non - serializable fields .
2015-12-16 10:49:27 +00:00
// see [ RCTJSCExecutorTests testDeserializationPerf ]
2015-02-20 04:10:52 +00:00
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
2015-05-23 02:33:21 +00:00
if ( ! JSValueIsNull ( contextJSRef , resultJSRef ) ) {
JSStringRef jsJSONString = JSValueCreateJSONString ( contextJSRef , resultJSRef , 0 , nil ) ;
2015-02-20 04:10:52 +00:00
if ( jsJSONString ) {
NSString * objcJSONString = ( __bridge _transfer NSString * ) JSStringCopyCFString ( kCFAllocatorDefault , jsJSONString ) ;
JSStringRelease ( jsJSONString ) ;
objcValue = RCTJSONParse ( objcJSONString , NULL ) ;
}
}
onComplete ( objcValue , nil ) ;
2015-12-08 23:57:34 +00:00
} ) , 0 , @ "js_call" , ( @ { @ "method" : method , @ "args" : arguments } ) ) ] ;
2015-02-20 04:10:52 +00:00
}
2015-10-16 15:10:25 +00:00
- ( void ) executeApplicationScript : ( NSData * ) script
2015-04-19 19:55:46 +00:00
sourceURL : ( NSURL * ) sourceURL
2015-02-20 04:10:52 +00:00
onComplete : ( RCTJavaScriptCompleteBlock ) onComplete
{
2015-08-07 13:42:34 +00:00
RCTAssertParam ( script ) ;
RCTAssertParam ( sourceURL ) ;
2015-04-19 19:55:46 +00:00
2015-12-16 10:49:27 +00:00
__weak RCTJSCExecutor * weakSelf = self ;
2015-04-20 11:55:05 +00:00
[ self executeBlockOnJavaScriptQueue : RCTProfileBlock ( ( ^ {
2015-12-16 10:49:27 +00:00
RCTJSCExecutor * strongSelf = weakSelf ;
2015-04-10 14:28:10 +00:00
if ( ! strongSelf || ! strongSelf . isValid ) {
return ;
}
2015-08-21 18:33:04 +00:00
2015-10-22 13:39:55 +00:00
RCTPerformanceLoggerStart ( RCTPLScriptExecution ) ;
2015-10-16 15:10:25 +00:00
// JSStringCreateWithUTF8CString expects a null terminated C string
NSMutableData * nullTerminatedScript = [ NSMutableData dataWithCapacity : script . length + 1 ] ;
[ nullTerminatedScript appendData : script ] ;
[ nullTerminatedScript appendBytes : "" length : 1 ] ;
2015-02-20 04:10:52 +00:00
JSValueRef jsError = NULL ;
2015-10-16 15:10:25 +00:00
JSStringRef execJSString = JSStringCreateWithUTF8CString ( nullTerminatedScript . bytes ) ;
2015-04-19 19:55:46 +00:00
JSStringRef jsURL = JSStringCreateWithCFString ( ( __bridge CFStringRef ) sourceURL . absoluteString ) ;
JSValueRef result = JSEvaluateScript ( strongSelf -> _context . ctx , execJSString , NULL , jsURL , 0 , & jsError ) ;
JSStringRelease ( jsURL ) ;
2015-02-20 04:10:52 +00:00
JSStringRelease ( execJSString ) ;
2015-08-21 18:33:04 +00:00
RCTPerformanceLoggerEnd ( RCTPLScriptExecution ) ;
2015-02-20 04:10:52 +00:00
2015-04-19 19:55:46 +00:00
if ( onComplete ) {
NSError * error ;
if ( ! result ) {
error = RCTNSErrorFromJSError ( strongSelf -> _context . ctx , jsError ) ;
}
onComplete ( error ) ;
2015-02-20 04:10:52 +00:00
}
2015-08-20 06:36:11 +00:00
} ) , 0 , @ "js_call" , ( @ { @ "url" : sourceURL . absoluteString } ) ) ] ;
2015-02-20 04:10:52 +00:00
}
- ( void ) executeBlockOnJavaScriptQueue : ( dispatch_block _t ) block
{
2015-05-28 20:16:06 +00:00
if ( [ NSThread currentThread ] ! = _javaScriptThread ) {
[ self performSelector : @ selector ( executeBlockOnJavaScriptQueue : )
onThread : _javaScriptThread withObject : block waitUntilDone : NO ] ;
} else {
block ( ) ;
}
2015-04-28 15:02:56 +00:00
}
- ( void ) executeAsyncBlockOnJavaScriptQueue : ( dispatch_block _t ) block
{
[ self performSelector : @ selector ( executeBlockOnJavaScriptQueue : )
onThread : _javaScriptThread
withObject : block
waitUntilDone : NO ] ;
2015-04-26 02:18:39 +00:00
}
- ( void ) _runBlock : ( dispatch_block _t ) block
{
block ( ) ;
2015-02-20 04:10:52 +00:00
}
- ( void ) injectJSONText : ( NSString * ) script
asGlobalObjectNamed : ( NSString * ) objectName
callback : ( RCTJavaScriptCompleteBlock ) onComplete
{
2015-04-21 12:26:51 +00:00
if ( RCT_DEBUG ) {
RCTAssert ( RCTJSONParse ( script , NULL ) ! = nil , @ "%@ wasn't valid JSON!" , script ) ;
}
2015-02-20 04:10:52 +00:00
2015-12-16 10:49:27 +00:00
__weak RCTJSCExecutor * weakSelf = self ;
2015-04-20 11:55:05 +00:00
[ self executeBlockOnJavaScriptQueue : RCTProfileBlock ( ( ^ {
2015-12-16 10:49:27 +00:00
RCTJSCExecutor * strongSelf = weakSelf ;
2015-04-10 14:28:10 +00:00
if ( ! strongSelf || ! strongSelf . isValid ) {
return ;
}
2015-02-20 04:10:52 +00:00
JSStringRef execJSString = JSStringCreateWithCFString ( ( __bridge CFStringRef ) script ) ;
2015-04-10 14:28:10 +00:00
JSValueRef valueToInject = JSValueMakeFromJSONString ( strongSelf -> _context . ctx , execJSString ) ;
2015-02-20 04:10:52 +00:00
JSStringRelease ( execJSString ) ;
if ( ! valueToInject ) {
NSString * errorDesc = [ NSString stringWithFormat : @ "Can't make JSON value from script '%@'" , script ] ;
RCTLogError ( @ "%@" , errorDesc ) ;
2015-04-19 19:55:46 +00:00
if ( onComplete ) {
NSError * error = [ NSError errorWithDomain : @ "JS" code : 2 userInfo : @ { NSLocalizedDescriptionKey : errorDesc } ] ;
onComplete ( error ) ;
}
2015-02-20 04:10:52 +00:00
return ;
}
2015-04-10 14:28:10 +00:00
JSObjectRef globalObject = JSContextGetGlobalObject ( strongSelf -> _context . ctx ) ;
2015-02-20 04:10:52 +00:00
JSStringRef JSName = JSStringCreateWithCFString ( ( __bridge CFStringRef ) objectName ) ;
2015-04-10 14:28:10 +00:00
JSObjectSetProperty ( strongSelf -> _context . ctx , globalObject , JSName , valueToInject , kJSPropertyAttributeNone , NULL ) ;
2015-02-20 04:10:52 +00:00
JSStringRelease ( JSName ) ;
2015-04-19 19:55:46 +00:00
if ( onComplete ) {
onComplete ( nil ) ;
}
2015-08-20 06:36:11 +00:00
} ) , 0 , @ "js_call,json_call" , ( @ { @ "objectName" : objectName } ) ) ] ;
2015-02-20 04:10:52 +00:00
}
2015-08-11 20:18:08 +00:00
RCT_EXPORT _METHOD ( setContextName : ( nonnull NSString * ) name )
2015-08-04 13:05:31 +00:00
{
2015-09-24 09:47:43 +00:00
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtautological-pointer-compare"
2015-08-04 13:05:31 +00:00
if ( JSGlobalContextSetName ! = NULL ) {
2015-09-24 09:47:43 +00:00
# pragma clang diagnostic pop
2015-08-04 13:05:31 +00:00
JSStringRef JSName = JSStringCreateWithCFString ( ( __bridge CFStringRef ) name ) ;
JSGlobalContextSetName ( _context . ctx , JSName ) ;
JSStringRelease ( JSName ) ;
}
}
2015-02-20 04:10:52 +00:00
@ end