Recognise and run BC bundles
Reviewed By: javache Differential Revision: D4067425 fbshipit-source-id: fade9adebfa8a59dc49aeadfd01a782f7b686082
This commit is contained in:
parent
94711bfb06
commit
16290851aa
|
@ -9,7 +9,40 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern uint32_t const RCTRAMBundleMagicNumber;
|
||||
#import "RCTDefines.h"
|
||||
|
||||
/**
|
||||
* RCTScriptTag
|
||||
*
|
||||
* Scripts given to the JS Executors to run could be in any of the following
|
||||
* formats. They are tagged so the executor knows how to run them.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger) {
|
||||
RCTScriptString = 0,
|
||||
RCTScriptRAMBundle,
|
||||
RCTScriptBCBundle,
|
||||
} RCTScriptTag;
|
||||
|
||||
/**
|
||||
* RCTMagicNumber
|
||||
*
|
||||
* RAM bundles and BC bundles begin with magic numbers. For RAM bundles this is
|
||||
* 4 bytes, for BC bundles this is 8 bytes. This structure holds the first 8
|
||||
* bytes from a bundle in a way that gives access to that information.
|
||||
*/
|
||||
typedef union {
|
||||
uint64_t allBytes;
|
||||
uint32_t first4;
|
||||
uint64_t first8;
|
||||
} RCTMagicNumber;
|
||||
|
||||
/**
|
||||
* RCTParseMagicNumber
|
||||
*
|
||||
* Takes the first 8 bytes of a bundle, and returns a tag describing the
|
||||
* bundle's format.
|
||||
*/
|
||||
RCT_EXTERN RCTScriptTag RCTParseMagicNumber(RCTMagicNumber magic);
|
||||
|
||||
extern NSString *const RCTJavaScriptLoaderErrorDomain;
|
||||
|
||||
|
@ -42,7 +75,7 @@ typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source, int64_t sourc
|
|||
* @experimental
|
||||
* Attempts to synchronously load the script at the given URL. The following two conditions must be met:
|
||||
* 1. It must be a file URL.
|
||||
* 2. It must point to a RAM bundle.
|
||||
* 2. It must not point to a text/javascript file.
|
||||
* If the URL does not meet those conditions, this method will return nil and supply an error with the domain
|
||||
* RCTJavaScriptLoaderErrorDomain and the code RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously.
|
||||
*/
|
||||
|
|
|
@ -18,10 +18,24 @@
|
|||
|
||||
#include <sys/stat.h>
|
||||
|
||||
uint32_t const RCTRAMBundleMagicNumber = 0xFB0BD1E5;
|
||||
static uint32_t const RCTRAMBundleMagicNumber = 0xFB0BD1E5;
|
||||
static uint64_t const RCTBCBundleMagicNumber = 0xFF4865726D657300;
|
||||
|
||||
NSString *const RCTJavaScriptLoaderErrorDomain = @"RCTJavaScriptLoaderErrorDomain";
|
||||
|
||||
RCTScriptTag RCTParseMagicNumber(RCTMagicNumber magic)
|
||||
{
|
||||
if (NSSwapLittleIntToHost(magic.first4) == RCTRAMBundleMagicNumber) {
|
||||
return RCTScriptRAMBundle;
|
||||
}
|
||||
|
||||
if (NSSwapLittleLongLongToHost(magic.first8) == RCTBCBundleMagicNumber) {
|
||||
return RCTScriptBCBundle;
|
||||
}
|
||||
|
||||
return RCTScriptString;
|
||||
}
|
||||
|
||||
@implementation RCTLoadingProgress
|
||||
|
||||
- (NSString *)description
|
||||
|
@ -112,7 +126,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return nil;
|
||||
}
|
||||
|
||||
uint32_t magicNumber;
|
||||
RCTMagicNumber magicNumber = {.allBytes = 0};
|
||||
size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle);
|
||||
fclose(bundle);
|
||||
if (readResult != 1) {
|
||||
|
@ -125,13 +139,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return nil;
|
||||
}
|
||||
|
||||
magicNumber = NSSwapLittleIntToHost(magicNumber);
|
||||
if (magicNumber != RCTRAMBundleMagicNumber) {
|
||||
RCTScriptTag tag = RCTParseMagicNumber(magicNumber);
|
||||
if (tag == RCTScriptString) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain
|
||||
code:RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously
|
||||
userInfo:@{NSLocalizedDescriptionKey:
|
||||
@"Cannot load non-RAM bundled files synchronously"}];
|
||||
@"Cannot load text/javascript files synchronously"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,11 @@ struct RandomAccessBundleStartupCode {
|
|||
}
|
||||
};
|
||||
|
||||
struct TaggedScript {
|
||||
const RCTScriptTag tag;
|
||||
const NSData *script;
|
||||
};
|
||||
|
||||
#if RCT_PROFILE
|
||||
@interface RCTCookieMap : NSObject
|
||||
{
|
||||
|
@ -280,15 +285,20 @@ static NSThread *newJavaScriptThread(void)
|
|||
JSContext:(JSContext *)context
|
||||
error:(NSError **)error
|
||||
{
|
||||
BOOL isRAMBundle = NO;
|
||||
script = loadPossiblyBundledApplicationScript(script, sourceURL, _performanceLogger, isRAMBundle, _randomAccessBundle, error);
|
||||
if (!script) {
|
||||
TaggedScript taggedScript = loadTaggedScript(script, sourceURL, _performanceLogger, _randomAccessBundle, error);
|
||||
|
||||
if (!taggedScript.script) {
|
||||
return NO;
|
||||
}
|
||||
if (isRAMBundle) {
|
||||
|
||||
if (taggedScript.tag == RCTScriptRAMBundle) {
|
||||
registerNativeRequire(context, self);
|
||||
}
|
||||
NSError *returnedError = executeApplicationScript(script, sourceURL, _jscWrapper, _performanceLogger, _context.context.JSGlobalContextRef);
|
||||
|
||||
NSError *returnedError = executeApplicationScript(taggedScript, sourceURL,
|
||||
_jscWrapper,
|
||||
_performanceLogger,
|
||||
_context.context.JSGlobalContextRef);
|
||||
if (returnedError) {
|
||||
if (error) {
|
||||
*error = returnedError;
|
||||
|
@ -652,16 +662,16 @@ RCT_EXPORT_METHOD(setContextName:(nonnull NSString *)contextName)
|
|||
RCTAssertParam(script);
|
||||
RCTAssertParam(sourceURL);
|
||||
|
||||
BOOL isRAMBundle = NO;
|
||||
{
|
||||
NSError *error;
|
||||
script = loadPossiblyBundledApplicationScript(script, sourceURL, _performanceLogger, isRAMBundle, _randomAccessBundle, &error);
|
||||
if (script == nil) {
|
||||
if (onComplete) {
|
||||
onComplete(error);
|
||||
}
|
||||
return;
|
||||
NSError *loadError;
|
||||
TaggedScript taggedScript = loadTaggedScript(script, sourceURL,
|
||||
_performanceLogger,
|
||||
_randomAccessBundle,
|
||||
&loadError);
|
||||
if (!taggedScript.script) {
|
||||
if (onComplete) {
|
||||
onComplete(loadError);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RCTProfileBeginFlowEvent();
|
||||
|
@ -671,11 +681,13 @@ RCT_EXPORT_METHOD(setContextName:(nonnull NSString *)contextName)
|
|||
return;
|
||||
}
|
||||
|
||||
if (isRAMBundle) {
|
||||
if (taggedScript.tag == RCTScriptRAMBundle) {
|
||||
registerNativeRequire(self.context.context, self);
|
||||
}
|
||||
|
||||
NSError *error = executeApplicationScript(script, sourceURL, self->_jscWrapper, self->_performanceLogger,
|
||||
NSError *error = executeApplicationScript(taggedScript, sourceURL,
|
||||
self->_jscWrapper,
|
||||
self->_performanceLogger,
|
||||
self->_context.context.JSGlobalContextRef);
|
||||
if (onComplete) {
|
||||
onComplete(error);
|
||||
|
@ -683,33 +695,42 @@ RCT_EXPORT_METHOD(setContextName:(nonnull NSString *)contextName)
|
|||
}];
|
||||
}
|
||||
|
||||
static NSData *loadPossiblyBundledApplicationScript(NSData *script, NSURL *sourceURL,
|
||||
RCTPerformanceLogger *performanceLogger,
|
||||
BOOL &isRAMBundle, RandomAccessBundleData &randomAccessBundle,
|
||||
NSError **error)
|
||||
static TaggedScript loadTaggedScript(NSData *script,
|
||||
NSURL *sourceURL,
|
||||
RCTPerformanceLogger *performanceLogger,
|
||||
RandomAccessBundleData &randomAccessBundle,
|
||||
NSError **error)
|
||||
{
|
||||
RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / prepare bundle", nil);
|
||||
|
||||
// The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`.
|
||||
uint32_t magicNumber = 0;
|
||||
RCTMagicNumber magicNumber = {.allBytes = 0};
|
||||
[script getBytes:&magicNumber length:sizeof(magicNumber)];
|
||||
isRAMBundle = NSSwapLittleIntToHost(magicNumber) == RCTRAMBundleMagicNumber;
|
||||
if (isRAMBundle) {
|
||||
[performanceLogger markStartForTag:RCTPLRAMBundleLoad];
|
||||
script = loadRAMBundle(sourceURL, error, randomAccessBundle);
|
||||
[performanceLogger markStopForTag:RCTPLRAMBundleLoad];
|
||||
[performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize];
|
||||
} 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;
|
||||
RCTScriptTag tag = RCTParseMagicNumber(magicNumber);
|
||||
|
||||
NSData *loadedScript = NULL;
|
||||
switch (tag) {
|
||||
case RCTScriptRAMBundle:
|
||||
[performanceLogger markStartForTag:RCTPLRAMBundleLoad];
|
||||
|
||||
loadedScript = loadRAMBundle(sourceURL, error, randomAccessBundle);
|
||||
|
||||
[performanceLogger markStopForTag:RCTPLRAMBundleLoad];
|
||||
[performanceLogger setValue:loadedScript.length forTag:RCTPLRAMStartupCodeSize];
|
||||
break;
|
||||
|
||||
case RCTScriptBCBundle:
|
||||
loadedScript = script;
|
||||
break;
|
||||
|
||||
case RCTScriptString: {
|
||||
NSMutableData *nullTerminatedScript = [NSMutableData dataWithData:script];
|
||||
[nullTerminatedScript appendBytes:"" length:1];
|
||||
loadedScript = nullTerminatedScript;
|
||||
}
|
||||
}
|
||||
|
||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
|
||||
return script;
|
||||
return { .tag = tag, .script = loadedScript };
|
||||
}
|
||||
|
||||
static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor)
|
||||
|
@ -718,22 +739,46 @@ static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor)
|
|||
context[@"nativeRequire"] = ^(NSNumber *moduleID) { [weakExecutor _nativeRequire:moduleID]; };
|
||||
}
|
||||
|
||||
static NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJSCWrapper *jscWrapper,
|
||||
RCTPerformanceLogger *performanceLogger, JSGlobalContextRef ctx)
|
||||
static NSError *executeApplicationScript(TaggedScript taggedScript,
|
||||
NSURL *sourceURL,
|
||||
RCTJSCWrapper *jscWrapper,
|
||||
RCTPerformanceLogger *performanceLogger,
|
||||
JSGlobalContextRef ctx)
|
||||
{
|
||||
RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / execute script", (@{
|
||||
@"url": sourceURL.absoluteString, @"size": @(script.length)
|
||||
@"url": sourceURL.absoluteString, @"size": @(taggedScript.script.length)
|
||||
}));
|
||||
|
||||
[performanceLogger markStartForTag:RCTPLScriptExecution];
|
||||
JSValueRef jsError = NULL;
|
||||
JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes);
|
||||
JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String);
|
||||
jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError);
|
||||
|
||||
switch (taggedScript.tag) {
|
||||
case RCTScriptRAMBundle:
|
||||
/* fallthrough */
|
||||
case RCTScriptString: {
|
||||
JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)taggedScript.script.bytes);
|
||||
jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError);
|
||||
jscWrapper->JSStringRelease(execJSString);
|
||||
break;
|
||||
}
|
||||
|
||||
case RCTScriptBCBundle: {
|
||||
file_ptr source(fopen(sourceURL.path.UTF8String, "r"), fclose);
|
||||
int sourceFD = fileno(source.get());
|
||||
|
||||
jscWrapper->JSEvaluateBytecodeBundle(ctx, NULL, sourceFD, bundleURL, &jsError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jscWrapper->JSStringRelease(bundleURL);
|
||||
jscWrapper->JSStringRelease(execJSString);
|
||||
[performanceLogger markStopForTag:RCTPLScriptExecution];
|
||||
|
||||
NSError *error = jsError ? RCTNSErrorFromJSErrorRef(jsError, ctx, jscWrapper) : nil;
|
||||
NSError *error = jsError
|
||||
? RCTNSErrorFromJSErrorRef(jsError, ctx, jscWrapper)
|
||||
: nil;
|
||||
|
||||
RCT_PROFILE_END_EVENT(0, @"js_call");
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ typedef JSStringRef (*JSValueCreateJSONStringFuncType)(JSContextRef, JSValueRef,
|
|||
typedef bool (*JSValueIsUndefinedFuncType)(JSContextRef, JSValueRef);
|
||||
typedef bool (*JSValueIsNullFuncType)(JSContextRef, JSValueRef);
|
||||
typedef JSValueRef (*JSEvaluateScriptFuncType)(JSContextRef, JSStringRef, JSObjectRef, JSStringRef, int, JSValueRef *);
|
||||
typedef JSValueRef (*JSEvaluateBytecodeBundleFuncType)(JSContextRef, JSObjectRef, int, JSStringRef, JSValueRef *);
|
||||
|
||||
typedef struct RCTJSCWrapper {
|
||||
JSStringCreateWithCFStringFuncType JSStringCreateWithCFString;
|
||||
|
@ -41,6 +42,7 @@ typedef struct RCTJSCWrapper {
|
|||
JSValueIsUndefinedFuncType JSValueIsUndefined;
|
||||
JSValueIsNullFuncType JSValueIsNull;
|
||||
JSEvaluateScriptFuncType JSEvaluateScript;
|
||||
JSEvaluateBytecodeBundleFuncType JSEvaluateBytecodeBundle;
|
||||
Class JSContext;
|
||||
Class JSValue;
|
||||
} RCTJSCWrapper;
|
||||
|
|
|
@ -26,7 +26,7 @@ static void *RCTCustomLibraryHandler(void)
|
|||
static dispatch_once_t token;
|
||||
static void *handler;
|
||||
dispatch_once(&token, ^{
|
||||
handler = dlopen("@executable_path/Frameworks/JSC.framework/JSC", RTLD_LAZY | RTLD_LOCAL);
|
||||
handler = dlopen("@loader_path/Frameworks/JSC.framework/JSC", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handler) {
|
||||
const char *err = dlerror();
|
||||
|
||||
|
@ -57,6 +57,7 @@ static void RCTSetUpSystemLibraryPointers(RCTJSCWrapper *wrapper)
|
|||
wrapper->JSValueIsUndefined = JSValueIsUndefined;
|
||||
wrapper->JSValueIsNull = JSValueIsNull;
|
||||
wrapper->JSEvaluateScript = JSEvaluateScript;
|
||||
wrapper->JSEvaluateBytecodeBundle = NULL;
|
||||
wrapper->JSContext = [JSContext class];
|
||||
wrapper->JSValue = [JSValue class];
|
||||
}
|
||||
|
@ -83,6 +84,7 @@ static void RCTSetUpCustomLibraryPointers(RCTJSCWrapper *wrapper)
|
|||
wrapper->JSValueIsUndefined = (JSValueIsUndefinedFuncType)dlsym(libraryHandle, "JSValueIsUndefined");
|
||||
wrapper->JSValueIsNull = (JSValueIsNullFuncType)dlsym(libraryHandle, "JSValueIsNull");
|
||||
wrapper->JSEvaluateScript = (JSEvaluateScriptFuncType)dlsym(libraryHandle, "JSEvaluateScript");
|
||||
wrapper->JSEvaluateBytecodeBundle = (JSEvaluateBytecodeBundleFuncType)dlsym(libraryHandle, "JSEvaluateBytecodeBundle");
|
||||
wrapper->JSContext = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSContext");
|
||||
wrapper->JSValue = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSValue");
|
||||
|
||||
|
|
Loading…
Reference in New Issue