diff --git a/React/Base/RCTJavaScriptLoader.h b/React/Base/RCTJavaScriptLoader.h index 1768e54ce26..8722d4294ea 100755 --- a/React/Base/RCTJavaScriptLoader.h +++ b/React/Base/RCTJavaScriptLoader.h @@ -11,55 +11,6 @@ #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; - -/** - * RCTBundleHeader - * - * 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 { - // `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; - -/** - * RCTParseTypeFromHeader - * - * Takes the first 8 bytes of a bundle, and returns a tag describing the - * bundle's format. - */ -RCT_EXTERN RCTScriptTag RCTParseTypeFromHeader(RCTBundleHeader header); - -/** - * RCTStringForScriptTag - * - * Convert an `RCTScriptTag` enum into a string, useful for emitting in errors - * and diagnostic messages. - */ -RCT_EXTERN NSString *RCTStringForScriptTag(RCTScriptTag tag); - extern NSString *const RCTJavaScriptLoaderErrorDomain; NS_ENUM(NSInteger) { diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.mm similarity index 92% rename from React/Base/RCTJavaScriptLoader.m rename to React/Base/RCTJavaScriptLoader.mm index f308421f10b..20f3b51fad2 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.mm @@ -16,38 +16,11 @@ #import "RCTPerformanceLogger.h" #import "RCTMultipartDataTask.h" +#include #include -static uint32_t const RCTRAMBundleMagicNumber = 0xFB0BD1E5; -static uint64_t const RCTBCBundleMagicNumber = 0xFF4865726D657300; - NSString *const RCTJavaScriptLoaderErrorDomain = @"RCTJavaScriptLoaderErrorDomain"; -RCTScriptTag RCTParseTypeFromHeader(RCTBundleHeader header) -{ - if (NSSwapLittleIntToHost(header.RAMMagic) == RCTRAMBundleMagicNumber) { - return RCTScriptRAMBundle; - } - - if (NSSwapLittleLongLongToHost(header.BCMagic) == RCTBCBundleMagicNumber) { - return RCTScriptBCBundle; - } - - return RCTScriptString; -} - -NSString *RCTStringForScriptTag(RCTScriptTag tag) -{ - switch (tag) { - case RCTScriptString: - return @"String"; - case RCTScriptRAMBundle: - return @"RAM Bundle"; - case RCTScriptBCBundle: - return @"BC Bundle"; - } -} - @implementation RCTLoadingProgress - (NSString *)description @@ -58,7 +31,7 @@ NSString *RCTStringForScriptTag(RCTScriptTag tag) if (_total > 0) { [desc appendFormat:@" %ld%% (%@/%@)", (long)(100 * [_done integerValue] / [_total integerValue]), _done, _total]; } - [desc appendString:@"…"]; + [desc appendString:@"\u2026"]; return desc; } @@ -140,7 +113,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return nil; } - RCTBundleHeader header = {}; + facebook::react::BundleHeader header{}; size_t readResult = fread(&header, sizeof(header), 1, bundle); fclose(bundle); if (readResult != 1) { @@ -153,12 +126,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return nil; } - RCTScriptTag tag = RCTParseTypeFromHeader(header); + facebook::react::ScriptTag tag = facebook::react::parseTypeFromHeader(header); switch (tag) { - case RCTScriptRAMBundle: + case facebook::react::ScriptTag::RAMBundle: break; - case RCTScriptString: + case facebook::react::ScriptTag::String: if (error) { *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain code:RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously @@ -167,7 +140,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } return nil; - case RCTScriptBCBundle: + case facebook::react::ScriptTag::BCBundle: if (header.BCVersion != runtimeBCVersion) { if (error) { NSString *errDesc = diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 551358ab266..41333d86d96 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -17,6 +17,8 @@ #import +#import + #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTDefines.h" @@ -66,7 +68,7 @@ struct RandomAccessBundleStartupCode { }; struct TaggedScript { - const RCTScriptTag tag; + const facebook::react::ScriptTag tag; const NSData *script; }; @@ -292,7 +294,7 @@ static NSThread *newJavaScriptThread(void) return loadError; } - if (taggedScript.tag == RCTScriptRAMBundle) { + if (taggedScript.tag == facebook::react::ScriptTag::RAMBundle) { registerNativeRequire(_context.context, self); } @@ -712,7 +714,7 @@ RCT_EXPORT_METHOD(setContextName:(nonnull NSString *)contextName) return; } - if (taggedScript.tag == RCTScriptRAMBundle) { + if (taggedScript.tag == facebook::react::ScriptTag::RAMBundle) { registerNativeRequire(self.context.context, self); } @@ -734,13 +736,13 @@ static TaggedScript loadTaggedScript(NSData *script, { RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / prepare bundle", nil); - RCTBundleHeader header = {}; + facebook::react::BundleHeader header{}; [script getBytes:&header length:sizeof(header)]; - RCTScriptTag tag = RCTParseTypeFromHeader(header); + facebook::react::ScriptTag tag = facebook::react::parseTypeFromHeader(header); NSData *loadedScript = NULL; switch (tag) { - case RCTScriptRAMBundle: + case facebook::react::ScriptTag::RAMBundle: [performanceLogger markStartForTag:RCTPLRAMBundleLoad]; loadedScript = loadRAMBundle(sourceURL, error, randomAccessBundle); @@ -749,11 +751,11 @@ static TaggedScript loadTaggedScript(NSData *script, [performanceLogger setValue:loadedScript.length forTag:RCTPLRAMStartupCodeSize]; break; - case RCTScriptBCBundle: + case facebook::react::ScriptTag::BCBundle: loadedScript = script; break; - case RCTScriptString: { + case facebook::react::ScriptTag::String: { NSMutableData *nullTerminatedScript = [NSMutableData dataWithData:script]; [nullTerminatedScript appendBytes:"" length:1]; loadedScript = nullTerminatedScript; @@ -785,16 +787,16 @@ static NSError *executeApplicationScript(TaggedScript taggedScript, JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); switch (taggedScript.tag) { - case RCTScriptRAMBundle: + case facebook::react::ScriptTag::RAMBundle: /* fallthrough */ - case RCTScriptString: { + case facebook::react::ScriptTag::String: { JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)taggedScript.script.bytes); jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError); jscWrapper->JSStringRelease(execJSString); break; } - case RCTScriptBCBundle: { + case facebook::react::ScriptTag::BCBundle: { file_ptr source(fopen(sourceURL.path.UTF8String, "r"), fclose); int sourceFD = fileno(source.get()); diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 8b9f32b0a99..328f18fb828 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -60,7 +60,6 @@ 13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+React.m */; }; 13E41EEB1C05CA0B00CD8DAC /* RCTProfileTrampoline-i386.S in Sources */ = {isa = PBXBuildFile; fileRef = 14BF717F1C04793D00C97D0C /* RCTProfileTrampoline-i386.S */; }; 13F17A851B8493E5007D4C75 /* RCTRedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 13F17A841B8493E5007D4C75 /* RCTRedBox.m */; }; - 14200DAA1AC179B3008EE6BA /* RCTJavaScriptLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */; }; 142014191B32094000CC17BA /* RCTPerformanceLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 142014171B32094000CC17BA /* RCTPerformanceLogger.m */; }; 14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; }; 14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; }; @@ -90,7 +89,6 @@ 2D3B5E9A1D9B089D00451313 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */; }; 2D3B5E9B1D9B08A000451313 /* RCTFrameUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */; }; 2D3B5E9C1D9B08A300451313 /* RCTImageSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BB3D011BECD54500932C10 /* RCTImageSource.m */; }; - 2D3B5E9D1D9B08A800451313 /* RCTJavaScriptLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */; }; 2D3B5E9E1D9B08AD00451313 /* RCTJSStackFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */; }; 2D3B5E9F1D9B08AF00451313 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; }; 2D3B5EA01D9B08B200451313 /* RCTLog.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA4E1A601E3B00E9B192 /* RCTLog.mm */; }; @@ -193,6 +191,8 @@ 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 */; }; + AC70D2E91DE489E4002E6351 /* RCTJavaScriptLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */; }; + AC70D2ED1DE48A22002E6351 /* JSBundleType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */; }; B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */; }; B95154321D1B34B200FE7B80 /* RCTActivityIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */; }; E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; @@ -339,7 +339,6 @@ 13F17A831B8493E5007D4C75 /* RCTRedBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRedBox.h; sourceTree = ""; }; 13F17A841B8493E5007D4C75 /* RCTRedBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRedBox.m; sourceTree = ""; }; 14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptLoader.h; sourceTree = ""; }; - 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptLoader.m; sourceTree = ""; }; 142014171B32094000CC17BA /* RCTPerformanceLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPerformanceLogger.m; sourceTree = ""; }; 142014181B32094000CC17BA /* RCTPerformanceLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPerformanceLogger.h; sourceTree = ""; }; 1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTFrameUpdate.h; sourceTree = ""; }; @@ -438,6 +437,10 @@ 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; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = RCTJSCWrapper.mm; sourceTree = ""; }; + AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJavaScriptLoader.mm; sourceTree = ""; }; + AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSBundleType.cpp; path = cxxreact/JSBundleType.cpp; sourceTree = ""; }; + AC70D2EC1DE48A22002E6351 /* JSBundleType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSBundleType.h; path = cxxreact/JSBundleType.h; sourceTree = ""; }; + AC70D2EE1DE48AC5002E6351 /* oss-compat-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "oss-compat-util.h"; path = "cxxreact/oss-compat-util.h"; sourceTree = ""; }; ACDD3FDA1BC7430D00E7DE33 /* RCTBorderStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderStyle.h; sourceTree = ""; }; B233E6E81D2D843200BC68BA /* RCTI18nManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTI18nManager.h; sourceTree = ""; }; B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTI18nManager.m; sourceTree = ""; }; @@ -665,6 +668,7 @@ isa = PBXGroup; children = ( 133683431D37ACA10077D0C3 /* CSSLayout */, + AC70D2EA1DE489FC002E6351 /* cxxreact */, ); name = ReactCommon; path = ../ReactCommon; @@ -733,7 +737,7 @@ 83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */, 83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */, 14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */, - 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */, + AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */, 008341F51D1DB34400876D9A /* RCTJSStackFrame.h */, 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */, 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */, @@ -773,6 +777,16 @@ path = Base; sourceTree = ""; }; + AC70D2EA1DE489FC002E6351 /* cxxreact */ = { + isa = PBXGroup; + children = ( + AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */, + AC70D2EC1DE48A22002E6351 /* JSBundleType.h */, + AC70D2EE1DE48AC5002E6351 /* oss-compat-util.h */, + ); + name = cxxreact; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -932,7 +946,6 @@ 2D3B5E9B1D9B08A000451313 /* RCTFrameUpdate.m in Sources */, 2D3B5EE41D9B09BB00451313 /* RCTSegmentedControlManager.m in Sources */, 2D3B5EE31D9B09B700451313 /* RCTSegmentedControl.m in Sources */, - 2D3B5E9D1D9B08A800451313 /* RCTJavaScriptLoader.m in Sources */, 2D3B5EB71D9B091800451313 /* RCTRedBox.m in Sources */, 2D3B5ED11D9B097500451313 /* RCTMapAnnotation.m in Sources */, 2D3B5EAB1D9B08EC00451313 /* RCTJSCErrorHandling.m in Sources */, @@ -1042,12 +1055,12 @@ 58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */, 1450FF8A1BCFF28A00208362 /* RCTProfileTrampoline-x86_64.S in Sources */, 13D9FEEB1CDCCECF00158BD7 /* RCTEventEmitter.m in Sources */, + AC70D2E91DE489E4002E6351 /* RCTJavaScriptLoader.mm in Sources */, 14F7A0EC1BDA3B3C003C6C10 /* RCTPerfMonitor.m in Sources */, 1450FF881BCFF28A00208362 /* RCTProfileTrampoline-arm64.S in Sources */, 13E41EEB1C05CA0B00CD8DAC /* RCTProfileTrampoline-i386.S in Sources */, 3D37B5821D522B190042D5B5 /* RCTFont.mm in Sources */, 13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */, - 14200DAA1AC179B3008EE6BA /* RCTJavaScriptLoader.m in Sources */, 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */, 369123E11DDC75850095B341 /* JSCSamplingProfiler.m in Sources */, 13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */, @@ -1089,6 +1102,7 @@ 83A1FE8C1B62640A00BE0E65 /* RCTModalHostView.m in Sources */, 13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */, 1450FF871BCFF28A00208362 /* RCTProfileTrampoline-arm.S in Sources */, + AC70D2ED1DE48A22002E6351 /* JSBundleType.cpp in Sources */, 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */, 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */, 191E3EBE1C29D9AF00C180A6 /* RCTRefreshControlManager.m in Sources */, diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index 6849c641365..c8d20f934fc 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -117,6 +117,8 @@ CXXREACT_PUBLIC_HEADERS = [ 'JSCExecutor.h', 'JSCNativeModules.h', 'JSCWebWorker.h', + 'JSBundleType.h', + 'JSIndexedRAMBundle.h', 'JSModulesUnbundle.h', 'MessageQueueThread.h', 'MethodCall.h', diff --git a/ReactCommon/cxxreact/JSBundleType.cpp b/ReactCommon/cxxreact/JSBundleType.cpp new file mode 100644 index 00000000000..a53e0097743 --- /dev/null +++ b/ReactCommon/cxxreact/JSBundleType.cpp @@ -0,0 +1,37 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "JSBundleType.h" +#include "oss-compat-util.h" + +namespace facebook { +namespace react { + +static uint32_t constexpr RAMBundleMagicNumber = 0xFB0BD1E5; +static uint64_t constexpr BCBundleMagicNumber = 0xFF4865726D657300; + +ScriptTag parseTypeFromHeader(const BundleHeader& header) { + if (littleEndianToHost(header.RAMMagic) == RAMBundleMagicNumber) { + return ScriptTag::RAMBundle; + } + + if (littleEndianToHost(header.BCMagic) == BCBundleMagicNumber) { + return ScriptTag::BCBundle; + } + + return ScriptTag::String; +} + +const char *stringForScriptTag(const ScriptTag& tag) { + switch (tag) { + case ScriptTag::String: + return "String"; + case ScriptTag::RAMBundle: + return "RAM Bundle"; + case ScriptTag::BCBundle: + return "BC Bundle"; + } + return ""; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/JSBundleType.h b/ReactCommon/cxxreact/JSBundleType.h new file mode 100644 index 00000000000..1945fa4c779 --- /dev/null +++ b/ReactCommon/cxxreact/JSBundleType.h @@ -0,0 +1,59 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +/* + * ScriptTag + * + * 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. + */ +enum struct ScriptTag { + String = 0, + RAMBundle, + BCBundle, +}; + +/** + * BundleHeader + * + * 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. + */ +union BundleHeader { + BundleHeader() { + std::memset(this, 0, sizeof(BundleHeader)); + } + + uint32_t RAMMagic; + struct { + uint64_t BCMagic; + uint32_t BCVersion; + }; +}; + +/** + * parseTypeFromHeader + * + * Takes the first 8 bytes of a bundle, and returns a tag describing the + * bundle's format. + */ +ScriptTag parseTypeFromHeader(const BundleHeader& header); + +/** + * stringForScriptTag + * + * Convert an `ScriptTag` enum into a string, useful for emitting in errors + * and diagnostic messages. + */ +const char* stringForScriptTag(const ScriptTag& tag); + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/JSIndexedRAMBundle.cpp b/ReactCommon/cxxreact/JSIndexedRAMBundle.cpp new file mode 100644 index 00000000000..977fcdd2eed --- /dev/null +++ b/ReactCommon/cxxreact/JSIndexedRAMBundle.cpp @@ -0,0 +1,92 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "JSIndexedRAMBundle.h" +#include "oss-compat-util.h" + +namespace facebook { +namespace react { + +JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) : + m_bundle (sourcePath, std::ios_base::in) { + if (!m_bundle) { + throw std::ios_base::failure( + toString("Bundle ", sourcePath, + "cannot be opened: ", m_bundle.rdstate())); + } + + // read in magic header, number of entries, and length of the startup section + uint32_t header[3]; + static_assert( + sizeof(header) == 12, + "header size must exactly match the input file format"); + + readBundle(reinterpret_cast(header), sizeof(header)); + const size_t numTableEntries = littleEndianToHost(header[1]); + const size_t startupCodeSize = littleEndianToHost(header[2]); + + // allocate memory for meta data and lookup table. + m_table = ModuleTable(numTableEntries); + m_baseOffset = sizeof(header) + m_table.byteLength(); + + // read the lookup table from the file + readBundle( + reinterpret_cast(m_table.data.get()), m_table.byteLength()); + + // read the startup code + m_startupCode = + std::make_unique(startupCodeSize); + + readBundle(m_startupCode->data(), startupCodeSize); +} + +JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(uint32_t moduleId) const { + Module ret; + ret.name = toString(moduleId, ".js"); + ret.code = getModuleCode(moduleId); + return ret; +} + +std::unique_ptr JSIndexedRAMBundle::getStartupCode() { + CHECK(m_startupCode) << "startup code for a RAM Bundle can only be retrieved once"; + return std::move(m_startupCode); +} + +std::string JSIndexedRAMBundle::getModuleCode(const uint32_t id) const { + const auto moduleData = id < m_table.numEntries ? &m_table.data[id] : nullptr; + + // entries without associated code have offset = 0 and length = 0 + const uint32_t length = moduleData ? littleEndianToHost(moduleData->length) : 0; + if (length == 0) { + throw std::ios_base::failure( + toString("Error loading module", id, "from RAM Bundle")); + } + + std::string ret(moduleData->length, '\0'); + readBundle(&ret.front(), length, m_baseOffset + littleEndianToHost(moduleData->offset)); + return ret; +} + +void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes) const { + if (!m_bundle.read(buffer, bytes)) { + if (m_bundle.rdstate() & std::ios::eofbit) { + throw std::ios_base::failure("Unexpected end of RAM Bundle file"); + } + throw std::ios_base::failure( + toString("Error reading RAM Bundle: ", m_bundle.rdstate())); + } +} + +void JSIndexedRAMBundle::readBundle( + char *buffer, + const std::streamsize bytes, + const std::ifstream::pos_type position) const { + + if (!m_bundle.seekg(position)) { + throw std::ios_base::failure( + toString("Error reading RAM Bundle: ", m_bundle.rdstate())); + } + readBundle(buffer, bytes); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/JSIndexedRAMBundle.h b/ReactCommon/cxxreact/JSIndexedRAMBundle.h new file mode 100644 index 00000000000..9562012ef23 --- /dev/null +++ b/ReactCommon/cxxreact/JSIndexedRAMBundle.h @@ -0,0 +1,63 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include +#include + +#include "Executor.h" +#include "JSBundleType.h" + +namespace facebook { +namespace react { + +class JSBigString; + +#include + +class JSIndexedRAMBundle : public facebook::react::JSModulesUnbundle { +public: + // Throws std::runtime_error on failure. + JSIndexedRAMBundle(const char *sourceURL); + + // Throws std::runtime_error on failure. + std::unique_ptr getStartupCode(); + // Throws std::runtime_error on failure. + Module getModule(uint32_t moduleId) const override; + +private: + struct ModuleData { + uint32_t offset; + uint32_t length; + }; + static_assert( + sizeof(ModuleData) == 8, + "ModuleData must not have any padding and use sizes matching input files"); + + struct ModuleTable { + size_t numEntries; + std::unique_ptr data; + ModuleTable() : numEntries(0) {}; + ModuleTable(size_t entries) : + numEntries(entries), + data(std::make_unique(numEntries)) {}; + size_t byteLength() const { + return numEntries * sizeof(ModuleData); + } + }; + + std::string getModuleCode(const uint32_t id) const; + void readBundle(char *buffer, const std::streamsize bytes) const; + void readBundle( + char *buffer, const + std::streamsize bytes, + const std::ifstream::pos_type position) const; + + mutable std::ifstream m_bundle; + ModuleTable m_table; + size_t m_baseOffset; + std::unique_ptr m_startupCode; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/oss-compat-util.h b/ReactCommon/cxxreact/oss-compat-util.h new file mode 100644 index 00000000000..5c8f4fbd682 --- /dev/null +++ b/ReactCommon/cxxreact/oss-compat-util.h @@ -0,0 +1,103 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +//TODO #14683310 remove this header as soon as RN iOS OSS links against folly + +#pragma once + +#if defined(ANDROID) + #define USE_FOLLY_FOR_ENDIAN_SWAP 1 + #define USE_FOLLY_FOR_TO_STRING 1 +#elif defined(__has_include) + #define USE_FOLLY_FOR_ENDIAN_SWAP __has_include() + #define USE_FOLLY_FOR_TO_STRING __has_include() +#else + #define USE_FOLLY_FOR_ENDIAN_SWAP 0 + #define USE_FOLLY_FOR_TO_STRING 0 +#endif + +#if USE_FOLLY_FOR_ENDIAN_SWAP +#include +#elif defined(__APPLE__) +#include +#include +#endif // USE_FOLLY_FOR_ENDIAN_SWAP + +#if USE_FOLLY_FOR_TO_STRING +#include +#else +#include +#include +#endif // USE_FOLLY_FOR_TO_STRING + + +#if USE_FOLLY_FOR_ENDIAN_SWAP +namespace facebook { +namespace react { + +template +inline T littleEndianToHost(T x) { + return folly::Endian::little(x); +} + +#elif defined(__APPLE__) + +namespace facebook { +namespace react { + +// Yes, this is horrible. #14683310 + +inline int32_t littleEndianToHost(int32_t x) { + return CFSwapInt32LittleToHost(x); +} + +inline uint32_t littleEndianToHost(uint32_t x) { + return CFSwapInt32LittleToHost(x); +} + +inline int64_t littleEndianToHost(int64_t x) { + return CFSwapInt64LittleToHost(x); +} + +inline uint64_t littleEndianToHost(uint64_t x) { + return CFSwapInt64LittleToHost(x); +} + +#endif // USE_FOLLY_FOR_ENDIAN_SWAP + +#if USE_FOLLY_FOR_TO_STRING + +template +inline std::string toString(Ts... values) { + return folly::to(std::forward(values)...); +} + +#else + +namespace { + +template +std::string toString(const std::stringstream& buf) { + return buf.str(); +} + +template +std::string toString(std::stringstream& buf, T value, Ts... values) { + buf << value; + return toString(buf, std::forward(values)...); +} + +} // anonymous namespace + +template +std::string toString(Ts... values) { + std::stringstream buf{}; + return toString(buf, std::forward(values)...); +} + +#endif // USE_FOLLY_FOR_TO_STRING + +} // namespace react +} // namespace facebook + +#undef USE_FOLLY_FOR_ENDIAN_SWAP +#undef USE_FOLLY_FOR_TO_STRING