From 8288bc200680f53c09cce16fd76da53b7c279716 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Fri, 11 Nov 2016 05:21:37 -0800 Subject: [PATCH] BC Soft Errors Reviewed By: javache Differential Revision: D4131340 fbshipit-source-id: b0827fc99d94985fbbb2a1b03ddc4d6feab10bff --- React/Base/RCTJavaScriptLoader.h | 28 +++++++++++++++-------- React/Base/RCTJavaScriptLoader.m | 38 ++++++++++++++++++++++++------- React/Executors/RCTJSCExecutor.h | 5 ++++ React/Executors/RCTJSCExecutor.mm | 11 ++++++--- React/Executors/RCTJSCWrapper.mm | 2 +- 5 files changed, 63 insertions(+), 21 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.h b/React/Base/RCTJavaScriptLoader.h index 8d09f07220..774d794102 100755 --- a/React/Base/RCTJavaScriptLoader.h +++ b/React/Base/RCTJavaScriptLoader.h @@ -24,25 +24,33 @@ typedef NS_ENUM(NSInteger) { } RCTScriptTag; /** - * RCTMagicNumber + * RCTBundleHeader * - * 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 + * RAM bundles and BC bundles begin with headers. For RAM bundles this is + * 4 bytes, for BC bundles this is 12 bytes. This structure holds the first 12 * 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; + // `allBytes` is the first field so that zero-initializing the union fills + // it completely with zeroes. Without it, only the first field of the union + // gets zero-initialized. + uint32_t allBytes[3]; + + uint32_t RAMMagic; + + struct { + uint64_t BCMagic; + uint32_t BCVersion; + }; +} RCTBundleHeader; /** - * RCTParseMagicNumber + * RCTParseTypeFromHeader * * Takes the first 8 bytes of a bundle, and returns a tag describing the * bundle's format. */ -RCT_EXTERN RCTScriptTag RCTParseMagicNumber(RCTMagicNumber magic); +RCT_EXTERN RCTScriptTag RCTParseTypeFromHeader(RCTBundleHeader header); extern NSString *const RCTJavaScriptLoaderErrorDomain; @@ -52,6 +60,7 @@ NS_ENUM(NSInteger) { RCTJavaScriptLoaderErrorFailedReadingFile = 3, RCTJavaScriptLoaderErrorFailedStatingFile = 3, RCTJavaScriptLoaderErrorURLLoadFailed = 3, + RCTJavaScriptLoaderErrorBCVersion = 4, RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously = 1000, }; @@ -80,6 +89,7 @@ typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source, int64_t sourc * RCTJavaScriptLoaderErrorDomain and the code RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously. */ + (NSData *)attemptSynchronousLoadOfBundleAtURL:(NSURL *)scriptURL + runtimeBCVersion:(int32_t)runtimeBCVersion sourceLength:(int64_t *)sourceLength error:(NSError **)error; diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index dedd936b6a..fe9389c67a 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -11,6 +11,7 @@ #import "RCTBridge.h" #import "RCTConvert.h" +#import "RCTJSCWrapper.h" #import "RCTSourceCode.h" #import "RCTUtils.h" #import "RCTPerformanceLogger.h" @@ -23,13 +24,13 @@ static uint64_t const RCTBCBundleMagicNumber = 0xFF4865726D657300; NSString *const RCTJavaScriptLoaderErrorDomain = @"RCTJavaScriptLoaderErrorDomain"; -RCTScriptTag RCTParseMagicNumber(RCTMagicNumber magic) +RCTScriptTag RCTParseTypeFromHeader(RCTBundleHeader header) { - if (NSSwapLittleIntToHost(magic.first4) == RCTRAMBundleMagicNumber) { + if (NSSwapLittleIntToHost(header.RAMMagic) == RCTRAMBundleMagicNumber) { return RCTScriptRAMBundle; } - if (NSSwapLittleLongLongToHost(magic.first8) == RCTBCBundleMagicNumber) { + if (NSSwapLittleLongLongToHost(header.BCMagic) == RCTBCBundleMagicNumber) { return RCTScriptBCBundle; } @@ -61,6 +62,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) int64_t sourceLength; NSError *error; NSData *data = [self attemptSynchronousLoadOfBundleAtURL:scriptURL + runtimeBCVersion:JSNoBytecodeFileFormatVersion sourceLength:&sourceLength error:&error]; if (data) { @@ -80,6 +82,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } + (NSData *)attemptSynchronousLoadOfBundleAtURL:(NSURL *)scriptURL + runtimeBCVersion:(int32_t)runtimeBCVersion sourceLength:(int64_t *)sourceLength error:(NSError **)error { @@ -126,8 +129,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return nil; } - RCTMagicNumber magicNumber = {.allBytes = 0}; - size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); + RCTBundleHeader header = {}; + size_t readResult = fread(&header, sizeof(header), 1, bundle); fclose(bundle); if (readResult != 1) { if (error) { @@ -139,8 +142,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return nil; } - RCTScriptTag tag = RCTParseMagicNumber(magicNumber); - if (tag == RCTScriptString) { + RCTScriptTag tag = RCTParseTypeFromHeader(header); + switch (tag) { + case RCTScriptRAMBundle: + break; + + case RCTScriptString: if (error) { *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain code:RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously @@ -148,6 +155,21 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) @"Cannot load text/javascript files synchronously"}]; } return nil; + + case RCTScriptBCBundle: + if (header.BCVersion != runtimeBCVersion) { + if (error) { + NSString *errDesc = + [NSString stringWithFormat:@"BC Version Mismatch. Expect: %d, Actual: %d", + runtimeBCVersion, header.BCVersion]; + + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorBCVersion + userInfo:@{NSLocalizedDescriptionKey: errDesc}]; + } + return nil; + } + break; } struct stat statInfo; @@ -163,7 +185,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) if (sourceLength) { *sourceLength = statInfo.st_size; } - return [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; + return [NSData dataWithBytes:&header length:sizeof(header)]; } static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoadProgressBlock onProgress, RCTSourceLoadBlock onComplete) diff --git a/React/Executors/RCTJSCExecutor.h b/React/Executors/RCTJSCExecutor.h index baa55245cb..50c55761fb 100644 --- a/React/Executors/RCTJSCExecutor.h +++ b/React/Executors/RCTJSCExecutor.h @@ -72,6 +72,11 @@ RCT_EXTERN NSString *const RCTFBJSValueClassKey; */ @property (nonatomic, readonly, assign) BOOL useCustomJSCLibrary; +/** + * Returns the bytecode file format that the underlying runtime supports. + */ +@property (nonatomic, readonly) int32_t bytecodeFileFormatVersion; + /** * Specify a name for the JSContext used, which will be visible in debugging tools * @default is "RCTJSContext" diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index fd70593728..0a889462f1 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -500,6 +500,11 @@ static void installBasicSynchronousHooksOnContext(JSContext *context) #endif } +- (int32_t)bytecodeFileFormatVersion +{ + return _jscWrapper->JSBytecodeFileFormatVersion; +} + - (NSString *)contextName { return [_context.context name]; @@ -688,9 +693,9 @@ static TaggedScript loadTaggedScript(NSData *script, { RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / prepare bundle", nil); - RCTMagicNumber magicNumber = {.allBytes = 0}; - [script getBytes:&magicNumber length:sizeof(magicNumber)]; - RCTScriptTag tag = RCTParseMagicNumber(magicNumber); + RCTBundleHeader header = {}; + [script getBytes:&header length:sizeof(header)]; + RCTScriptTag tag = RCTParseTypeFromHeader(header); NSData *loadedScript = NULL; switch (tag) { diff --git a/React/Executors/RCTJSCWrapper.mm b/React/Executors/RCTJSCWrapper.mm index ef71900505..3b13b8e148 100644 --- a/React/Executors/RCTJSCWrapper.mm +++ b/React/Executors/RCTJSCWrapper.mm @@ -74,9 +74,9 @@ static RCTJSCWrapper *RCTSetUpSystemLibraryPointers() .JSValueIsUndefined = JSValueIsUndefined, .JSValueIsNull = JSValueIsNull, .JSEvaluateScript = JSEvaluateScript, + .JSBytecodeFileFormatVersion = JSNoBytecodeFileFormatVersion, .JSEvaluateBytecodeBundle = (JSEvaluateBytecodeBundleFuncType)UnimplementedJSEvaluateBytecodeBundle, .configureJSCForIOS = (voidWithNoParamsFuncType)noOpSystemJSCFunc, - .JSBytecodeFileFormatVersion = JSNoBytecodeFileFormatVersion, .JSContext = [JSContext class], .JSValue = [JSValue class], };