Refine RCTJSCExecutor's APIs for using a thread/context that already exists
Summary: The `initWithJSContextProvider:` API created a `RCTJSCExecutor` with a thread/context that already exists, but it did not solve the problem of loading an actual application script; the `executeApplicationScript:` API is also asynchronous. Create a new merged API that allows you to pass in a pre-created thread/context pair and immediately receive an `RCTJSCExector` that has already executed a specified application script. This also removes the `underlyingJSContext` API entirely, in favor of passing it back in a byref variable in the new API. This minimizes the surface area for API abuse. Reviewed By: bnham Differential Revision: D3545349 fbshipit-source-id: 1c564f44d2a5379b5e6f75640079a28fd7169f67
This commit is contained in:
parent
38a6eec0db
commit
80c71e5cae
|
@ -54,11 +54,6 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
|||
*/
|
||||
- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary;
|
||||
|
||||
/**
|
||||
* Pass a RCTJSContextProvider object to use an NSThread/JSContext pair that have already been created.
|
||||
*/
|
||||
- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider;
|
||||
|
||||
/**
|
||||
* Create a NSError from a JSError object.
|
||||
*
|
||||
|
@ -68,8 +63,16 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
|||
- (NSError *)convertJSErrorToNSError:(JSValueRef)jsError context:(JSContextRef)context;
|
||||
|
||||
/**
|
||||
* Returns the underlying JSContext.
|
||||
* @experimental
|
||||
* Pass a RCTJSContextProvider object to use an NSThread/JSContext pair that have already been created.
|
||||
* The returned executor has already executed the supplied application script synchronously.
|
||||
* The underlying JSContext will be returned in the JSContext pointer if it is non-NULL and there was no error.
|
||||
* If an error occurs, this method will return nil and specify the error in the error pointer if it is non-NULL.
|
||||
*/
|
||||
- (JSContext *)underlyingJSContext;
|
||||
+ (instancetype)initializedExecutorWithContextProvider:(RCTJSContextProvider *)JSContextProvider
|
||||
applicationScript:(NSData *)applicationScript
|
||||
sourceURL:(NSURL *)sourceURL
|
||||
JSContext:(JSContext **)JSContext
|
||||
error:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
|
|
@ -320,10 +320,26 @@ static NSThread *newJavaScriptThread(void)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider
|
||||
+ (instancetype)initializedExecutorWithContextProvider:(RCTJSContextProvider *)JSContextProvider
|
||||
applicationScript:(NSData *)applicationScript
|
||||
sourceURL:(NSURL *)sourceURL
|
||||
JSContext:(JSContext **)JSContext
|
||||
error:(NSError **)error
|
||||
{
|
||||
const RCTJSContextData data = JSContextProvider.data;
|
||||
if (JSContext) {
|
||||
*JSContext = data.context;
|
||||
}
|
||||
RCTJSCExecutor *executor = [[RCTJSCExecutor alloc] initWithJSContextData:data];
|
||||
if (![executor _synchronouslyExecuteApplicationScript:applicationScript sourceURL:sourceURL JSContext:data.context error:error]) {
|
||||
return nil; // error has been set by _synchronouslyExecuteApplicationScript:
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
|
||||
- (instancetype)initWithJSContextData:(const RCTJSContextData &)data
|
||||
{
|
||||
if (self = [super init]) {
|
||||
const RCTJSContextData data = JSContextProvider.data;
|
||||
_useCustomJSCLibrary = data.useCustomJSCLibrary;
|
||||
_valid = YES;
|
||||
_javaScriptThread = data.javaScriptThread;
|
||||
|
@ -333,6 +349,30 @@ static NSThread *newJavaScriptThread(void)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)_synchronouslyExecuteApplicationScript:(NSData *)script
|
||||
sourceURL:(NSURL *)sourceURL
|
||||
JSContext:(JSContext *)context
|
||||
error:(NSError **)error
|
||||
{
|
||||
BOOL isRAMBundle = NO;
|
||||
script = loadPossiblyBundledApplicationScript(script, sourceURL, _performanceLogger, isRAMBundle, _randomAccessBundle, error);
|
||||
if (!script) {
|
||||
return NO;
|
||||
}
|
||||
if (isRAMBundle) {
|
||||
registerNativeRequire(context, self);
|
||||
}
|
||||
NSError *returnedError = executeApplicationScript(script, sourceURL, _jscWrapper, _performanceLogger, _context.context.JSGlobalContextRef);
|
||||
if (returnedError) {
|
||||
if (error) {
|
||||
*error = returnedError;
|
||||
}
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (RCTJavaScriptContext *)context
|
||||
{
|
||||
RCTAssertThread(_javaScriptThread, @"Must be called on JS thread.");
|
||||
|
@ -343,11 +383,6 @@ static NSThread *newJavaScriptThread(void)
|
|||
return _context;
|
||||
}
|
||||
|
||||
- (JSContext *)underlyingJSContext
|
||||
{
|
||||
return self.context.context;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
#if RCT_PROFILE
|
||||
|
@ -662,36 +697,16 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
|||
RCTAssertParam(script);
|
||||
RCTAssertParam(sourceURL);
|
||||
|
||||
// The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`.
|
||||
uint32_t magicNumber = NSSwapLittleIntToHost(*((uint32_t *)script.bytes));
|
||||
BOOL isRAMBundle = magicNumber == RCTRAMBundleMagicNumber;
|
||||
if (isRAMBundle) {
|
||||
[_performanceLogger markStartForTag:RCTPLRAMBundleLoad];
|
||||
BOOL isRAMBundle = NO;
|
||||
{
|
||||
NSError *error;
|
||||
script = loadRAMBundle(sourceURL, &error, _randomAccessBundle);
|
||||
[_performanceLogger markStopForTag:RCTPLRAMBundleLoad];
|
||||
[_performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize];
|
||||
|
||||
// Reset the counters that the native require implementation uses
|
||||
[_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires];
|
||||
[_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount];
|
||||
[_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize];
|
||||
|
||||
if (error) {
|
||||
script = loadPossiblyBundledApplicationScript(script, sourceURL, _performanceLogger, isRAMBundle, _randomAccessBundle, &error);
|
||||
if (script == nil) {
|
||||
if (onComplete) {
|
||||
onComplete(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// JSStringCreateWithUTF8CString expects a null terminated C string.
|
||||
// RAM Bundling already provides a null terminated one.
|
||||
NSMutableData *nullTerminatedScript = [NSMutableData dataWithCapacity:script.length + 1];
|
||||
|
||||
[nullTerminatedScript appendData:script];
|
||||
[nullTerminatedScript appendBytes:"" length:1];
|
||||
|
||||
script = nullTerminatedScript;
|
||||
}
|
||||
|
||||
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
|
||||
|
@ -699,26 +714,63 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
|||
return;
|
||||
}
|
||||
if (isRAMBundle) {
|
||||
__weak RCTJSCExecutor *weakSelf = self;
|
||||
self.context.context[@"nativeRequire"] = ^(NSNumber *moduleID) { [weakSelf _nativeRequire:moduleID]; };
|
||||
registerNativeRequire(self.context.context, self);
|
||||
}
|
||||
[self->_performanceLogger markStartForTag:RCTPLScriptExecution];
|
||||
NSError *error = executeApplicationScript(self->_jscWrapper, script, sourceURL, self->_context.context.JSGlobalContextRef);
|
||||
[self->_performanceLogger markStopForTag:RCTPLScriptExecution];
|
||||
NSError *error = executeApplicationScript(script, sourceURL, self->_jscWrapper, self->_performanceLogger,
|
||||
self->_context.context.JSGlobalContextRef);
|
||||
if (onComplete) {
|
||||
onComplete(error);
|
||||
}
|
||||
}), 0, @"js_call", (@{ @"url": sourceURL.absoluteString }))];
|
||||
}
|
||||
|
||||
static NSError *executeApplicationScript(RCTJSCWrapper *jscWrapper, NSData *script, NSURL *sourceURL, JSGlobalContextRef ctx)
|
||||
static NSData *loadPossiblyBundledApplicationScript(NSData *script, NSURL *sourceURL,
|
||||
RCTPerformanceLogger *performanceLogger,
|
||||
BOOL &isRAMBundle, RandomAccessBundleData &randomAccessBundle,
|
||||
NSError **error)
|
||||
{
|
||||
// The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`.
|
||||
uint32_t magicNumber = NSSwapLittleIntToHost(*((uint32_t *)script.bytes));
|
||||
isRAMBundle = magicNumber == RCTRAMBundleMagicNumber;
|
||||
if (isRAMBundle) {
|
||||
[performanceLogger markStartForTag:RCTPLRAMBundleLoad];
|
||||
script = loadRAMBundle(sourceURL, error, randomAccessBundle);
|
||||
[performanceLogger markStopForTag:RCTPLRAMBundleLoad];
|
||||
[performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize];
|
||||
|
||||
// Reset the counters that the native require implementation uses
|
||||
[performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires];
|
||||
[performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount];
|
||||
[performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize];
|
||||
|
||||
return script;
|
||||
} else {
|
||||
// JSStringCreateWithUTF8CString expects a null terminated C string.
|
||||
// RAM Bundling already provides a null terminated one.
|
||||
NSMutableData *nullTerminatedScript = [NSMutableData dataWithCapacity:script.length + 1];
|
||||
[nullTerminatedScript appendData:script];
|
||||
[nullTerminatedScript appendBytes:"" length:1];
|
||||
return nullTerminatedScript;
|
||||
}
|
||||
}
|
||||
|
||||
static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor)
|
||||
{
|
||||
__weak RCTJSCExecutor *weakExecutor = executor;
|
||||
context[@"nativeRequire"] = ^(NSNumber *moduleID) { [weakExecutor _nativeRequire:moduleID]; };
|
||||
}
|
||||
|
||||
static NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJSCWrapper *jscWrapper,
|
||||
RCTPerformanceLogger *performanceLogger, JSGlobalContextRef ctx)
|
||||
{
|
||||
[performanceLogger markStartForTag:RCTPLScriptExecution];
|
||||
JSValueRef jsError = NULL;
|
||||
JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes);
|
||||
JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String);
|
||||
JSValueRef result = jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError);
|
||||
jscWrapper->JSStringRelease(bundleURL);
|
||||
jscWrapper->JSStringRelease(execJSString);
|
||||
[performanceLogger markStopForTag:RCTPLScriptExecution];
|
||||
return result ? nil : RCTNSErrorFromJSError(jscWrapper, ctx, jsError);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue