From 07f323644d437cde169d69e013f884f33c83859a Mon Sep 17 00:00:00 2001 From: Hilke Heremans Date: Sat, 11 Mar 2017 08:52:57 +0100 Subject: [PATCH 001/366] Fix for IP detection on Macs with multiple interfaces and loopbacks configured --- packager/react-native-xcode.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/react-native-xcode.sh b/packager/react-native-xcode.sh index d136282fdd683d..2edea86bc35bf8 100755 --- a/packager/react-native-xcode.sh +++ b/packager/react-native-xcode.sh @@ -76,7 +76,7 @@ if [[ "$CONFIGURATION" = "Debug" && ! "$PLATFORM_NAME" == *simulator ]]; then PLIST=$TARGET_BUILD_DIR/$INFOPLIST_PATH IP=$(ipconfig getifaddr en0) if [ -z "$IP" ]; then - IP=$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | cut -d\ -f2 | awk 'NR==1{print $1}') + IP=$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | grep -v 127.94.0 | cut -d\ -f2 | awk 'NR==1{print $1}') fi $PLISTBUDDY -c "Add NSAppTransportSecurity:NSExceptionDomains:localhost:NSTemporaryExceptionAllowsInsecureHTTPLoads bool true" "$PLIST" $PLISTBUDDY -c "Add NSAppTransportSecurity:NSExceptionDomains:$IP.xip.io:NSTemporaryExceptionAllowsInsecureHTTPLoads bool true" "$PLIST" From 8ba06cbfb1c60980437e38c358a971ff105af245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cst=C3=BCn=20Ergenoglu?= Date: Sat, 11 Mar 2017 17:22:32 -0800 Subject: [PATCH 002/366] Fix wrong modal size in fullscreen Summary: Display.getCurrentSizeRange() doesn't include the size of the status bar, so Modal windows on Android have a gap in the bottom that is the same size as the status bar. This checks the current theme and adds the size of status bar to the modal window height if necessary. **Test plan (required)** Run a React Native app on Android with a theme that doesn't show status bar and launch a modal dialog. See issue #11872 for an example. Closes https://github.com/facebook/react-native/pull/11928 Differential Revision: D4695847 fbshipit-source-id: 9fafc2b5040e2f58d562c9cc4cd4d6d87b0df2a3 --- .../react/views/modal/ModalHostHelper.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostHelper.java index d95b2ee7aa887e..d63643266db40f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostHelper.java @@ -4,6 +4,8 @@ import android.annotation.TargetApi; import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Point; import android.view.Display; import android.view.WindowManager; @@ -38,12 +40,26 @@ public static Point getModalHostSize(Context context) { // getSize will return the dimensions of the screen in its current orientation display.getSize(SIZE_POINT); + int[] attrs = {android.R.attr.windowFullscreen}; + Resources.Theme theme = context.getTheme(); + TypedArray ta = theme.obtainStyledAttributes(attrs); + boolean windowFullscreen = ta.getBoolean(0, false); + + // We need to add the status bar height to the height if we have a fullscreen window, + // because Display.getCurrentSizeRange doesn't include it. + Resources resources = context.getResources(); + int statusBarId = resources.getIdentifier("status_bar_height", "dimen", "android"); + int statusBarHeight = 0; + if (windowFullscreen && statusBarId > 0) { + statusBarHeight = (int) resources.getDimension(statusBarId); + } + if (SIZE_POINT.x < SIZE_POINT.y) { // If we are vertical the width value comes from min width and height comes from max height - return new Point(MIN_POINT.x, MAX_POINT.y); + return new Point(MIN_POINT.x, MAX_POINT.y + statusBarHeight); } else { // If we are horizontal the width value comes from max width and height comes from min height - return new Point(MAX_POINT.x, MIN_POINT.y); + return new Point(MAX_POINT.x, MIN_POINT.y + statusBarHeight); } } } From 76f5b9606a43dee4cc1962b4f8a3ada4e90b910a Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Mon, 13 Mar 2017 05:17:54 -0700 Subject: [PATCH 003/366] packager: Bundler: refactor the maxWorkerCount Summary: The function giving the worker count was duplicated, let's just pass it down from a central place, the Bundler. Also, I simplified the function to use a simple formula rather than arbitrary ranges (it's still arbitrary, just a tad bit less :D ). Reviewed By: davidaurelio Differential Revision: D4689366 fbshipit-source-id: fe5b349396f1a07858f4f80ccaa63c375122fac8 --- .../src/Bundler/__tests__/Bundler-test.js | 15 +++++++++ packager/src/Bundler/index.js | 27 +++++++++++++++- packager/src/JSTransformer/index.js | 23 ++----------- packager/src/Resolver/index.js | 1 + .../__tests__/DependencyGraph-test.js | 2 +- packager/src/node-haste/index.js | 32 +++---------------- 6 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packager/src/Bundler/__tests__/Bundler-test.js b/packager/src/Bundler/__tests__/Bundler-test.js index f8b1639bb8e4fd..b4becd75dab965 100644 --- a/packager/src/Bundler/__tests__/Bundler-test.js +++ b/packager/src/Bundler/__tests__/Bundler-test.js @@ -15,6 +15,7 @@ jest .setMock('uglify-js') .mock('image-size') .mock('fs') + .mock('os') .mock('assert') .mock('progress') .mock('../../node-haste') @@ -31,6 +32,7 @@ var Resolver = require('../../Resolver'); var defaults = require('../../../defaults'); var sizeOf = require('image-size'); var fs = require('fs'); +const os = require('os'); var commonOptions = { allowBundleUpdates: false, @@ -76,6 +78,8 @@ describe('Bundler', function() { var projectRoots; beforeEach(function() { + os.cpus.mockReturnValue({length: 1}); + getDependencies = jest.fn(); getModuleSystemDependencies = jest.fn(); projectRoots = ['/root']; @@ -348,5 +352,16 @@ describe('Bundler', function() { '/root/img/new_image2@3x.png', ])); }); + + it('return correct number of workers', () => { + os.cpus.mockReturnValue({length: 1}); + expect(Bundler.getMaxWorkerCount()).toBe(1); + os.cpus.mockReturnValue({length: 8}); + expect(Bundler.getMaxWorkerCount()).toBe(6); + os.cpus.mockReturnValue({length: 24}); + expect(Bundler.getMaxWorkerCount()).toBe(14); + process.env.REACT_NATIVE_MAX_WORKERS = 5; + expect(Bundler.getMaxWorkerCount()).toBe(5); + }); }); }); diff --git a/packager/src/Bundler/index.js b/packager/src/Bundler/index.js index b4dcf64719ae4c..7066db33f911f3 100644 --- a/packager/src/Bundler/index.js +++ b/packager/src/Bundler/index.js @@ -25,6 +25,8 @@ const imageSize = require('image-size'); const path = require('path'); const denodeify = require('denodeify'); const defaults = require('../../defaults'); +const os = require('os'); +const invariant = require('fbjs/lib/invariant'); const { sep: pathSeparator, @@ -163,8 +165,10 @@ class Bundler { cacheKey: transformCacheKey, }); + const maxWorkerCount = Bundler.getMaxWorkerCount(); + /* $FlowFixMe: in practice it's always here. */ - this._transformer = new Transformer(opts.transformModulePath); + this._transformer = new Transformer(opts.transformModulePath, maxWorkerCount); const getTransformCacheKey = (src, filename, options) => { return transformCacheKey + getCacheKey(src, filename, options); @@ -178,6 +182,7 @@ class Bundler { getTransformCacheKey, globalTransformCache: opts.globalTransformCache, hasteImpl: opts.hasteImpl, + maxWorkerCount, minifyCode: this._transformer.minify, moduleFormat: opts.moduleFormat, platforms: new Set(opts.platforms), @@ -780,6 +785,26 @@ class Bundler { getResolver(): Promise { return this._resolverPromise; } + + /** + * Unless overriden, we use a diminishing amount of workers per core, because + * using more and more of them does not scale much. Ex. 6 workers for 8 + * cores, or 14 workers for 24 cores. + */ + static getMaxWorkerCount() { + const cores = os.cpus().length; + const envStr = process.env.REACT_NATIVE_MAX_WORKERS; + if (envStr == null) { + return Math.max(1, Math.ceil(cores * (0.5 + 0.5 * Math.exp(-cores * 0.07)) - 1)); + } + const envCount = parseInt(process.env.REACT_NATIVE_MAX_WORKERS, 10); + invariant( + Number.isInteger(envCount), + 'environment variable `REACT_NATIVE_MAX_WORKERS` must be a valid integer', + ); + return Math.min(cores, envCount); + } + } function getPathRelativeToRoot(roots, absPath) { diff --git a/packager/src/JSTransformer/index.js b/packager/src/JSTransformer/index.js index 393eb2452f872c..6cc7e56e17d4fc 100644 --- a/packager/src/JSTransformer/index.js +++ b/packager/src/JSTransformer/index.js @@ -16,7 +16,6 @@ const Logger = require('../Logger'); const debug = require('debug')('RNP:JStransformer'); const denodeify = require('denodeify'); const invariant = require('fbjs/lib/invariant'); -const os = require('os'); const path = require('path'); const util = require('util'); const workerFarm = require('worker-farm'); @@ -35,24 +34,7 @@ const TRANSFORM_TIMEOUT_INTERVAL = 301000; // How may times can we tolerate failures from the worker. const MAX_RETRIES = 2; -const maxConcurrentWorkers = ((cores, override) => { - if (override) { - return Math.min(cores, override); - } - - if (cores < 3) { - return cores; - } - if (cores < 8) { - return Math.floor(cores * 0.75); - } - if (cores < 24) { - return Math.floor(3 / 8 * cores + 3); // between cores *.75 and cores / 2 - } - return cores / 2; -})(os.cpus().length, parseInt(process.env.REACT_NATIVE_MAX_WORKERS, 10)); - -function makeFarm(worker, methods, timeout) { +function makeFarm(worker, methods, timeout, maxConcurrentWorkers) { return workerFarm( { autoStart: true, @@ -83,7 +65,7 @@ class Transformer { sourceMap: SourceMap, ) => Promise<{code: string, map: SourceMap}>; - constructor(transformModulePath: string) { + constructor(transformModulePath: string, maxWorkerCount: number) { invariant(path.isAbsolute(transformModulePath), 'transform module path should be absolute'); this._transformModulePath = transformModulePath; @@ -91,6 +73,7 @@ class Transformer { require.resolve('./worker'), ['minify', 'transformAndExtractDependencies'], TRANSFORM_TIMEOUT_INTERVAL, + maxWorkerCount, ); this._transform = denodeify(this._workers.transformAndExtractDependencies); this.minify = denodeify(this._workers.minify); diff --git a/packager/src/Resolver/index.js b/packager/src/Resolver/index.js index 31d53c9d3f7d56..e73706bf8eea16 100644 --- a/packager/src/Resolver/index.js +++ b/packager/src/Resolver/index.js @@ -37,6 +37,7 @@ type Options = { getTransformCacheKey: GetTransformCacheKey, globalTransformCache: ?GlobalTransformCache, hasteImpl?: HasteImpl, + maxWorkerCount: number, minifyCode: MinifyCode, platforms: Set, polyfillModuleNames?: Array, diff --git a/packager/src/node-haste/__tests__/DependencyGraph-test.js b/packager/src/node-haste/__tests__/DependencyGraph-test.js index 3c5aca6fbca76b..4a60d0b55c450c 100644 --- a/packager/src/node-haste/__tests__/DependencyGraph-test.js +++ b/packager/src/node-haste/__tests__/DependencyGraph-test.js @@ -112,7 +112,7 @@ describe('DependencyGraph', function() { platforms: new Set(['ios', 'android']), useWatchman: false, ignoreFilePath: () => false, - maxWorkers: 1, + maxWorkerCount: 1, moduleOptions: {cacheTransformResults: true}, resetCache: true, transformCode: (module, sourceCode, transformOptions) => { diff --git a/packager/src/node-haste/index.js b/packager/src/node-haste/index.js index 25af7db61b2231..6af6a1901f2e85 100644 --- a/packager/src/node-haste/index.js +++ b/packager/src/node-haste/index.js @@ -24,8 +24,8 @@ const fs = require('fs'); const getAssetDataFromName = require('./lib/getAssetDataFromName'); const getInverseDependencies = require('./lib/getInverseDependencies'); const getPlatformExtension = require('./lib/getPlatformExtension'); +const invariant = require('fbjs/lib/invariant'); const isAbsolutePath = require('absolute-path'); -const os = require('os'); const path = require('path'); const replacePatterns = require('./lib/replacePatterns'); const util = require('util'); @@ -58,7 +58,7 @@ type Options = { getTransformCacheKey: GetTransformCacheKey, globalTransformCache: ?GlobalTransformCache, ignoreFilePath: (filePath: string) => boolean, - maxWorkers: ?number, + maxWorkerCount: number, moduleOptions: ModuleOptions, platforms: Set, preferNativePlatform: boolean, @@ -87,6 +87,7 @@ class DependencyGraph extends EventEmitter { initialModuleMap: ModuleMap, }) { super(); + invariant(config.opts.maxWorkerCount >= 1, 'worker count must be greater or equal to 1'); this._opts = config.opts; this._haste = config.haste; this._hasteFS = config.initialHasteFS; @@ -97,12 +98,11 @@ class DependencyGraph extends EventEmitter { } static _createHaste(opts: Options): JestHasteMap { - const mw = opts.maxWorkers; return new JestHasteMap({ extensions: opts.extensions.concat(opts.assetExts), forceNodeFilesystemAPI: opts.forceNodeFilesystemAPI, ignorePattern: {test: opts.ignoreFilePath}, - maxWorkers: typeof mw === 'number' && mw >= 1 ? mw : getMaxWorkers(), + maxWorkers: opts.maxWorkerCount, mocksPattern: '', name: 'react-native-packager', platforms: Array.from(opts.platforms), @@ -306,28 +306,4 @@ function NotFoundError() { } util.inherits(NotFoundError, Error); -function getMaxWorkers() { - const cores = os.cpus().length; - - if (cores <= 1) { - // oh well... - return 1; - } - if (cores <= 4) { - // don't starve the CPU while still reading reasonably rapidly - return cores - 1; - } - if (cores <= 8) { - // empirical testing showed massive diminishing returns when going over - // 4 or 5 workers on 8-core machines - return Math.floor(cores * 0.75) - 1; - } - - // pretty much guesswork - if (cores < 24) { - return Math.floor(3 / 8 * cores + 3); - } - return cores / 2; -} - module.exports = DependencyGraph; From 41f3d0cf2d0b20315969b68895e9e217b114544c Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 13 Mar 2017 05:58:09 -0700 Subject: [PATCH 004/366] Expose getter for 'flex' property Summary: Helps mitigate part of https://github.com/facebook/react-native/pull/12245 while we wait for a more comprehensive solution. Reviewed By: emilsjolander Differential Revision: D4571776 fbshipit-source-id: 185cd1b0d3af37724136a37471df412c2000dfe4 --- React/Views/RCTShadowView.h | 3 +-- React/Views/RCTShadowView.m | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index bc15bfe595e0c6..61154b1d6e4aab 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -135,14 +135,13 @@ typedef void (^RCTApplierBlock)(NSDictionary *viewRegistry @property (nonatomic, assign) YGWrap flexWrap; @property (nonatomic, assign) YGDisplay display; +@property (nonatomic, assign) float flex; @property (nonatomic, assign) float flexGrow; @property (nonatomic, assign) float flexShrink; @property (nonatomic, assign) YGValue flexBasis; @property (nonatomic, assign) float aspectRatio; -- (void)setFlex:(float)flex; - /** * z-index, used to override sibling order in the view */ diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index c50f38281bbd40..8de41aaca7ff7f 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -669,11 +669,6 @@ - (void)setIntrinsicContentSize:(CGSize)intrinsicContentSize // Flex -- (void)setFlex:(float)value -{ - YGNodeStyleSetFlex(_yogaNode, value); -} - - (void)setFlexBasis:(YGValue)value { RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSetFlexBasis, _yogaNode); @@ -694,6 +689,7 @@ - (type)getProp \ return YGNodeStyleGet##cssProp(_yogaNode); \ } +RCT_STYLE_PROPERTY(Flex, flex, Flex, float) RCT_STYLE_PROPERTY(FlexGrow, flexGrow, FlexGrow, float) RCT_STYLE_PROPERTY(FlexShrink, flexShrink, FlexShrink, float) RCT_STYLE_PROPERTY(FlexDirection, flexDirection, FlexDirection, YGFlexDirection) From ac452c0a173594e40cab1e61e77823be59dd44c6 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Mon, 13 Mar 2017 06:14:34 -0700 Subject: [PATCH 005/366] packager: Server: make buildBundle() async Summary: Also remove the unnecessary await of the resolver, because `_bundler.bundle()` already does that. Reviewed By: davidaurelio Differential Revision: D4689448 fbshipit-source-id: 3b4fd73b1368f8b00c6eb7483e751387d9856ce9 --- packager/src/Server/index.js | 51 +++++++++++++++--------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/packager/src/Server/index.js b/packager/src/Server/index.js index 66c84869a178a0..972723c2883ded 100644 --- a/packager/src/Server/index.js +++ b/packager/src/Server/index.js @@ -302,37 +302,28 @@ class Server { } } - buildBundle(options: { - entryFile: string, - platform?: string, - }): Promise { - return this._bundler.getResolver().then(() => { - if (!options.platform) { - options.platform = getPlatformExtension(options.entryFile); - } - - const opts = bundleOpts(options); - return this._bundler.bundle(opts); - }).then(bundle => { - const modules = bundle.getModules(); - const nonVirtual = modules.filter(m => !m.virtual); - bundleDeps.set(bundle, { - files: new Map( - nonVirtual - .map(({sourcePath, meta}) => - [sourcePath, meta != null ? meta.dependencies : []]) - ), - idToIndex: new Map(modules.map(({id}, i) => [id, i])), - dependencyPairs: new Map( - nonVirtual - .filter(({meta}) => meta && meta.dependencyPairs) - /* $FlowFixMe: the filter above ensures `dependencyPairs` is not null. */ - .map(m => [m.sourcePath, m.meta.dependencyPairs]) - ), - outdated: new Set(), - }); - return bundle; + async buildBundle(options: {entryFile: string, platform?: string}): Promise { + if (!options.platform) { + options.platform = getPlatformExtension(options.entryFile); + } + const opts = bundleOpts(options); + const bundle = await this._bundler.bundle(opts); + const modules = bundle.getModules(); + const nonVirtual = modules.filter(m => !m.virtual); + bundleDeps.set(bundle, { + files: new Map(nonVirtual.map(({sourcePath, meta}) => + [sourcePath, meta != null ? meta.dependencies : []], + )), + idToIndex: new Map(modules.map(({id}, i) => [id, i])), + dependencyPairs: new Map( + nonVirtual + .filter(({meta}) => meta && meta.dependencyPairs) + /* $FlowFixMe: the filter above ensures `dependencyPairs` is not null. */ + .map(m => [m.sourcePath, m.meta.dependencyPairs]) + ), + outdated: new Set(), }); + return bundle; } buildBundleFromUrl(reqUrl: string): Promise { From 3ce31c24dad8a52ce17772c8e65687a14d320c09 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 13 Mar 2017 09:41:30 -0700 Subject: [PATCH 006/366] Rename *Component props to match SectionList Reviewed By: yungsters Differential Revision: D4697335 fbshipit-source-id: 742b7a1729ba7a08fe3d9707bcf6c51026779739 --- Examples/UIExplorer/js/FlatListExample.js | 6 +++--- Examples/UIExplorer/js/MultiColumnExample.js | 6 +++--- Libraries/CustomComponents/Lists/FlatList.js | 21 +++++++------------ .../CustomComponents/Lists/SectionList.js | 10 +-------- .../CustomComponents/Lists/VirtualizedList.js | 20 ++++++++---------- .../Lists/VirtualizedSectionList.js | 2 +- 6 files changed, 24 insertions(+), 41 deletions(-) diff --git a/Examples/UIExplorer/js/FlatListExample.js b/Examples/UIExplorer/js/FlatListExample.js index 90263d70aaed44..6ff92e6dcdf24e 100644 --- a/Examples/UIExplorer/js/FlatListExample.js +++ b/Examples/UIExplorer/js/FlatListExample.js @@ -133,9 +133,9 @@ class FlatListExample extends React.PureComponent { = { }; type OptionalProps = { /** - * Rendered at the bottom of all the items. + * Rendered in between each item, but not at the top or bottom. */ - FooterComponent?: ?ReactClass, + ItemSeparatorComponent?: ?ReactClass, /** - * Rendered at the top of all the items. + * Rendered at the bottom of all the items. */ - HeaderComponent?: ?ReactClass, + ListFooterComponent?: ?ReactClass, /** - * Rendered in between each item, but not at the top or bottom. + * Rendered at the top of all the items. */ - SeparatorComponent?: ?ReactClass, + ListHeaderComponent?: ?ReactClass, /** * `getItemLayout` is an optional optimizations that let us skip measurement of dynamic content if * you know the height of items a priori. `getItemLayout` is the most efficient, and is easy to @@ -88,7 +88,7 @@ type OptionalProps = { * )} * * Remember to include separator length (height or width) in your offset calculation if you - * specify `SeparatorComponent`. + * specify `ItemSeparatorComponent`. */ getItemLayout?: (data: ?Array, index: number) => {length: number, offset: number, index: number}, @@ -132,13 +132,6 @@ type OptionalProps = { * Optional custom style for multi-item rows generated when numColumns > 1 */ columnWrapperStyle?: StyleObj, - /** - * Optional optimization to minimize re-rendering items. - */ - shouldItemUpdate: ( - prevInfo: {item: ItemT, index: number}, - nextInfo: {item: ItemT, index: number} - ) => boolean, /** * See `ViewabilityHelper` for flow type and further documentation. */ diff --git a/Libraries/CustomComponents/Lists/SectionList.js b/Libraries/CustomComponents/Lists/SectionList.js index 2b6daefe77eb6a..2bc66a65affe54 100644 --- a/Libraries/CustomComponents/Lists/SectionList.js +++ b/Libraries/CustomComponents/Lists/SectionList.js @@ -183,16 +183,8 @@ class SectionList> static defaultProps: DefaultProps = VirtualizedSectionList.defaultProps; render() { - const {ListFooterComponent, ListHeaderComponent, ItemSeparatorComponent} = this.props; const List = this.props.legacyImplementation ? MetroListView : VirtualizedSectionList; - return ( - - ); + return ; } } diff --git a/Libraries/CustomComponents/Lists/VirtualizedList.js b/Libraries/CustomComponents/Lists/VirtualizedList.js index 206a1f2d8cd3f3..9040fcf1b81763 100644 --- a/Libraries/CustomComponents/Lists/VirtualizedList.js +++ b/Libraries/CustomComponents/Lists/VirtualizedList.js @@ -59,9 +59,6 @@ type RequiredProps = { data?: any, }; type OptionalProps = { - FooterComponent?: ?ReactClass, - HeaderComponent?: ?ReactClass, - SeparatorComponent?: ?ReactClass, /** * `debug` will turn on extra logging and visual overlays to aid with debugging both usage and * implementation, but with a significant perf hit. @@ -305,6 +302,7 @@ class VirtualizedList extends React.PureComponent { 'Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent ' + 'to support native onScroll events with useNativeDriver', ); + this._updateCellsToRenderBatcher = new Batchinator( this._updateCellsToRender, this.props.updateCellsBatchingPeriod, @@ -334,7 +332,7 @@ class VirtualizedList extends React.PureComponent { } _pushCells(cells, first, last) { - const {SeparatorComponent, data, getItem, getItemCount, keyExtractor} = this.props; + const {ItemSeparatorComponent, data, getItem, getItemCount, keyExtractor} = this.props; const end = getItemCount(data) - 1; last = Math.min(end, last); for (let ii = first; ii <= last; ii++) { @@ -352,19 +350,19 @@ class VirtualizedList extends React.PureComponent { parentProps={this.props} /> ); - if (SeparatorComponent && ii < end) { - cells.push(); + if (ItemSeparatorComponent && ii < end) { + cells.push(); } } } render() { - const {FooterComponent, HeaderComponent} = this.props; + const {ListFooterComponent, ListHeaderComponent} = this.props; const {data, disableVirtualization, horizontal} = this.props; const cells = []; - if (HeaderComponent) { + if (ListHeaderComponent) { cells.push( - + ); } @@ -404,10 +402,10 @@ class VirtualizedList extends React.PureComponent { ); } } - if (FooterComponent) { + if (ListFooterComponent) { cells.push( - + ); } diff --git a/Libraries/CustomComponents/Lists/VirtualizedSectionList.js b/Libraries/CustomComponents/Lists/VirtualizedSectionList.js index 84ec2c2c37822e..317891c4d3ebc2 100644 --- a/Libraries/CustomComponents/Lists/VirtualizedSectionList.js +++ b/Libraries/CustomComponents/Lists/VirtualizedSectionList.js @@ -84,7 +84,7 @@ type OptionalProps = { renderSectionHeader?: ?({section: SectionT}) => ?React.Element<*>, /** * Rendered at the bottom of every Section, except the very last one, in place of the normal - * SeparatorComponent. + * ItemSeparatorComponent. */ SectionSeparatorComponent?: ?ReactClass<*>, /** From 67a8d8951d3ffdc49f7a31403b3f08626565707c Mon Sep 17 00:00:00 2001 From: Hector Ramos Date: Mon, 13 Mar 2017 10:50:23 -0700 Subject: [PATCH 007/366] React Conf lanches Reviewed By: ericvicenti, gfosco Differential Revision: D4679678 fbshipit-source-id: c1088390a44540e8934666e0ffdd43a3e170de32 --- blog/2017-03-13-better-list-views.md | 120 ++++++++++++++++++ ...2017-03-13-idx-the-existential-function.md | 48 +++++++ ...-13-introducing-create-react-native-app.md | 34 +++++ 3 files changed, 202 insertions(+) create mode 100644 blog/2017-03-13-better-list-views.md create mode 100644 blog/2017-03-13-idx-the-existential-function.md create mode 100644 blog/2017-03-13-introducing-create-react-native-app.md diff --git a/blog/2017-03-13-better-list-views.md b/blog/2017-03-13-better-list-views.md new file mode 100644 index 00000000000000..c18e6f29b1cbd7 --- /dev/null +++ b/blog/2017-03-13-better-list-views.md @@ -0,0 +1,120 @@ +--- +title: Better List Views in React Native +author: Spencer Ahrens +authorTitle: Software Engineer at Facebook +authorURL: https://github.com/sahrens +authorImage: https://avatars1.githubusercontent.com/u/1509831 +authorTwitter: sahrens2012 +category: engineering +--- + +Many of you have started playing with some of our new List components already after our [teaser announcement in the community group](https://www.facebook.com/groups/react.native.community/permalink/921378591331053), but we are officially announcing them today! No more `ListView`s or `DataSource`s, stale rows, ignored bugs, or excessive memory consumption - with the latest React Native March 2017 release candidate (`0.43-rc.1`) you can pick from the new suite of components what best fits your use-case, with great perf and feature sets out of the box: + +### [``](https://facebook.github.io/react-native/releases/next/docs/flatlist.html) ### + +This is the workhorse component for simple, performant lists. Provide an array of data and a `renderItem` function and you're good to go: + +``` + +``` + +### [``](https://facebook.github.io/react-native/releases/next/docs/sectionlist.html) ### + +If you want to render a set of data broken into logical sections, maybe with section headers (e.g. in an alphabetical address book), and potentially with heterogeneous data and rendering (such as a profile view with some buttons followed by a composer, then a photo grid, then a friend grid, and finally a list of stories), this is the way to go. + +``` +

} + sections={[ // homogenous rendering between sections + {data: [...], key: ...}, + {data: [...], key: ...}, + {data: [...], key: ...}, + ]} +/> + + +``` + +### [``](https://facebook.github.io/react-native/releases/next/docs/virtualizedlist.html) ## + +The implementation behind the scenes with a more flexible API. Especially handy if your data is not in a plain array (e.g. an immutable list). + +## Features ## + +Lists are used in many contexts, so we packed the new components full of features to handle the majority of use cases out of the box: + +* Scroll loading (`onEndReached`). +* Pull to refresh (`onRefresh` / `refreshing`). +* [Configurable](https://github.com/facebook/react-native/blob/master/Libraries/CustomComponents/Lists/ViewabilityHelper.js) viewability (VPV) callbacks (`onViewableItemsChanged` / `viewabilityConfig`). +* Horizontal mode (`horizontal`). +* Intelligent item and section separators. +* Multi-column support (`numColumns`) +* `scrollToEnd`, `scrollToIndex`, and `scrollToItem` +* Better Flow typing. + +### Some Caveats ### + +- The internal state of item subtrees is not preserved when content scrolls out of the render window. Make sure all your data is captured in the item data or external stores like Flux, Redux, or Relay. + +- These components are based on `PureComponent` which means that they will not re-render if `props` remains shallow-equal. Make sure that everything your `renderItem` function depends on directly is passed as a prop that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. For example: + + ```javascript + this.setState((oldState) => ({ + selected: { // New instance breaks `===` + ...oldState.selected, // copy old data + [item.key]: !oldState.selected[item.key], // toggle + }})) + } + selected={ + !!this.state.selected[item.key] // renderItem depends on state + } + />} + selected={ // Can be any prop that doesn't collide with existing props + this.state.selected // A change to selected should re-render FlatList + } + /> + ``` + +- In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application, and we are working on improving it behind the scenes. + +- By default, these new lists look for a `key` prop on each item and use that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. + +## Performance ## + +Besides simplifying the API, the new list components also have significant performance enhancements, the main one being nearly constant memory usage for any number of rows. This is done by 'virtualizing' elements that are outside of the render window by completely unmounting them from the component hierarchy and reclaiming the JS memory from the react components, along with the native memory from the shadow tree and the UI views. This has a catch which is that internal component state will not be preserved, so **make sure you track any important state outside of the components themselves, e.g. in Relay or Redux or Flux store.** + +Limiting the render window also reduces the amount of work that needs to be done by React and the native platform, e.g from view traversals. Even if you are rendering the last of a million elements, with these new lists there is no need to iterate through all those elements in order to render. You can even jump to the middle with `scrollToIndex` without excessive rendering. + +We've also made some improvements with scheduling which should help with application responsiveness. Items at the edge of the render window are rendered infrequently and at a lower priority after any active gestures or animations or other interactions have completed. + +## Advanced Usage ## + +Unlike `ListView`, all items in the render window are re-rendered any time any props change. Often this is fine because the windowing reduces the number of items to a constant number, but if your items are on the complex side, you should make sure to follow React best practices for performance and use `React.PureComponent` and/or `shouldComponentUpdate` as appropriate within your components to limit re-renders of the recursive subtree. + +If you can calculate the height of your rows without rendering them, you can improve the user experience by providing the `getItemLayout` prop. This makes it much smoother to scroll to specific items with e.g. `scrollToIndex`, and will improve the scroll indicator UI because the height of the content can be determined without rendering it. + +If you have an alternative data type, like an immutable list, `` is the way to go. It takes a `getItem` prop that lets you return the item data for any given index and has looser flow typing. + +There are also a bunch of parameters you can tweak if you have an unusual use case. For example, you can use `windowSize` to trade off memory usage vs. user experience, `maxToRenderPerBatch` to adjust fill rate vs. responsiveness, `onEndReachedThreshold` to control when scroll loading happens, and more. + +## Future Work ## + +* Migration of existing surfaces (ultimately deprecation of `ListView`). +* More features as we see/hear the need (let us know!). +* Sticky section header support. +* More performance optimizations. +* Support functional item components with state. diff --git a/blog/2017-03-13-idx-the-existential-function.md b/blog/2017-03-13-idx-the-existential-function.md new file mode 100644 index 00000000000000..d10ba1cde0683b --- /dev/null +++ b/blog/2017-03-13-idx-the-existential-function.md @@ -0,0 +1,48 @@ +--- +title: idx: The Existential Function +author: Timothy Yung +authorTitle: Engineering Manager at Facebook +authorURL: https://github.com/yungsters +authorImage: https://pbs.twimg.com/profile_images/1592444107/image.jpg +authorTwitter: yungsters +category: engineering +--- + +At Facebook, we often need to access deeply nested values in data structures fetched with GraphQL. On the way to accessing these deeply nested values, it is common for one or more intermediate fields to be nullable. These intermediate fields may be null for a variety of reasons, from failed privacy checks to the mere fact that null happens to be the most flexible way to represent non-fatal errors. + +Unfortunately, accessing these deeply nested values is currently tedious and verbose. + +```javascript +props.user && +props.user.friends && +props.user.friends[0] && +props.user.friends[0].friends +``` + +There is [an ECMAScript proposal to introduce the existential operator](https://github.com/claudepache/es-optional-chaining) which will make this much more convenient. But until a time when that proposal is finalized, we want a solution that improves our quality of life, maintains existing language semantics, and encourages type safety with Flow. + +We came up with an existential _function_ we call `idx`. + +```javascript +idx(props, _ => _.user.friends[0].friends) +``` + +The invocation in this code snippet behaves similarly to the boolean expression in the code snippet above, except with significantly less repetition. The `idx` function takes exactly two arguments: + +- Any value, typically an object or array into which you want to access a nested value. +- A function that receives the first argument and accesses a nested value on it. + +In theory, the `idx` function will try-catch errors that are the result of accessing properties on null or undefined. If such an error is caught, it will return either null or undefined. (And you can see how this might be implemented in [idx.js](https://github.com/facebookincubator/idx/blob/master/packages/idx/src/idx.js).) + +In practice, try-catching every nested property access is slow, and differentiating between specific kinds of TypeErrors is fragile. To deal with these shortcomings, we created a Babel plugin that transforms the above `idx` invocation into the following expression: + +```javascript +props.user == null ? props.user : +props.user.friends == null ? props.user.friends : +props.user.friends[0] == null ? props.user.friends[0] : +props.user.friends[0].friends +``` + +Finally, we added a custom Flow type declaration for `idx` that allows the traversal in the second argument to be properly type-checked while permitting nested access on nullable properties. + +The function, Babel plugin, and Flow declaration are now [available on GitHub](https://github.com/facebookincubator/idx). They are used by installing the **idx** and **babel-plugin-idx** npm packages, and adding “idx” to the list of plugins in your `.babelrc` file. diff --git a/blog/2017-03-13-introducing-create-react-native-app.md b/blog/2017-03-13-introducing-create-react-native-app.md new file mode 100644 index 00000000000000..4ff6714e8d05a6 --- /dev/null +++ b/blog/2017-03-13-introducing-create-react-native-app.md @@ -0,0 +1,34 @@ +--- +title: Introducing Create React Native App +author: Adam Perry +authorTitle: Software Engineer at Expo +authorURL: https://github.com/dikaiosune +authorImage: https://avatars2.githubusercontent.com/u/6812281 +authorTwitter: dika10sune +category: engineering +--- + +Today we’re announcing [Create React Native App](https://github.com/react-community/create-react-native-app): a new tool that makes it significantly easier to get started with a React Native project! It’s heavily inspired by the design of [Create React App](https://github.com/facebookincubator/create-react-app) and is the product of a collaboration between [Facebook](https://code.facebook.com) and [Expo](https://expo.io) (formerly Exponent). + +Many developers struggle with installing and configuring React Native’s current native build dependencies, especially for Android. With Create React Native App, there’s no need to use Xcode or Android Studio, and you can develop for your iOS device using Linux or Windows. This is accomplished using the Expo app, which loads and runs CRNA projects written in pure JavaScript without compiling any native code. + +Try creating a new project (replace with suitable yarn commands if you have it installed): + +``` +$ npm i -g create-react-native-app +$ create-react-native-app my-project +$ cd my-project +$ npm start +``` + +This will start the React Native packager and print a QR code. Open it in the [Expo app](https://expo.io) to load your JavaScript. Calls to `console.log` are forwarded to your terminal. You can make use of any standard React Native APIs as well as the [Expo SDK](https://docs.expo.io/versions/latest/sdk/index.html). + +## What about native code? + +Many React Native projects have Java or Objective-C/Swift dependencies that need to be compiled. The Expo app does include APIs for camera, video, contacts, and more, and bundles popular libraries like [Airbnb’s react-native-maps](https://docs.expo.io/versions/v14.0.0/sdk/map-view.html), or [Facebook authentication](https://docs.expo.io/versions/latest/sdk/facebook.html). However if you need a native code dependency that Expo doesn’t bundle then you’ll probably need to have your own build configuration for it. Just like Create React App, “ejecting” is supported by CRNA. + +You can run `npm run eject` to get a project very similar to what `react-native init` would generate. At that point you’ll need Xcode and/or Android Studio just as you would if you started with `react-native init` , adding libraries with `react-native link` will work, and you’ll have full control over the native code compilation process. + +## Questions? Feedback? + +Create React Native App is now stable enough for general use, which means we’re very eager to hear about your experience using it! You can find me [on Twitter](https://twitter.com/dika10sune) or open an issue on [the GitHub repository](https://github.com/react-community/create-react-native-app). Pull requests are very welcome! From 2976aa12bcb0ca26c800261f0e5c7e26c97425f0 Mon Sep 17 00:00:00 2001 From: amilcar-andrade Date: Mon, 13 Mar 2017 12:25:49 -0700 Subject: [PATCH 008/366] Fixes spelling mistake inside TouchableNativeFeedback.android.js Summary: Thanks for submitting a pull request! Please provide enough information so that others can review your pull request: > **Unless you are a React Native release maintainer and cherry-picking an *existing* commit into a current release, ensure your pull request is targeting the `master` React Native branch.** Explain the **motivation** for making this change. What existing problem does the pull request solve? Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. **Test plan (required)** Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. Make sure tests pass on both Travis and Circle CI. **Code formatting** Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide). For more info, see Closes https://github.com/facebook/react-native/pull/12901 Differential Revision: D4699296 Pulled By: hramos fbshipit-source-id: 514ae27c47c8ae22e1aadb99a787daa6fdc3b6a4 --- .../Components/Touchable/TouchableNativeFeedback.android.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 037a3b85ca8cfd..5707b61e378aaf 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -255,7 +255,7 @@ var TouchableNativeFeedback = React.createClass({ // We need to clone the actual element so that the ripple background drawable // can be applied directly to the background of this element rather than to - // a wrapper view as done in outher Touchable* + // a wrapper view as done in other Touchable* return React.cloneElement( child, childProps From 98798a06aebe87fdde7643143a9e5acd7f8c99ae Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 13 Mar 2017 13:19:55 -0700 Subject: [PATCH 009/366] More accurate implementation of `RCTRootView`'s `sizeThatFits:` Reviewed By: mmmulani Differential Revision: D4672788 fbshipit-source-id: 780487f2264916349e32785808a93ed6f957e471 --- React/Base/RCTRootView.m | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 839640a8fbe482..db1ed02343c415 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -153,10 +153,22 @@ - (void)setPassThroughTouches:(BOOL)passThroughTouches - (CGSize)sizeThatFits:(CGSize)size { - return CGSizeMake( - _sizeFlexibility & RCTRootViewSizeFlexibilityWidth ? MIN(_intrinsicContentSize.width, size.width) : size.width, - _sizeFlexibility & RCTRootViewSizeFlexibilityHeight ? MIN(_intrinsicContentSize.height, size.height) : size.height - ); + CGSize fitSize = _intrinsicContentSize; + CGSize currentSize = self.bounds.size; + + // Following the current `size` and current `sizeFlexibility` policy. + fitSize = CGSizeMake( + _sizeFlexibility & RCTRootViewSizeFlexibilityWidth ? fitSize.width : currentSize.width, + _sizeFlexibility & RCTRootViewSizeFlexibilityHeight ? fitSize.height : currentSize.height + ); + + // Following the given size constraints. + fitSize = CGSizeMake( + MIN(size.width, fitSize.width), + MIN(size.height, fitSize.height) + ); + + return fitSize; } - (void)layoutSubviews From 103fa3d818cab67a73de55a64bb19381db397098 Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Mon, 13 Mar 2017 13:24:48 -0700 Subject: [PATCH 010/366] Skip flaky ScrollView tests Summary: Closes https://github.com/facebook/react-native/pull/12868 Differential Revision: D4699900 Pulled By: hramos fbshipit-source-id: b531f2359734177df1868b453dd1d882e693caa3 --- scripts/run-android-ci-instrumentation-tests.js | 8 ++++++++ scripts/run-android-local-integration-tests.sh | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/run-android-ci-instrumentation-tests.js b/scripts/run-android-ci-instrumentation-tests.js index 329d46070fa12b..4d60bf7591fbfc 100644 --- a/scripts/run-android-ci-instrumentation-tests.js +++ b/scripts/run-android-ci-instrumentation-tests.js @@ -28,11 +28,19 @@ const numberOfRetries = argv.retries || 1; const tryExecNTimes = require('./try-n-times'); const path = require('path'); +// Flaky tests ignored on Circle CI. They still run internally at fb. +const ignoredTests = [ + 'ReactScrollViewTestCase', + 'ReactHorizontalScrollViewTestCase' +]; + // ReactAndroid/src/androidTest/java/com/facebook/react/tests/ReactHorizontalScrollViewTestCase.java const testClasses = ls(`${argv.path}/*.java`) .map(javaFile => { // ReactHorizontalScrollViewTestCase return path.basename(javaFile, '.java'); +}).filter(className => { + return ignoredTests.indexOf(className) === -1; }).map(className => { // com.facebook.react.tests.ReactHorizontalScrollViewTestCase return argv.package + '.' + className; diff --git a/scripts/run-android-local-integration-tests.sh b/scripts/run-android-local-integration-tests.sh index 4d454a1c732803..a85d02be37cfa2 100755 --- a/scripts/run-android-local-integration-tests.sh +++ b/scripts/run-android-local-integration-tests.sh @@ -20,4 +20,5 @@ buck fetch ReactAndroid/src/androidTest/buck-runner:instrumentation-tests buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests echo "Running integration tests..." -adb shell am instrument -w com.facebook.react.tests/android.support.test.runner.AndroidJUnitRunner +# Use the JS script that runs all tests in a loop and is easy to tweak +node ./scripts/run-android-ci-instrumentation-tests.js --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests From fa30f414032a74a2009c7a162e1ab9b49f90fe34 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 13 Mar 2017 13:31:35 -0700 Subject: [PATCH 011/366] Better TextInput: RCTTextView and RCTTextField was marked as Yoga leaf nodes Reviewed By: mmmulani Differential Revision: D4639060 fbshipit-source-id: d4580303a61a7f86cf8bb6ec016fd9834340ffe0 --- Libraries/Text/RCTShadowTextField.h | 14 ++++++++++ Libraries/Text/RCTShadowTextField.m | 19 ++++++++++++++ Libraries/Text/RCTShadowTextView.h | 14 ++++++++++ Libraries/Text/RCTShadowTextView.m | 19 ++++++++++++++ .../Text/RCTText.xcodeproj/project.pbxproj | 26 +++++++++++++++---- Libraries/Text/RCTTextFieldManager.m | 6 +++++ Libraries/Text/RCTTextViewManager.m | 6 +++++ 7 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 Libraries/Text/RCTShadowTextField.h create mode 100644 Libraries/Text/RCTShadowTextField.m create mode 100644 Libraries/Text/RCTShadowTextView.h create mode 100644 Libraries/Text/RCTShadowTextView.m diff --git a/Libraries/Text/RCTShadowTextField.h b/Libraries/Text/RCTShadowTextField.h new file mode 100644 index 00000000000000..543f7fb349a0ef --- /dev/null +++ b/Libraries/Text/RCTShadowTextField.h @@ -0,0 +1,14 @@ +/** + * 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. + */ + +#import + +@interface RCTShadowTextField : RCTShadowView + +@end diff --git a/Libraries/Text/RCTShadowTextField.m b/Libraries/Text/RCTShadowTextField.m new file mode 100644 index 00000000000000..3bc5929dc49c37 --- /dev/null +++ b/Libraries/Text/RCTShadowTextField.m @@ -0,0 +1,19 @@ +/** + * 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. + */ + +#import "RCTShadowTextField.h" + +@implementation RCTShadowTextField + +- (BOOL)isYogaLeafNode +{ + return YES; +} + +@end diff --git a/Libraries/Text/RCTShadowTextView.h b/Libraries/Text/RCTShadowTextView.h new file mode 100644 index 00000000000000..e2c6f1e7ed45db --- /dev/null +++ b/Libraries/Text/RCTShadowTextView.h @@ -0,0 +1,14 @@ +/** + * 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. + */ + +#import + +@interface RCTShadowTextView : RCTShadowView + +@end diff --git a/Libraries/Text/RCTShadowTextView.m b/Libraries/Text/RCTShadowTextView.m new file mode 100644 index 00000000000000..d3e3602edc4073 --- /dev/null +++ b/Libraries/Text/RCTShadowTextView.m @@ -0,0 +1,19 @@ +/** + * 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. + */ + +#import "RCTShadowTextView.h" + +@implementation RCTShadowTextView + +- (BOOL)isYogaLeafNode +{ + return YES; +} + +@end diff --git a/Libraries/Text/RCTText.xcodeproj/project.pbxproj b/Libraries/Text/RCTText.xcodeproj/project.pbxproj index 8e7ee1f1a20598..e2cfafd68291b7 100644 --- a/Libraries/Text/RCTText.xcodeproj/project.pbxproj +++ b/Libraries/Text/RCTText.xcodeproj/project.pbxproj @@ -27,6 +27,10 @@ 58B511D01A9E6C5C00147676 /* RCTShadowText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CB1A9E6C5C00147676 /* RCTShadowText.m */; }; 58B511D11A9E6C5C00147676 /* RCTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */; }; 58B512161A9E6EFF00147676 /* RCTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512141A9E6EFF00147676 /* RCTText.m */; }; + 59F60E911E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; }; + 59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; }; + 59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; + 59F60E941E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; }; AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; }; /* End PBXBuildFile section */ @@ -54,6 +58,10 @@ 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextManager.m; sourceTree = ""; }; 58B512141A9E6EFF00147676 /* RCTText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTText.m; sourceTree = ""; }; 58B512151A9E6EFF00147676 /* RCTText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTText.h; sourceTree = ""; }; + 59F60E8D1E661BDD0081153B /* RCTShadowTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextField.h; sourceTree = ""; }; + 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextField.m; sourceTree = ""; }; + 59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextView.h; sourceTree = ""; }; + 59F60E901E661BDD0081153B /* RCTShadowTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextView.m; sourceTree = ""; }; AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+Text.h"; sourceTree = ""; }; AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -62,29 +70,33 @@ 58B511921A9E6C1200147676 = { isa = PBXGroup; children = ( + 58B5119C1A9E6C1200147676 /* Products */, AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */, AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */, - 19FC5C861D41A4220090108F /* RCTTextSelection.h */, - 19FC5C841D41A4120090108F /* RCTTextSelection.m */, 58B511C61A9E6C5C00147676 /* RCTRawTextManager.h */, 58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */, 58B511C81A9E6C5C00147676 /* RCTShadowRawText.h */, 58B511C91A9E6C5C00147676 /* RCTShadowRawText.m */, 58B511CA1A9E6C5C00147676 /* RCTShadowText.h */, 58B511CB1A9E6C5C00147676 /* RCTShadowText.m */, + 59F60E8D1E661BDD0081153B /* RCTShadowTextField.h */, + 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */, + 59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */, + 59F60E901E661BDD0081153B /* RCTShadowTextView.m */, 58B512151A9E6EFF00147676 /* RCTText.h */, 58B512141A9E6EFF00147676 /* RCTText.m */, - 58B511CC1A9E6C5C00147676 /* RCTTextManager.h */, - 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */, 1362F0FC1B4D51F400E06D8C /* RCTTextField.h */, 1362F0FD1B4D51F400E06D8C /* RCTTextField.m */, 1362F0FE1B4D51F400E06D8C /* RCTTextFieldManager.h */, 1362F0FF1B4D51F400E06D8C /* RCTTextFieldManager.m */, + 58B511CC1A9E6C5C00147676 /* RCTTextManager.h */, + 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */, + 19FC5C861D41A4220090108F /* RCTTextSelection.h */, + 19FC5C841D41A4120090108F /* RCTTextSelection.m */, 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */, 131B6ABD1AF0CD0600FFC3E0 /* RCTTextView.m */, 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */, 131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */, - 58B5119C1A9E6C1200147676 /* Products */, ); indentWidth = 2; sourceTree = ""; @@ -180,8 +192,10 @@ 2D3B5F3B1D9B106F00451313 /* RCTTextView.m in Sources */, 2D3B5F3A1D9B106F00451313 /* RCTTextFieldManager.m in Sources */, 2D3B5F341D9B103100451313 /* RCTRawTextManager.m in Sources */, + 59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */, AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */, 2D3B5F3C1D9B106F00451313 /* RCTTextViewManager.m in Sources */, + 59F60E941E661BDD0081153B /* RCTShadowTextView.m in Sources */, 2D3B5F331D9B102D00451313 /* RCTTextSelection.m in Sources */, 2D3B5F351D9B103300451313 /* RCTShadowRawText.m in Sources */, ); @@ -198,8 +212,10 @@ 1362F1001B4D51F400E06D8C /* RCTTextField.m in Sources */, 58B512161A9E6EFF00147676 /* RCTText.m in Sources */, 1362F1011B4D51F400E06D8C /* RCTTextFieldManager.m in Sources */, + 59F60E911E661BDD0081153B /* RCTShadowTextField.m in Sources */, AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */, 131B6AC11AF0CD0600FFC3E0 /* RCTTextViewManager.m in Sources */, + 59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */, 58B511CF1A9E6C5C00147676 /* RCTShadowRawText.m in Sources */, 58B511D01A9E6C5C00147676 /* RCTShadowText.m in Sources */, ); diff --git a/Libraries/Text/RCTTextFieldManager.m b/Libraries/Text/RCTTextFieldManager.m index 72e205ae417322..27287c32afc262 100644 --- a/Libraries/Text/RCTTextFieldManager.m +++ b/Libraries/Text/RCTTextFieldManager.m @@ -14,6 +14,7 @@ #import #import "RCTConvert+Text.h" +#import "RCTShadowTextField.h" #import "RCTTextField.h" @@ -21,6 +22,11 @@ @implementation RCTTextFieldManager RCT_EXPORT_MODULE() +- (RCTShadowView *)shadowView +{ + return [RCTShadowTextField new]; +} + - (UIView *)view { return [[RCTTextField alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index c599526b9f968c..7b7f5d0473d422 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -15,12 +15,18 @@ #import #import "RCTConvert+Text.h" +#import "RCTShadowTextView.h" #import "RCTTextView.h" @implementation RCTTextViewManager RCT_EXPORT_MODULE() +- (RCTShadowView *)shadowView +{ + return [RCTShadowTextView new]; +} + - (UIView *)view { return [[RCTTextView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; From 22e03a09b12c1f32e5fd57fd2047314cf655065c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Mon, 13 Mar 2017 16:25:38 -0700 Subject: [PATCH 012/366] Fix example code Summary: Closes https://github.com/facebook/react-native/pull/12908 Differential Revision: D4701705 Pulled By: hramos fbshipit-source-id: 9ac33e57e0df73b4b8a0d1a05a4254e7482e2ef6 --- blog/2017-03-13-better-list-views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/2017-03-13-better-list-views.md b/blog/2017-03-13-better-list-views.md index c18e6f29b1cbd7..3d7b7574501ac3 100644 --- a/blog/2017-03-13-better-list-views.md +++ b/blog/2017-03-13-better-list-views.md @@ -17,7 +17,7 @@ This is the workhorse component for simple, performant lists. Provide an array o ``` } /> ``` From 8a7eb170dd4ce42023eb8c20f65fb60145880524 Mon Sep 17 00:00:00 2001 From: Edwin Date: Mon, 13 Mar 2017 23:49:47 -0700 Subject: [PATCH 013/366] Adds Animated.loop to Animated API Summary: * Any animation can be looped on the javascript thread * Only basic animations supported natively at this stage, loops run using the native driver cannot contain animations of type sequence, parallel, stagger, or loop Motivation: We need a spinner in our app that is displayed and animated while the javascript thread is tied up with other tasks. This means it needs to be offloaded from the javascript thread, so that it will continue to run while those tasks are churning away. I originally submitted PR #9513, which has served our needs, but brentvatne pointed out a better way to do it. Had hoped his suggestion would be implemented by janicduplessis or another fb employee, but after 5 months I thought I'd give it another push. I've put together an implementation that basically matches the suggested API. Let me know what you think, and whether others can pick it up from here and get it in to core. Personal Motivation: I am leaving my current organisation on Feb 10th, so am trying to clean thing Closes https://github.com/facebook/react-native/pull/11973 Differential Revision: D4704381 fbshipit-source-id: 42a2cdf5d53a7c0d08f86a58485f7f38739e6cd9 --- .../Animated/src/AnimatedImplementation.js | 222 ++++++++++++++++-- .../Animated/src/__tests__/Animated-test.js | 177 ++++++++++++++ .../react/animated/DecayAnimation.java | 26 +- .../animated/FrameBasedAnimationDriver.java | 13 +- .../react/animated/SpringAnimation.java | 20 +- .../NativeAnimatedNodeTraversalTest.java | 175 ++++++++++++++ 6 files changed, 603 insertions(+), 30 deletions(-) diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index e0860eced71cac..1c06b9a6e4ab13 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -97,6 +97,7 @@ type AnimationConfig = { isInteraction?: bool, useNativeDriver?: bool, onComplete?: ?EndCallback, + iterations?: number, }; // Important note: start() and stop() will only be called at most once. @@ -107,6 +108,7 @@ class Animation { __isInteraction: bool; __nativeId: number; __onEnd: ?EndCallback; + __iterations: number; start( fromValue: number, onUpdate: (value: number) => void, @@ -228,7 +230,7 @@ function _flush(rootNode: AnimatedValue): void { animatedStyles.forEach(animatedStyle => animatedStyle.update()); } -type TimingAnimationConfig = AnimationConfig & { +type TimingAnimationConfig = AnimationConfig & { toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY, easing?: (value: number) => number, duration?: number, @@ -271,6 +273,7 @@ class TimingAnimation extends Animation { this._easing = config.easing !== undefined ? config.easing : easeInOut(); this._duration = config.duration !== undefined ? config.duration : 500; this._delay = config.delay !== undefined ? config.delay : 0; + this.__iterations = config.iterations !== undefined ? config.iterations : 1; this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; this._useNativeDriver = shouldUseNativeDriver(config); } @@ -286,7 +289,8 @@ class TimingAnimation extends Animation { type: 'frames', frames, toValue: this._toValue, - delay: this._delay + delay: this._delay, + iterations: this.__iterations, }; } @@ -386,6 +390,7 @@ class DecayAnimation extends Animation { this._velocity = config.velocity; this._useNativeDriver = shouldUseNativeDriver(config); this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; + this.__iterations = config.iterations !== undefined ? config.iterations : 1; } __getNativeAnimationConfig() { @@ -393,6 +398,7 @@ class DecayAnimation extends Animation { type: 'decay', deceleration: this._deceleration, velocity: this._velocity, + iterations: this.__iterations, }; } @@ -505,6 +511,7 @@ class SpringAnimation extends Animation { this._toValue = config.toValue; this._useNativeDriver = shouldUseNativeDriver(config); this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; + this.__iterations = config.iterations !== undefined ? config.iterations : 1; var springConfig; if (config.bounciness !== undefined || config.speed !== undefined) { @@ -536,6 +543,7 @@ class SpringAnimation extends Animation { friction: this._friction, initialVelocity: withDefault(this._initialVelocity, this._lastVelocity), toValue: this._toValue, + iterations: this.__iterations, }; } @@ -695,6 +703,7 @@ var _uniqueId = 1; */ class AnimatedValue extends AnimatedWithChildren { _value: number; + _startingValue: number; _offset: number; _animation: ?Animation; _tracking: ?Animated; @@ -703,7 +712,7 @@ class AnimatedValue extends AnimatedWithChildren { constructor(value: number) { super(); - this._value = value; + this._startingValue = this._value = value; this._offset = 0; this._animation = null; this._listeners = {}; @@ -846,6 +855,14 @@ class AnimatedValue extends AnimatedWithChildren { callback && callback(this.__getValue()); } + /** + * Stops any animation and resets the value to its original + */ + resetAnimation(callback?: ?(value: number) => void): void { + this.stopAnimation(callback); + this._value = this._startingValue; + } + /** * Interpolates the value before updating the property, e.g. mapping 0-1 to * 0-10. @@ -1013,6 +1030,12 @@ class AnimatedValueXY extends AnimatedWithChildren { }; } + resetAnimation(callback?: (value: {x: number, y: number}) => void): void { + this.x.resetAnimation(); + this.y.resetAnimation(); + callback && callback(this.__getValue()); + } + stopAnimation(callback?: (value: {x: number, y: number}) => void): void { this.x.stopAnimation(); this.y.stopAnimation(); @@ -1888,6 +1911,9 @@ class AnimatedTracking extends Animated { type CompositeAnimation = { start: (callback?: ?EndCallback) => void, stop: () => void, + reset: () => void, + _startNativeLoop: (iterations?: number) => void, + _isUsingNativeDriver: () => boolean, }; var add = function( @@ -1965,16 +1991,18 @@ var spring = function( value: AnimatedValue | AnimatedValueXY, config: SpringAnimationConfig, ): CompositeAnimation { - return maybeVectorAnim(value, config, spring) || { - start: function(callback?: ?EndCallback): void { - callback = _combineCallbacks(callback, config); - var singleValue: any = value; - var singleConfig: any = config; + var start = function( + animatedValue: AnimatedValue | AnimatedValueXY, + configuration: SpringAnimationConfig, + callback?: ?EndCallback): void { + callback = _combineCallbacks(callback, configuration); + var singleValue: any = animatedValue; + var singleConfig: any = configuration; singleValue.stopTracking(); - if (config.toValue instanceof Animated) { + if (configuration.toValue instanceof Animated) { singleValue.track(new AnimatedTracking( singleValue, - config.toValue, + configuration.toValue, SpringAnimation, singleConfig, callback @@ -1982,11 +2010,28 @@ var spring = function( } else { singleValue.animate(new SpringAnimation(singleConfig), callback); } + }; + return maybeVectorAnim(value, config, spring) || { + start: function(callback?: ?EndCallback): void { + start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, + + reset: function(): void { + value.resetAnimation(); + }, + + _startNativeLoop: function(iterations?: number): void { + var singleConfig = { ...config, iterations }; + start(value, singleConfig); + }, + + _isUsingNativeDriver: function(): boolean { + return config.useNativeDriver || false; + } }; }; @@ -1994,16 +2039,18 @@ var timing = function( value: AnimatedValue | AnimatedValueXY, config: TimingAnimationConfig, ): CompositeAnimation { - return maybeVectorAnim(value, config, timing) || { - start: function(callback?: ?EndCallback): void { - callback = _combineCallbacks(callback, config); - var singleValue: any = value; - var singleConfig: any = config; + var start = function( + animatedValue: AnimatedValue | AnimatedValueXY, + configuration: TimingAnimationConfig, + callback?: ?EndCallback): void { + callback = _combineCallbacks(callback, configuration); + var singleValue: any = animatedValue; + var singleConfig: any = configuration; singleValue.stopTracking(); - if (config.toValue instanceof Animated) { + if (configuration.toValue instanceof Animated) { singleValue.track(new AnimatedTracking( singleValue, - config.toValue, + configuration.toValue, TimingAnimation, singleConfig, callback @@ -2011,11 +2058,29 @@ var timing = function( } else { singleValue.animate(new TimingAnimation(singleConfig), callback); } + }; + + return maybeVectorAnim(value, config, timing) || { + start: function(callback?: ?EndCallback): void { + start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, + + reset: function(): void { + value.resetAnimation(); + }, + + _startNativeLoop: function(iterations?: number): void { + var singleConfig = { ...config, iterations }; + start(value, singleConfig); + }, + + _isUsingNativeDriver: function(): boolean { + return config.useNativeDriver || false; + } }; }; @@ -2023,18 +2088,38 @@ var decay = function( value: AnimatedValue | AnimatedValueXY, config: DecayAnimationConfig, ): CompositeAnimation { - return maybeVectorAnim(value, config, decay) || { - start: function(callback?: ?EndCallback): void { - callback = _combineCallbacks(callback, config); - var singleValue: any = value; - var singleConfig: any = config; + var start = function( + animatedValue: AnimatedValue | AnimatedValueXY, + configuration: DecayAnimationConfig, + callback?: ?EndCallback): void { + callback = _combineCallbacks(callback, configuration); + var singleValue: any = animatedValue; + var singleConfig: any = configuration; singleValue.stopTracking(); singleValue.animate(new DecayAnimation(singleConfig), callback); + }; + + return maybeVectorAnim(value, config, decay) || { + start: function(callback?: ?EndCallback): void { + start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, + + reset: function(): void { + value.resetAnimation(); + }, + + _startNativeLoop: function(iterations?: number): void { + var singleConfig = { ...config, iterations }; + start(value, singleConfig); + }, + + _isUsingNativeDriver: function(): boolean { + return config.useNativeDriver || false; + } }; }; @@ -2071,6 +2156,23 @@ var sequence = function( if (current < animations.length) { animations[current].stop(); } + }, + + reset: function() { + animations.forEach((animation, idx) => { + if (idx <= current) { + animation.reset(); + } + }); + current = 0; + }, + + _startNativeLoop: function() { + throw new Error('Loops run using the native driver cannot contain Animated.sequence animations'); + }, + + _isUsingNativeDriver: function(): boolean { + return false; } }; }; @@ -2122,6 +2224,22 @@ var parallel = function( !hasEnded[idx] && animation.stop(); hasEnded[idx] = true; }); + }, + + reset: function(): void { + animations.forEach((animation, idx) => { + animation.reset(); + hasEnded[idx] = false; + doneCount = 0; + }); + }, + + _startNativeLoop: function() { + throw new Error('Loops run using the native driver cannot contain Animated.parallel animations'); + }, + + _isUsingNativeDriver: function(): boolean { + return false; } }; @@ -2145,6 +2263,59 @@ var stagger = function( })); }; +type LoopAnimationConfig = { iterations: number }; + +var loop = function( + animation: CompositeAnimation, + { iterations = -1 }: LoopAnimationConfig = {}, +): CompositeAnimation { + var isFinished = false; + var iterationsSoFar = 0; + return { + start: function(callback?: ?EndCallback) { + var restart = function(result: EndResult = {finished: true}): void { + if (isFinished || + (iterationsSoFar === iterations) || + (result.finished === false)) { + callback && callback(result); + } else { + iterationsSoFar++; + animation.reset(); + animation.start(restart); + } + }; + if (!animation || iterations === 0) { + callback && callback({finished: true}); + } else { + if (animation._isUsingNativeDriver()) { + animation._startNativeLoop(iterations); + } else { + restart(); // Start looping recursively on the js thread + } + } + }, + + stop: function(): void { + isFinished = true; + animation.stop(); + }, + + reset: function(): void { + iterationsSoFar = 0; + isFinished = false; + animation.reset(); + }, + + _startNativeLoop: function() { + throw new Error('Loops run using the native driver cannot contain Animated.loop animations'); + }, + + _isUsingNativeDriver: function(): boolean { + return animation._isUsingNativeDriver(); + } + }; +}; + type Mapping = {[key: string]: Mapping} | AnimatedValue; type EventConfig = { listener?: ?Function, @@ -2606,6 +2777,13 @@ module.exports = { * sequence with successive delays. Nice for doing trailing effects. */ stagger, + /** + * Loops a given animation continuously, so that each time it reaches the + * end, it resets and begins again from the start. Can specify number of + * times to loop using the key 'iterations' in the config. Will loop without + * blocking the UI thread if the child animation is set to 'useNativeDriver'. + */ + loop, /** * Takes an array of mappings and extracts values from each arg accordingly, diff --git a/Libraries/Animated/src/__tests__/Animated-test.js b/Libraries/Animated/src/__tests__/Animated-test.js index fc313a8af0582b..c89d2979995a21 100644 --- a/Libraries/Animated/src/__tests__/Animated-test.js +++ b/Libraries/Animated/src/__tests__/Animated-test.js @@ -214,6 +214,183 @@ describe('Animated tests', () => { }); }); + describe('Animated Loop', () => { + + it('loops indefinitely if config not specified', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(4); + expect(cb).not.toBeCalled(); + }); + + it('loops indefinitely if iterations is -1', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: -1 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(4); + expect(cb).not.toBeCalled(); + }); + + it('loops indefinitely if iterations not specified', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { anotherKey: 'value' }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(4); + expect(cb).not.toBeCalled(); + }); + + it('loops three times if iterations is 3', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: 3 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).toBeCalledWith({finished: true}); + }); + + it('does not loop if iterations is 1', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: 1 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(cb).toBeCalledWith({finished: true}); + }); + + it('does not animate if iterations is 0', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: 0 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).not.toBeCalled(); + expect(cb).toBeCalledWith({ finished: true }); + }); + + it('supports interrupting an indefinite loop', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + Animated.loop(animation).start(cb); + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: false}); // Interrupt loop + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).toBeCalledWith({finished: false}); + }); + + it('supports stopping loop', () => { + var animation = {start: jest.fn(), stop: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation); + loop.start(cb); + loop.stop(); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(animation.stop).toBeCalled(); + + animation.start.mock.calls[0][0]({finished: false}); // Interrupt loop + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).toBeCalledWith({finished: false}); + }); + }); + describe('Animated Parallel', () => { it('works with an empty parallel', () => { diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java b/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java index 84dac0623b17ed..41b6d24ff31239 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java @@ -21,12 +21,17 @@ public class DecayAnimation extends AnimationDriver { private final double mDeceleration; private long mStartFrameTimeMillis = -1; - private double mFromValue; - private double mLastValue; + private double mFromValue = 0d; + private double mLastValue = 0d; + private int mIterations; + private int mCurrentLoop; public DecayAnimation(ReadableMap config) { mVelocity = config.getDouble("velocity"); mDeceleration = config.getDouble("deceleration"); + mIterations = config.hasKey("iterations") ? config.getInt("iterations") : 1; + mCurrentLoop = 1; + mHasFinished = mIterations == 0; } @Override @@ -35,7 +40,11 @@ public void runAnimationStep(long frameTimeNanos) { if (mStartFrameTimeMillis == -1) { // since this is the first animation step, consider the start to be on the previous frame mStartFrameTimeMillis = frameTimeMillis - 16; - mFromValue = mAnimatedValue.mValue; + if (mFromValue == mLastValue) { // first iteration, assign mFromValue based on mAnimatedValue + mFromValue = mAnimatedValue.mValue; + } else { // not the first iteration, reset mAnimatedValue based on mFromValue + mAnimatedValue.mValue = mFromValue; + } mLastValue = mAnimatedValue.mValue; } @@ -44,8 +53,15 @@ public void runAnimationStep(long frameTimeNanos) { (1 - Math.exp(-(1 - mDeceleration) * (frameTimeMillis - mStartFrameTimeMillis))); if (Math.abs(mLastValue - value) < 0.1) { - mHasFinished = true; - return; + + if (mIterations == -1 || mCurrentLoop < mIterations) { // looping animation, return to start + // set mStartFrameTimeMillis to -1 to reset instance variables on the next runAnimationStep + mStartFrameTimeMillis = -1; + mCurrentLoop++; + } else { // animation has completed + mHasFinished = true; + return; + } } mLastValue = value; diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java b/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java index 86eac84521b8c7..94b12178979326 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java @@ -26,6 +26,8 @@ class FrameBasedAnimationDriver extends AnimationDriver { private final double[] mFrames; private final double mToValue; private double mFromValue; + private int mIterations; + private int mCurrentLoop; FrameBasedAnimationDriver(ReadableMap config) { ReadableArray frames = config.getArray("frames"); @@ -35,6 +37,9 @@ class FrameBasedAnimationDriver extends AnimationDriver { mFrames[i] = frames.getDouble(i); } mToValue = config.getDouble("toValue"); + mIterations = config.hasKey("iterations") ? config.getInt("iterations") : 1; + mCurrentLoop = 1; + mHasFinished = mIterations == 0; } @Override @@ -53,9 +58,13 @@ public void runAnimationStep(long frameTimeNanos) { } double nextValue; if (frameIndex >= mFrames.length - 1) { - // animation has completed, no more frames left - mHasFinished = true; nextValue = mToValue; + if (mIterations == -1 || mCurrentLoop < mIterations) { // looping animation, return to start + mStartFrameTimeNanos = frameTimeNanos; + mCurrentLoop++; + } else { // animation has completed, no more frames left + mHasFinished = true; + } } else { nextValue = mFromValue + mFrames[frameIndex] * (mToValue - mFromValue); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/SpringAnimation.java b/ReactAndroid/src/main/java/com/facebook/react/animated/SpringAnimation.java index 7cb14d109f62e3..a57a9152dfd34f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/SpringAnimation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/SpringAnimation.java @@ -38,6 +38,10 @@ private static class PhysicsState { private double mRestSpeedThreshold; private double mDisplacementFromRestThreshold; private double mTimeAccumulator = 0; + // for controlling loop + private int mIterations; + private int mCurrentLoop = 0; + private double mOriginalValue; SpringAnimation(ReadableMap config) { mSpringFriction = config.getDouble("friction"); @@ -47,12 +51,18 @@ private static class PhysicsState { mRestSpeedThreshold = config.getDouble("restSpeedThreshold"); mDisplacementFromRestThreshold = config.getDouble("restDisplacementThreshold"); mOvershootClampingEnabled = config.getBoolean("overshootClamping"); + mIterations = config.hasKey("iterations") ? config.getInt("iterations") : 1; + mHasFinished = mIterations == 0; } @Override public void runAnimationStep(long frameTimeNanos) { long frameTimeMillis = frameTimeNanos / 1000000; if (!mSpringStarted) { + if (mCurrentLoop == 0) { + mOriginalValue = mAnimatedValue.mValue; + mCurrentLoop = 1; + } mStartValue = mCurrentState.position = mAnimatedValue.mValue; mLastTime = frameTimeMillis; mSpringStarted = true; @@ -60,7 +70,15 @@ public void runAnimationStep(long frameTimeNanos) { advance((frameTimeMillis - mLastTime) / 1000.0); mLastTime = frameTimeMillis; mAnimatedValue.mValue = mCurrentState.position; - mHasFinished = isAtRest(); + if (isAtRest()) { + if (mIterations == -1 || mCurrentLoop < mIterations) { // looping animation, return to start + mSpringStarted = false; + mAnimatedValue.mValue = mOriginalValue; + mCurrentLoop++; + } else { // animation has completed + mHasFinished = true; + } + } } /** diff --git a/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java b/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java index 5476ab5ea2ee2f..ced79811c36960 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java @@ -167,6 +167,42 @@ public void testFramesAnimation() { verifyNoMoreInteractions(mUIImplementationMock); } + @Test + public void testFramesAnimationLoopsFiveTimes() { + createSimpleAnimatedViewWithOpacity(1000, 0d); + + JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d); + Callback animationCallback = mock(Callback.class); + mNativeAnimatedNodesManager.startAnimatingNode( + 1, + 1, + JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d, "iterations", 5), + animationCallback); + + ArgumentCaptor stylesCaptor = + ArgumentCaptor.forClass(ReactStylesDiffMap.class); + + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture()); + assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0); + + for (int iteration = 0; iteration < 5; iteration++) { + for (int i = 0; i < frames.size(); i++) { + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verify(mUIImplementationMock) + .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture()); + assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)) + .isEqualTo(frames.getDouble(i)); + } + } + + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verifyNoMoreInteractions(mUIImplementationMock); + } + @Test public void testNodeValueListenerIfNotListening() { int nodeId = 1; @@ -285,6 +321,84 @@ public void testSpringAnimation() { verifyNoMoreInteractions(mUIImplementationMock); } + @Test + public void testSpringAnimationLoopsFiveTimes() { + createSimpleAnimatedViewWithOpacity(1000, 0d); + + Callback animationCallback = mock(Callback.class); + mNativeAnimatedNodesManager.startAnimatingNode( + 1, + 1, + JavaOnlyMap.of( + "type", + "spring", + "friction", + 7d, + "tension", + 40.0d, + "initialVelocity", + 0d, + "toValue", + 1d, + "restSpeedThreshold", + 0.001d, + "restDisplacementThreshold", + 0.001d, + "overshootClamping", + false, + "iterations", + 5), + animationCallback); + + ArgumentCaptor stylesCaptor = + ArgumentCaptor.forClass(ReactStylesDiffMap.class); + + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture()); + assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0); + + double previousValue = 0d; + boolean wasGreaterThanOne = false; + boolean didComeToRest = false; + int numberOfResets = 0; + /* run 3 secs of animation, five times */ + for (int i = 0; i < 3 * 60 * 5; i++) { + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verify(mUIImplementationMock, atMost(1)) + .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture()); + double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN); + if (currentValue > 1d) { + wasGreaterThanOne = true; + } + // Test to see if it reset after coming to rest + if (didComeToRest && + currentValue == 0d && + Math.abs(Math.abs(currentValue - previousValue) - 1d) < 0.001d) { + numberOfResets++; + } + + // verify that an animation step is relatively small, unless it has come to rest and reset + if (!didComeToRest) assertThat(Math.abs(currentValue - previousValue)).isLessThan(0.1d); + + + // record that the animation did come to rest when it rests on toValue + didComeToRest = Math.abs(currentValue - 1d) < 0.001d && + Math.abs(currentValue - previousValue) < 0.001d; + previousValue = currentValue; + } + // verify that we've reach the final value at the end of animation + assertThat(previousValue).isEqualTo(1d); + // verify that value has reached some maximum value that is greater than the final value (bounce) + assertThat(wasGreaterThanOne); + // verify that value reset 4 times after finishing a full animation + assertThat(numberOfResets).isEqualTo(4); + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verifyNoMoreInteractions(mUIImplementationMock); + } + @Test public void testDecayAnimation() { createSimpleAnimatedViewWithOpacity(1000, 0d); @@ -342,6 +456,67 @@ public void testDecayAnimation() { verifyNoMoreInteractions(mUIImplementationMock); } + @Test + public void testDecayAnimationLoopsFiveTimes() { + createSimpleAnimatedViewWithOpacity(1000, 0d); + + Callback animationCallback = mock(Callback.class); + mNativeAnimatedNodesManager.startAnimatingNode( + 1, + 1, + JavaOnlyMap.of( + "type", + "decay", + "velocity", + 0.5d, + "deceleration", + 0.998d, + "iterations", + 5), + animationCallback); + + ArgumentCaptor stylesCaptor = + ArgumentCaptor.forClass(ReactStylesDiffMap.class); + + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verify(mUIImplementationMock, atMost(1)) + .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture()); + double previousValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN); + double previousDiff = Double.POSITIVE_INFINITY; + double initialValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN); + boolean didComeToRest = false; + int numberOfResets = 0; + /* run 3 secs of animation, five times */ + for (int i = 0; i < 3 * 60 * 5; i++) { + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verify(mUIImplementationMock, atMost(1)) + .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture()); + double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN); + double currentDiff = currentValue - previousValue; + // Test to see if it reset after coming to rest (i.e. dropped back to ) + if (didComeToRest && currentValue == initialValue) { + numberOfResets++; + } + + // verify monotonicity, unless it has come to rest and reset + // greater *or equal* because the animation stops during these 3 seconds + if (!didComeToRest) assertThat(currentValue).as("on frame " + i).isGreaterThanOrEqualTo(previousValue); + + // Test if animation has come to rest using the 0.1 threshold from DecayAnimation.java + didComeToRest = Math.abs(currentDiff) < 0.1d; + previousValue = currentValue; + previousDiff = currentDiff; + } + + // verify that value reset (looped) 4 times after finishing a full animation + assertThat(numberOfResets).isEqualTo(4); + reset(mUIImplementationMock); + mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); + verifyNoMoreInteractions(mUIImplementationMock); + } + @Test public void testAnimationCallbackFinish() { createSimpleAnimatedViewWithOpacity(1000, 0d); From fbd1b0a6654d87b236a382d52575d637f87acf24 Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Tue, 14 Mar 2017 08:11:59 -0700 Subject: [PATCH 014/366] Temp disable OSS Android e2e test until we debug and fix it Reviewed By: mkonicek Differential Revision: D4705570 Ninja: OSS only fbshipit-source-id: 9aa401c0e030794accabcdc4861f5bf9950ff4d8 --- circle.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 00cad8d46afdfb..945e6b7412ff4c 100644 --- a/circle.yml +++ b/circle.yml @@ -76,7 +76,9 @@ test: - node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests # Android e2e test - - source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2 + # Temp disabled to debug an ongoing issue (so far we think it's packager related). + # Re-enable this ASAP once the issue is fixed. + #- source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2 # testing docs generation - cd website && npm test From 8ac35c8a0e31a0d5d384e18fc07fc09fd9bcd4e1 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 14 Mar 2017 11:01:19 -0700 Subject: [PATCH 015/366] packager: react-packager.js: reinforce the typing at API boundary Reviewed By: davidaurelio Differential Revision: D4698256 fbshipit-source-id: 7a42e9b79744f599e92b5d3d91c41cdd0de2ece5 --- packager/react-packager.js | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packager/react-packager.js b/packager/react-packager.js index 1e95d24ed1b91a..7525ec52abc066 100644 --- a/packager/react-packager.js +++ b/packager/react-packager.js @@ -16,6 +16,7 @@ const Logger = require('./src/Logger'); const debug = require('debug'); const invariant = require('fbjs/lib/invariant'); +import type Server from './src/Server'; import type GlobalTransformCache from './src/lib/GlobalTransformCache'; import type {Reporter} from './src/lib/reporting'; import type {HasteImpl} from './src/node-haste/Module'; @@ -41,9 +42,27 @@ type StrictOptions = { watch?: boolean, }; -exports.buildBundle = function(options: Options, bundleOptions: {}) { +/** + * This is a public API, so we don't trust the values and consider them as + * `mixed`. Because it understands `invariant`, Flow ensure that we refine these + * values completely. + */ +exports.buildBundle = function(options: Options, bundleOptions: mixed) { + invariant( + typeof bundleOptions === 'object' && bundleOptions != null, + '`bundleOptions` must be an object', + ); + const {entryFile, platform} = bundleOptions; + invariant( + platform === undefined || typeof platform === 'string', + '`bundleOptions` field `platform` must be a string', + ); + invariant( + typeof entryFile === 'string', + '`bundleOptions` must contain a string field `entryFile`', + ); var server = createNonPersistentServer(options); - return server.buildBundle(bundleOptions) + return server.buildBundle({...bundleOptions, entryFile, platform}) .then(p => { server.end(); return p; @@ -73,7 +92,7 @@ function enableDebug() { debug.enable(debugPattern); } -function createServer(options: StrictOptions) { +function createServer(options: StrictOptions): Server { // the debug module is configured globally, we need to enable debugging // *before* requiring any packages that use `debug` for logging if (options.verbose) { @@ -84,11 +103,11 @@ function createServer(options: StrictOptions) { invariant(options.reporter != null, 'createServer() requires reporter'); const serverOptions = Object.assign({}, options); delete serverOptions.verbose; - var Server = require('./src/Server'); - return new Server(serverOptions); + const ServerClass = require('./src/Server'); + return new ServerClass(serverOptions); } -function createNonPersistentServer(options: Options) { +function createNonPersistentServer(options: Options): Server { const serverOptions = { // It's unsound to set-up the reporter here, // but this allows backward compatibility. From 4203c9c8374104017a8b3e8dc4b6e46c31f724a8 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 14 Mar 2017 11:01:21 -0700 Subject: [PATCH 016/366] packager: fix buildBundle() options Summary: The problem with `bundleOpts` is that it discards Flow typing, so it prevents reinforcing the integration of `Bundler` into `Server`. This changeset removes the `bundleOpts` to solve that issues. Instead, it makes the options explicit so that there is less uncertaintly. I love making options explicit, because they force callsites to take a consicious decision about what is really needed, making them more robust. They also expose oddities that probably needs refatoring, for example having a `resolutionRequest` in the bundle options does not seem correct, it should be an implementation details. Likewise, `onProgress` should probably be exposed differently, as it does not affect the content of the bundle itself. Reviewed By: davidaurelio Differential Revision: D4697729 fbshipit-source-id: d543870ba024e7588c10b101fa51429c77cc5ddc --- local-cli/bundle/output/bundle.js | 5 +- local-cli/bundle/output/unbundle/index.js | 5 +- packager/react-packager.js | 59 +++++--- packager/src/Bundler/Bundle.js | 4 +- packager/src/Bundler/index.js | 2 +- packager/src/Server/__tests__/Server-test.js | 93 ++++++------ packager/src/Server/index.js | 140 +++++++------------ 7 files changed, 149 insertions(+), 159 deletions(-) diff --git a/local-cli/bundle/output/bundle.js b/local-cli/bundle/output/bundle.js index c4b6effe1e6d30..0b1487f3c4d5b3 100644 --- a/local-cli/bundle/output/bundle.js +++ b/local-cli/bundle/output/bundle.js @@ -8,18 +8,21 @@ * * @flow */ + 'use strict'; +const Server = require('../../../packager/src/Server'); + const meta = require('./meta'); const relativizeSourceMap = require('../../../packager/src//lib/relativizeSourceMap'); const writeFile = require('./writeFile'); import type Bundle from '../../../packager/src//Bundler/Bundle'; -import type Server from '../../../packager/src//Server'; import type {OutputOptions, RequestOptions} from '../types.flow'; function buildBundle(packagerClient: Server, requestOptions: RequestOptions) { return packagerClient.buildBundle({ + ...Server.DEFAULT_BUNDLE_OPTIONS, ...requestOptions, isolateModuleIDs: true, }); diff --git a/local-cli/bundle/output/unbundle/index.js b/local-cli/bundle/output/unbundle/index.js index e830311dec9ad4..62c13f819ee1fe 100644 --- a/local-cli/bundle/output/unbundle/index.js +++ b/local-cli/bundle/output/unbundle/index.js @@ -8,17 +8,20 @@ * * @flow */ + 'use strict'; +const Server = require('../../../../packager/src/Server'); + const asAssets = require('./as-assets'); const asIndexedFile = require('./as-indexed-file'); import type Bundle from '../../../../packager/src//Bundler/Bundle'; -import type Server from '../../../../packager/src//Server'; import type {OutputOptions, RequestOptions} from '../../types.flow'; function buildBundle(packagerClient: Server, requestOptions: RequestOptions) { return packagerClient.buildBundle({ + ...Server.DEFAULT_BUNDLE_OPTIONS, ...requestOptions, unbundle: true, isolateModuleIDs: true, diff --git a/packager/react-packager.js b/packager/react-packager.js index 7525ec52abc066..e51592303223be 100644 --- a/packager/react-packager.js +++ b/packager/react-packager.js @@ -42,31 +42,46 @@ type StrictOptions = { watch?: boolean, }; +type PublicBundleOptions = { + +dev?: boolean, + +entryFile: string, + +generateSourceMaps?: boolean, + +inlineSourceMap?: boolean, + +minify?: boolean, + +platform?: string, + +runModule?: boolean, + +sourceMapUrl?: string, +}; + /** - * This is a public API, so we don't trust the values and consider them as - * `mixed`. Because it understands `invariant`, Flow ensure that we refine these - * values completely. + * This is a public API, so we don't trust the value and purposefully downgrade + * it as `mixed`. Because it understands `invariant`, Flow ensure that we + * refine these values completely. */ -exports.buildBundle = function(options: Options, bundleOptions: mixed) { - invariant( - typeof bundleOptions === 'object' && bundleOptions != null, - '`bundleOptions` must be an object', - ); - const {entryFile, platform} = bundleOptions; - invariant( - platform === undefined || typeof platform === 'string', - '`bundleOptions` field `platform` must be a string', - ); - invariant( - typeof entryFile === 'string', - '`bundleOptions` must contain a string field `entryFile`', - ); +function assertPublicBundleOptions(bo: mixed): PublicBundleOptions { + invariant(typeof bo === 'object' && bo != null, 'bundle options must be an object'); + invariant(bo.dev === undefined || typeof bo.dev === 'boolean', 'bundle options field `dev` must be a boolean'); + const {entryFile} = bo; + invariant(typeof entryFile === 'string', 'bundle options must contain a string field `entryFile`'); + invariant(bo.generateSourceMaps === undefined || typeof bo.generateSourceMaps === 'boolean', 'bundle options field `generateSourceMaps` must be a boolean'); + invariant(bo.inlineSourceMap === undefined || typeof bo.inlineSourceMap === 'boolean', 'bundle options field `inlineSourceMap` must be a boolean'); + invariant(bo.minify === undefined || typeof bo.minify === 'boolean', 'bundle options field `minify` must be a boolean'); + invariant(bo.platform === undefined || typeof bo.platform === 'string', 'bundle options field `platform` must be a string'); + invariant(bo.runModule === undefined || typeof bo.runModule === 'boolean', 'bundle options field `runModule` must be a boolean'); + invariant(bo.sourceMapUrl === undefined || typeof bo.sourceMapUrl === 'string', 'bundle options field `sourceMapUrl` must be a boolean'); + return {entryFile, ...bo}; +} + +exports.buildBundle = function(options: Options, bundleOptions: PublicBundleOptions) { var server = createNonPersistentServer(options); - return server.buildBundle({...bundleOptions, entryFile, platform}) - .then(p => { - server.end(); - return p; - }); + const ServerClass = require('./src/Server'); + return server.buildBundle({ + ...ServerClass.DEFAULT_BUNDLE_OPTIONS, + ...assertPublicBundleOptions(bundleOptions), + }).then(p => { + server.end(); + return p; + }); }; exports.getOrderedDependencyPaths = function(options: Options, bundleOptions: {}) { diff --git a/packager/src/Bundler/Bundle.js b/packager/src/Bundler/Bundle.js index 2b22ec498dab97..b6bedf22a30574 100644 --- a/packager/src/Bundler/Bundle.js +++ b/packager/src/Bundler/Bundle.js @@ -44,10 +44,10 @@ class Bundle extends BundleBase { _ramGroups: Array | void; _sourceMap: string | null; _sourceMapFormat: SourceMapFormat; - _sourceMapUrl: string | void; + _sourceMapUrl: ?string; constructor({sourceMapUrl, dev, minify, ramGroups}: { - sourceMapUrl?: string, + sourceMapUrl: ?string, dev?: boolean, minify?: boolean, ramGroups?: Array, diff --git a/packager/src/Bundler/index.js b/packager/src/Bundler/index.js index 7066db33f911f3..ce940a7f6cbf85 100644 --- a/packager/src/Bundler/index.js +++ b/packager/src/Bundler/index.js @@ -221,7 +221,7 @@ class Bundler { dev: boolean, minify: boolean, unbundle: boolean, - sourceMapUrl: string, + sourceMapUrl: ?string, }): Promise { const {dev, minify, unbundle} = options; return this._resolverPromise.then( diff --git a/packager/src/Server/__tests__/Server-test.js b/packager/src/Server/__tests__/Server-test.js index 8223190b36320c..361eb4a907fa44 100644 --- a/packager/src/Server/__tests__/Server-test.js +++ b/packager/src/Server/__tests__/Server-test.js @@ -6,6 +6,7 @@ * 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. */ + 'use strict'; jest.disableAutomock(); @@ -144,21 +145,22 @@ describe('processRequest', () => { ).then(response => { expect(response.body).toEqual('this is the source'); expect(Bundler.prototype.bundle).toBeCalledWith({ + assetPlugins: [], + dev: true, entryFile: 'index.ios.js', - inlineSourceMap: false, - minify: false, + entryModuleOnly: false, generateSourceMaps: false, hot: false, - runModule: true, - sourceMapUrl: 'index.ios.includeRequire.map', - dev: true, - platform: undefined, + inlineSourceMap: false, + isolateModuleIDs: false, + minify: false, onProgress: jasmine.any(Function), + platform: undefined, + resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], + runModule: true, + sourceMapUrl: 'index.ios.includeRequire.map', unbundle: false, - entryModuleOnly: false, - isolateModuleIDs: false, - assetPlugins: [], }); }); }); @@ -170,21 +172,22 @@ describe('processRequest', () => { ).then(function(response) { expect(response.body).toEqual('this is the source'); expect(Bundler.prototype.bundle).toBeCalledWith({ + assetPlugins: [], + dev: true, entryFile: 'index.js', - inlineSourceMap: false, - minify: false, + entryModuleOnly: false, generateSourceMaps: false, hot: false, - runModule: true, - sourceMapUrl: 'index.map?platform=ios', - dev: true, - platform: 'ios', + inlineSourceMap: false, + isolateModuleIDs: false, + minify: false, onProgress: jasmine.any(Function), + platform: 'ios', + resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], + runModule: true, + sourceMapUrl: 'index.map?platform=ios', unbundle: false, - entryModuleOnly: false, - isolateModuleIDs: false, - assetPlugins: [], }); }); }); @@ -196,21 +199,22 @@ describe('processRequest', () => { ).then(function(response) { expect(response.body).toEqual('this is the source'); expect(Bundler.prototype.bundle).toBeCalledWith({ + assetPlugins: ['assetPlugin1', 'assetPlugin2'], + dev: true, entryFile: 'index.js', - inlineSourceMap: false, - minify: false, + entryModuleOnly: false, generateSourceMaps: false, hot: false, - runModule: true, - sourceMapUrl: 'index.map?assetPlugin=assetPlugin1&assetPlugin=assetPlugin2', - dev: true, - platform: undefined, + inlineSourceMap: false, + isolateModuleIDs: false, + minify: false, onProgress: jasmine.any(Function), + platform: undefined, + resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], + runModule: true, + sourceMapUrl: 'index.map?assetPlugin=assetPlugin1&assetPlugin=assetPlugin2', unbundle: false, - entryModuleOnly: false, - isolateModuleIDs: false, - assetPlugins: ['assetPlugin1', 'assetPlugin2'], }); }); }); @@ -422,21 +426,26 @@ describe('processRequest', () => { describe('buildbundle(options)', () => { it('Calls the bundler with the correct args', () => { return server.buildBundle({ + ...Server.DEFAULT_BUNDLE_OPTIONS, entryFile: 'foo file', }).then(() => expect(Bundler.prototype.bundle).toBeCalledWith({ + assetPlugins: [], + dev: true, entryFile: 'foo file', + entryModuleOnly: false, + generateSourceMaps: false, + hot: false, inlineSourceMap: false, + isolateModuleIDs: false, minify: false, - hot: false, - runModule: true, - dev: true, + onProgress: null, platform: undefined, + resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], + runModule: true, + sourceMapUrl: null, unbundle: false, - entryModuleOnly: false, - isolateModuleIDs: false, - assetPlugins: [], }) ); }); @@ -447,20 +456,22 @@ describe('processRequest', () => { return server.buildBundleFromUrl('/path/to/foo.bundle?dev=false&runModule=false') .then(() => expect(Bundler.prototype.bundle).toBeCalledWith({ + assetPlugins: [], + dev: false, entryFile: 'path/to/foo.js', - inlineSourceMap: false, - minify: false, + entryModuleOnly: false, generateSourceMaps: true, hot: false, - runModule: false, - sourceMapUrl: '/path/to/foo.map?dev=false&runModule=false', - dev: false, + inlineSourceMap: false, + isolateModuleIDs: false, + minify: false, + onProgress: null, platform: undefined, + resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], + runModule: false, + sourceMapUrl: '/path/to/foo.map?dev=false&runModule=false', unbundle: false, - entryModuleOnly: false, - isolateModuleIDs: false, - assetPlugins: [], }) ); }); diff --git a/packager/src/Server/index.js b/packager/src/Server/index.js index 972723c2883ded..e4b7e9f9eca0b2 100644 --- a/packager/src/Server/index.js +++ b/packager/src/Server/index.js @@ -77,70 +77,24 @@ type Options = { watch?: boolean, }; -const bundleOpts = declareOpts({ - sourceMapUrl: { - type: 'string', - required: false, - }, - entryFile: { - type: 'string', - required: true, - }, - dev: { - type: 'boolean', - default: true, - }, - minify: { - type: 'boolean', - default: false, - }, - runModule: { - type: 'boolean', - default: true, - }, - inlineSourceMap: { - type: 'boolean', - default: false, - }, - platform: { - type: 'string', - required: true, - }, - runBeforeMainModule: { - type: 'array', - default: defaults.runBeforeMainModule, - }, - unbundle: { - type: 'boolean', - default: false, - }, - hot: { - type: 'boolean', - default: false, - }, - entryModuleOnly: { - type: 'boolean', - default: false, - }, - isolateModuleIDs: { - type: 'boolean', - default: false, - }, - resolutionResponse: { - type: 'object', - }, - generateSourceMaps: { - type: 'boolean', - required: false, - }, - assetPlugins: { - type: 'array', - default: [], - }, - onProgress: { - type: 'function', - }, -}); +export type BundleOptions = { + +assetPlugins: Array, + dev: boolean, + entryFile: string, + +entryModuleOnly: boolean, + +generateSourceMaps: boolean, + +hot: boolean, + +inlineSourceMap: boolean, + +isolateModuleIDs: boolean, + minify: boolean, + onProgress: ?(doneCont: number, totalCount: number) => mixed, + +platform: ?string, + +resolutionResponse: ?{}, + +runBeforeMainModule: Array, + +runModule: boolean, + sourceMapUrl: ?string, + unbundle: boolean, +}; const dependencyOpts = declareOpts({ platform: { @@ -302,12 +256,8 @@ class Server { } } - async buildBundle(options: {entryFile: string, platform?: string}): Promise { - if (!options.platform) { - options.platform = getPlatformExtension(options.entryFile); - } - const opts = bundleOpts(options); - const bundle = await this._bundler.bundle(opts); + async buildBundle(options: BundleOptions): Promise { + const bundle = await this._bundler.bundle(options); const modules = bundle.getModules(); const nonVirtual = modules.filter(m => !m.virtual); bundleDeps.set(bundle, { @@ -359,7 +309,7 @@ class Server { getDependencies(options: { entryFile: string, - platform?: string, + platform: ?string, }): Promise { return Promise.resolve().then(() => { if (!options.platform) { @@ -568,10 +518,7 @@ class Server { }); } - _useCachedOrUpdateOrCreateBundle(options: { - entryFile: string, - platform?: string, - }): Promise { + _useCachedOrUpdateOrCreateBundle(options: BundleOptions): Promise { const optionsJson = this.optionsHash(options); const bundleFromScratch = () => { const building = this.buildBundle(options); @@ -597,8 +544,7 @@ class Server { // $FlowFixMe(>=0.37.0) deps.outdated = new Set(); - const opts = bundleOpts(options); - const {platform, dev, minify, hot} = opts; + const {platform, dev, minify, hot} = options; // Need to create a resolution response to pass to the bundler // to process requires after transform. By providing a @@ -880,20 +826,7 @@ class Server { } } - _getOptionsFromUrl(reqUrl: string): { - sourceMapUrl: string, - entryFile: string, - dev: boolean, - minify: boolean, - hot: boolean, - runModule: boolean, - inlineSourceMap: boolean, - platform?: string, - entryModuleOnly: boolean, - generateSourceMaps: boolean, - assetPlugins: Array, - onProgress?: (doneCont: number, totalCount: number) => mixed, - } { + _getOptionsFromUrl(reqUrl: string): BundleOptions { // `true` to parse the query param as an object. const urlObj = url.parse(reqUrl, true); @@ -934,13 +867,16 @@ class Server { dev, minify, hot: this._getBoolOptionFromQuery(urlObj.query, 'hot', false), + runBeforeMainModule: defaults.runBeforeMainModule, runModule: this._getBoolOptionFromQuery(urlObj.query, 'runModule', true), inlineSourceMap: this._getBoolOptionFromQuery( urlObj.query, 'inlineSourceMap', false ), + isolateModuleIDs: false, platform, + resolutionResponse: null, entryModuleOnly: this._getBoolOptionFromQuery( urlObj.query, 'entryModuleOnly', @@ -949,6 +885,8 @@ class Server { generateSourceMaps: minify || !dev || this._getBoolOptionFromQuery(urlObj.query, 'babelSourcemap', false), assetPlugins, + onProgress: null, + unbundle: false, }; } @@ -960,8 +898,28 @@ class Server { return query[opt] === 'true' || query[opt] === '1'; } + + static DEFAULT_BUNDLE_OPTIONS; + } +Server.DEFAULT_BUNDLE_OPTIONS = { + assetPlugins: [], + dev: true, + entryModuleOnly: false, + generateSourceMaps: false, + hot: false, + inlineSourceMap: false, + isolateModuleIDs: false, + minify: false, + onProgress: null, + resolutionResponse: null, + runBeforeMainModule: defaults.runBeforeMainModule, + runModule: true, + sourceMapUrl: null, + unbundle: false, +}; + function contentsEqual(array: Array, set: Set): boolean { return array.length === set.size && array.every(set.has, set); } From 0a7427f5990fc501932a8086591e445a036b6e66 Mon Sep 17 00:00:00 2001 From: Jonathan Keljo Date: Tue, 14 Mar 2017 11:07:54 -0700 Subject: [PATCH 017/366] Migrate ReactAndroid to use Buck's new java_annotation_processor rule Reviewed By: asp2insp Differential Revision: D4654756 fbshipit-source-id: d98d55786d84cf02234699a260e8750305982267 --- ReactAndroid/DEFS | 22 +++++-------------- .../com/facebook/react/module/processing/BUCK | 16 ++++++++++---- .../java/com/facebook/react/processing/BUCK | 16 ++++++++++---- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/ReactAndroid/DEFS b/ReactAndroid/DEFS index 632167ab730677..d54f4a601d2191 100644 --- a/ReactAndroid/DEFS +++ b/ReactAndroid/DEFS @@ -46,38 +46,28 @@ original_android_library=android_library def android_library( name, deps=[], - annotation_processors=[], - annotation_processor_deps=[], + plugins=[] *args, **kwargs): if react_native_target('java/com/facebook/react/uimanager/annotations:annotations') in deps and name != 'processing': - react_property_processors = [ - 'com.facebook.react.processing.ReactPropertyProcessor', - ] - react_property_processor_deps = [ + react_property_plugins = [ react_native_target('java/com/facebook/react/processing:processing'), ] - annotation_processors = list(set(annotation_processors + react_property_processors)) - annotation_processor_deps = list(set(annotation_processor_deps + react_property_processor_deps)) + plugins = list(set(plugins + react_property_plugins)) if react_native_target('java/com/facebook/react/module/annotations:annotations') in deps and name != 'processing': - react_module_processors = [ - 'com.facebook.react.module.processing.ReactModuleSpecProcessor', - ] - react_module_processor_deps = [ + react_module_plugins = [ react_native_target('java/com/facebook/react/module/processing:processing'), ] - annotation_processors = list(set(annotation_processors + react_module_processors)) - annotation_processor_deps = list(set(annotation_processor_deps + react_module_processor_deps)) + plugins = list(set(plugins + react_module_plugins)) original_android_library( name=name, deps=deps, - annotation_processors=annotation_processors, - annotation_processor_deps=annotation_processor_deps, + plugins=plugins, *args, **kwargs) diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK b/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK index bed89ebebe269a..92ee442d7d78de 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK @@ -1,13 +1,21 @@ include_defs("//ReactAndroid/DEFS") -java_library( +java_annotation_processor( name = "processing", - srcs = glob(["*.java"]), - source = "7", - target = "7", + processor_class = "com.facebook.react.module.processing.ReactModuleSpecProcessor", visibility = [ "PUBLIC", ], + deps = [ + ":processing-lib", + ], +) + +java_library( + name = "processing-lib", + srcs = glob(["*.java"]), + source = "7", + target = "7", deps = [ react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/javapoet:javapoet"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK b/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK index b2d3f6dba2a077..7f7c396dd8f79d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK @@ -1,13 +1,21 @@ include_defs("//ReactAndroid/DEFS") -java_library( +java_annotation_processor( name = "processing", - srcs = glob(["*.java"]), - source = "7", - target = "7", + processor_class = "com.facebook.react.processing.ReactPropertyProcessor", visibility = [ "PUBLIC", ], + deps = [ + ":processing-lib", + ], +) + +java_library( + name = "processing-lib", + srcs = glob(["*.java"]), + source = "7", + target = "7", deps = [ react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/javapoet:javapoet"), From 4ac585b34bc1d15fd5b528007cee67c38499133c Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 14 Mar 2017 11:19:35 -0700 Subject: [PATCH 018/366] packager: attachHMRServer.js: fix callsite of Server#getModuleForPath() Summary: In a previous changeset, Server#getModuleForPath() started returning `Promise` instead of `Module`, but the callsites in HMR haven't been updated, causing it to break. This would have been caught if `attachHMRServer.js` was using flow, that I'm considering doing in a following up diff. This would also have been caught if we had better integration testing of HMR. Good news however,it was caught by the OSS e2e test, that covers Hot Reloading. Reviewed By: davidaurelio Differential Revision: D4705937 fbshipit-source-id: fe787bc6ae50024759c7f7aeed747394fdce9aa1 --- local-cli/server/util/attachHMRServer.js | 60 ++++++++++++------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/local-cli/server/util/attachHMRServer.js b/local-cli/server/util/attachHMRServer.js index 02373c43dcbfc9..9e1dd4241fd132 100644 --- a/local-cli/server/util/attachHMRServer.js +++ b/local-cli/server/util/attachHMRServer.js @@ -175,9 +175,9 @@ function attachHMRServer({httpServer, path, packagerServer}) { entryFile: filename, recursive: true, }).then(response => { - const module = packagerServer.getModuleForPath(filename); - - return response.copy({dependencies: [module]}); + return packagerServer.getModuleForPath(filename).then(module => { + return response.copy({dependencies: [module]}); + }); }); } @@ -195,32 +195,34 @@ function attachHMRServer({httpServer, path, packagerServer}) { return {}; } - // build list of modules for which we'll send HMR updates - const modulesToUpdate = [packagerServer.getModuleForPath(filename)]; - Object.keys(depsModulesCache).forEach(module => { - if (!client.dependenciesModulesCache[module]) { - modulesToUpdate.push(depsModulesCache[module]); - } - }); - - // Need to send modules to the client in an order it can - // process them: if a new dependency graph was uncovered - // because a new dependency was added, the file that was - // changed, which is the root of the dependency tree that - // will be sent, needs to be the last module that gets - // processed. Reversing the new modules makes sense - // because we get them through the resolver which returns - // a BFS ordered list. - modulesToUpdate.reverse(); - - // invalidate caches - client.dependenciesCache = depsCache; - client.dependenciesModulesCache = depsModulesCache; - client.shallowDependencies = shallowDeps; - client.inverseDependenciesCache = inverseDepsCache; - - return resolutionResponse.copy({ - dependencies: modulesToUpdate + return packagerServer.getModuleForPath(filename).then(moduleToUpdate => { + // build list of modules for which we'll send HMR updates + const modulesToUpdate = [moduleToUpdate]; + Object.keys(depsModulesCache).forEach(module => { + if (!client.dependenciesModulesCache[module]) { + modulesToUpdate.push(depsModulesCache[module]); + } + }); + + // Need to send modules to the client in an order it can + // process them: if a new dependency graph was uncovered + // because a new dependency was added, the file that was + // changed, which is the root of the dependency tree that + // will be sent, needs to be the last module that gets + // processed. Reversing the new modules makes sense + // because we get them through the resolver which returns + // a BFS ordered list. + modulesToUpdate.reverse(); + + // invalidate caches + client.dependenciesCache = depsCache; + client.dependenciesModulesCache = depsModulesCache; + client.shallowDependencies = shallowDeps; + client.inverseDependenciesCache = inverseDepsCache; + + return resolutionResponse.copy({ + dependencies: modulesToUpdate + }); }); }); }) From 3e528b10145a13896b6610bfa690aefb99e7dbbd Mon Sep 17 00:00:00 2001 From: Theo Yaung Date: Tue, 14 Mar 2017 11:40:20 -0700 Subject: [PATCH 019/366] Remove Inspector Logic (cannot work on iOS / System JSC) Reviewed By: javache Differential Revision: D4620530 fbshipit-source-id: 52abc6178b1ad1b52ba1b1825702c9c254a04520 --- ReactAndroid/src/main/jni/xreact/jni/BUCK | 1 - .../src/main/jni/xreact/jni/JInspector.cpp | 4 + .../src/main/jni/xreact/jni/JInspector.h | 4 + ReactCommon/cxxreact/BUCK | 3 +- ReactCommon/inspector/Agent.cpp | 50 ----- ReactCommon/inspector/Agent.h | 33 --- ReactCommon/inspector/Android.mk | 28 --- ReactCommon/inspector/BUCK | 60 ------ ReactCommon/inspector/ConsoleAgent.cpp | 152 -------------- ReactCommon/inspector/ConsoleAgent.h | 43 ---- ReactCommon/inspector/Dispatcher.cpp | 3 - ReactCommon/inspector/Dispatcher.h | 41 ---- ReactCommon/inspector/Error.cpp | 19 -- ReactCommon/inspector/Error.h | 34 ---- ReactCommon/inspector/Inspector.cpp | 162 --------------- ReactCommon/inspector/Inspector.h | 94 --------- ReactCommon/inspector/InspectorAgent.cpp | 22 -- ReactCommon/inspector/InspectorAgent.h | 22 -- ReactCommon/inspector/InspectorController.cpp | 192 ------------------ ReactCommon/inspector/InspectorController.h | 42 ---- ReactCommon/inspector/JSDispatcher.cpp | 135 ------------ ReactCommon/inspector/JSDispatcher.h | 46 ----- ReactCommon/inspector/LegacyAgents.cpp | 35 ---- ReactCommon/inspector/LegacyAgents.h | 32 --- ReactCommon/inspector/LegacyDebuggerAgent.cpp | 44 ---- ReactCommon/inspector/LegacyDebuggerAgent.h | 40 ---- ReactCommon/inspector/LegacyDispatcher.cpp | 54 ----- ReactCommon/inspector/LegacyDispatcher.h | 52 ----- .../inspector/LegacyInspectorEnvironment.cpp | 34 ---- .../inspector/LegacyInspectorEnvironment.h | 35 ---- ReactCommon/inspector/LegacyRuntimeAgent.cpp | 63 ------ ReactCommon/inspector/LegacyRuntimeAgent.h | 39 ---- .../inspector/LegacyScriptDebugServer.cpp | 64 ------ .../inspector/LegacyScriptDebugServer.h | 45 ---- ReactCommon/inspector/PageAgent.cpp | 31 --- ReactCommon/inspector/PageAgent.h | 20 -- ReactCommon/inspector/Protocol.cpp | 86 -------- ReactCommon/inspector/Protocol.h | 77 ------- ReactCommon/inspector/README.md | 17 -- ReactCommon/inspector/Util.cpp | 9 - ReactCommon/inspector/Util.h | 45 ---- 41 files changed, 9 insertions(+), 2003 deletions(-) delete mode 100644 ReactCommon/inspector/Agent.cpp delete mode 100644 ReactCommon/inspector/Agent.h delete mode 100644 ReactCommon/inspector/Android.mk delete mode 100644 ReactCommon/inspector/BUCK delete mode 100644 ReactCommon/inspector/ConsoleAgent.cpp delete mode 100644 ReactCommon/inspector/ConsoleAgent.h delete mode 100644 ReactCommon/inspector/Dispatcher.cpp delete mode 100644 ReactCommon/inspector/Dispatcher.h delete mode 100644 ReactCommon/inspector/Error.cpp delete mode 100644 ReactCommon/inspector/Error.h delete mode 100644 ReactCommon/inspector/Inspector.cpp delete mode 100644 ReactCommon/inspector/Inspector.h delete mode 100644 ReactCommon/inspector/InspectorAgent.cpp delete mode 100644 ReactCommon/inspector/InspectorAgent.h delete mode 100644 ReactCommon/inspector/InspectorController.cpp delete mode 100644 ReactCommon/inspector/InspectorController.h delete mode 100644 ReactCommon/inspector/JSDispatcher.cpp delete mode 100644 ReactCommon/inspector/JSDispatcher.h delete mode 100644 ReactCommon/inspector/LegacyAgents.cpp delete mode 100644 ReactCommon/inspector/LegacyAgents.h delete mode 100644 ReactCommon/inspector/LegacyDebuggerAgent.cpp delete mode 100644 ReactCommon/inspector/LegacyDebuggerAgent.h delete mode 100644 ReactCommon/inspector/LegacyDispatcher.cpp delete mode 100644 ReactCommon/inspector/LegacyDispatcher.h delete mode 100644 ReactCommon/inspector/LegacyInspectorEnvironment.cpp delete mode 100644 ReactCommon/inspector/LegacyInspectorEnvironment.h delete mode 100644 ReactCommon/inspector/LegacyRuntimeAgent.cpp delete mode 100644 ReactCommon/inspector/LegacyRuntimeAgent.h delete mode 100644 ReactCommon/inspector/LegacyScriptDebugServer.cpp delete mode 100644 ReactCommon/inspector/LegacyScriptDebugServer.h delete mode 100644 ReactCommon/inspector/PageAgent.cpp delete mode 100644 ReactCommon/inspector/PageAgent.h delete mode 100644 ReactCommon/inspector/Protocol.cpp delete mode 100644 ReactCommon/inspector/Protocol.h delete mode 100644 ReactCommon/inspector/README.md delete mode 100644 ReactCommon/inspector/Util.cpp delete mode 100644 ReactCommon/inspector/Util.h diff --git a/ReactAndroid/src/main/jni/xreact/jni/BUCK b/ReactAndroid/src/main/jni/xreact/jni/BUCK index 6c2be6899b28a4..ef06da174c223b 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/BUCK +++ b/ReactAndroid/src/main/jni/xreact/jni/BUCK @@ -51,6 +51,5 @@ cxx_library( "//xplat/fbsystrace:fbsystrace", react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("cxxreact:module"), - react_native_xplat_target("inspector:inspector"), ], ) diff --git a/ReactAndroid/src/main/jni/xreact/jni/JInspector.cpp b/ReactAndroid/src/main/jni/xreact/jni/JInspector.cpp index 4146fc0fd095cd..cad09b1cc00562 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/JInspector.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/JInspector.cpp @@ -2,6 +2,8 @@ #include "JInspector.h" +#ifdef WITH_INSPECTOR + namespace facebook { namespace react { @@ -88,3 +90,5 @@ void JInspector::registerNatives() { } } + +#endif diff --git a/ReactAndroid/src/main/jni/xreact/jni/JInspector.h b/ReactAndroid/src/main/jni/xreact/jni/JInspector.h index 3335deccb2d9a9..0b9db509a75be8 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/JInspector.h +++ b/ReactAndroid/src/main/jni/xreact/jni/JInspector.h @@ -2,6 +2,8 @@ #pragma once +#ifdef WITH_INSPECTOR + #include #include @@ -59,3 +61,5 @@ class JInspector : public jni::HybridClass { } } + +#endif diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index da1ae79918b8e7..45496978a27648 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -27,8 +27,7 @@ if THIS_IS_FBANDROID: '-Wno-pessimizing-move', ], deps = [ - '//xplat/folly:molly', - react_native_xplat_target('inspector:inspector'), + '//xplat/folly:molly' ]) cxx_library( diff --git a/ReactCommon/inspector/Agent.cpp b/ReactCommon/inspector/Agent.cpp deleted file mode 100644 index 1a3720cd6a9a52..00000000000000 --- a/ReactCommon/inspector/Agent.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "Agent.h" - -#include "Error.h" -#include "Protocol.h" - -#include - -namespace facebook { -namespace react { - -void Agent::onConnect(std::shared_ptr channel) { - channel_ = std::move(channel); - - channel_->registerDomain(getDomain(), [this](std::string, int callId, const std::string& method, folly::dynamic args) { - auto result = handle(method, std::move(args)); - if (result.isNull()) { - result = folly::dynamic::object; - } - auto message = folly::dynamic::object("id", callId)("result", std::move(result)); - channel_->sendMessage(folly::toJson(std::move(message))); - }); -} - -void Agent::onDisconnect() { - channel_.reset(); -} - -folly::dynamic Agent::handle(const std::string& method, folly::dynamic args) { - try { - return methods_.at(method)(std::move(args)); - } catch (const std::out_of_range& e) { - throw InspectorException(ErrorCode::MethodNotFound, "Unknown method: '" + method + "'"); - } -} - -void Agent::registerMethod(std::string name, Method method) { - methods_.emplace(std::move(name), std::move(method)); -} - -void Agent::sendEvent(std::string name, folly::dynamic params) { - if (!channel_) { - return; - } - channel_->sendMessage(Event(getDomain(), std::move(name), std::move(params))); -} - -} -} diff --git a/ReactCommon/inspector/Agent.h b/ReactCommon/inspector/Agent.h deleted file mode 100644 index 94443231f768df..00000000000000 --- a/ReactCommon/inspector/Agent.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Dispatcher.h" - -#include - -namespace facebook { -namespace react { - -/* - * An dispatcher that makes it simple to implement an agent that serves a single domain. - */ -class Agent : public Dispatcher { -public: - void onConnect(std::shared_ptr channel) override; - void onDisconnect() override; -protected: - using Method = std::function; - void registerMethod(std::string name, Method method); - void sendEvent(std::string name, folly::dynamic params = nullptr); - - virtual std::string getDomain() = 0; -private: - folly::dynamic handle(const std::string& method, folly::dynamic args); - - std::shared_ptr channel_; - std::unordered_map methods_; -}; - -} -} diff --git a/ReactCommon/inspector/Android.mk b/ReactCommon/inspector/Android.mk deleted file mode 100644 index bd13be66e81362..00000000000000 --- a/ReactCommon/inspector/Android.mk +++ /dev/null @@ -1,28 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := inspector - -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) - -LOCAL_CFLAGS := \ - -DLOG_TAG=\"ReactNative\" - -LOCAL_CFLAGS += -Wall -Werror -fexceptions -frtti -CXX11_FLAGS := -std=c++11 -LOCAL_CFLAGS += $(CXX11_FLAGS) -LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS) - -LOCAL_STATIC_LIBRARIES := jschelpers -LOCAL_SHARED_LIBRARIES := libfolly_json libjsc libglog - -include $(BUILD_STATIC_LIBRARY) - -$(call import-module,folly) -$(call import-module,jsc) -$(call import-module,glog) -$(call import-module,jschelpers) diff --git a/ReactCommon/inspector/BUCK b/ReactCommon/inspector/BUCK deleted file mode 100644 index d98a369ec3a72c..00000000000000 --- a/ReactCommon/inspector/BUCK +++ /dev/null @@ -1,60 +0,0 @@ -EXPORTED_HEADERS = [ - "Inspector.h", -] - -def library(**kwargs): - if THIS_IS_FBANDROID: - include_defs('//ReactAndroid/DEFS') - - cxx_library( - force_static = True, - # We depend on JSC, support the same platforms - supported_platforms_regex = '^android-(armv7|x86)$', - deps = [ - '//xplat/folly:molly', - react_native_xplat_target('jschelpers:jschelpers'), - ], - **kwargs - ) - elif THIS_IS_FBOBJC: - ios_library( - inherited_buck_flags = STATIC_LIBRARY_IOS_FLAGS, - frameworks = [ - '$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework', - ], - deps = [ - '//xplat/folly:molly', - react_native_xplat_target('jschelpers:jschelpers'), - ], - **kwargs - ) - else: - raise Error('Unknown repo') - -library( - name = "inspector", - srcs = glob(["*.cpp"]), - compiler_flags = [ - "-Wall", - "-Wno-shadow", - "-Wno-inconsistent-missing-override", - "-Wno-unused-local-typedef", - "-Wno-unused-private-field", - "-Wno-undefined-bool-conversion", - "-fexceptions", - "-fvisibility=hidden", - "-std=gnu++1y", - ], - exported_headers = EXPORTED_HEADERS, - header_namespace = "inspector", - headers = subdir_glob( - [("inspector", "*.h")], - excludes = EXPORTED_HEADERS, - ), - preprocessor_flags = [ - "-DENABLE_INSPECTOR=1", - ], - visibility = [ - "PUBLIC", - ], -) diff --git a/ReactCommon/inspector/ConsoleAgent.cpp b/ReactCommon/inspector/ConsoleAgent.cpp deleted file mode 100644 index 665a1df45f60b3..00000000000000 --- a/ReactCommon/inspector/ConsoleAgent.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "ConsoleAgent.h" - -#include "Protocol.h" -#include "Util.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -namespace { - -static JSValueRef inspectorLog( - ConsoleAgent* agent, - JSContextRef ctx, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[]) { - CHECK(argumentCount == 4) << "__inspectorLog takes 4 args"; - auto execState = toJS(ctx); - JSC::JSLockHolder lock(execState); - auto params = toJS(execState, arguments[2]); - agent->log( - execState, - Value(ctx, arguments[0]).toString().str(), - Value(ctx, arguments[1]).toString().str(), - JSC::asArray(params), - Value(ctx, arguments[3]).asUnsignedInteger()); - return JSValueMakeUndefined(ctx); -} - -size_t skipNativeCode(const Inspector::ScriptCallStack& callStack, size_t offset) { - for (; offset < callStack.size(); offset++) { - auto& frame = callStack.at(offset); - if (frame.sourceURL() != "[native code]") { - return offset; - } - } - - return callStack.size(); -} - -const Inspector::ScriptCallFrame* firstUserFrame(const Inspector::ScriptCallStack& callStack, size_t framesToSkip) { - // Skip out of native code - size_t offset = skipNativeCode(callStack, 0); - - // Skip frames of console polyfill - offset = skipNativeCode(callStack, offset + framesToSkip); - if (offset >= callStack.size()) { - return nullptr; - } - - if (callStack.at(offset).functionName() == "infoLog") { - offset += 1; - } - - if (offset >= callStack.size()) { - return nullptr; - } - - return &callStack.at(offset); -} - -} - -ConsoleAgent::ConsoleAgent(JSC::JSGlobalObject& globalObject, Inspector::InjectedScriptManager* injectedScriptManager) - : globalObject_(globalObject) - , injectedScriptManager_(injectedScriptManager) { - registerMethod("enable", [this](folly::dynamic) -> folly::dynamic { - using namespace std::placeholders; - enabled_ = true; - JSGlobalContextRef context = toGlobalRef(globalObject_.globalExec()); - installGlobalFunction(context, "__inspectorLog", std::bind(&inspectorLog, this, _1, _2, _3, _4)); - return nullptr; - }); - registerMethod("disable", [this](folly::dynamic) -> folly::dynamic { - JSGlobalContextRef context = toGlobalRef(globalObject_.globalExec()); - removeGlobal(context, "__inspectorLog"); - enabled_ = false; - return nullptr; - }); -} - -void ConsoleAgent::log(JSC::ExecState* execState, std::string message) { - log(execState, "log", std::move(message), nullptr, 0); -} - -void ConsoleAgent::log(JSC::ExecState* execState, std::string level, std::string message, JSC::JSArray* params, size_t framesToSkip) { - if (!enabled_) { - return; - } - - auto callStack = Inspector::createScriptCallStack(execState, Inspector::ScriptCallStack::maxCallStackSizeToCapture); - - auto logEntry = folly::dynamic::object - ("source", "console-api") - ("level", level) - ("text", std::move(message)) - ("timestamp", Timestamp::now()) - ("stackTrace", folly::parseJson(toStdString(callStack->buildInspectorArray()->toJSONString()))); - - if (params) { - logEntry("parameters", convertParams(execState, params)); - } - - if (auto frame = firstUserFrame(*callStack, framesToSkip)) { - logEntry - ("url", toStdString(frame->sourceURL())) - ("line", frame->lineNumber()) - ("column", frame->columnNumber()); - } - - sendEvent("messageAdded", folly::dynamic::object("message", std::move(logEntry))); -} - -folly::dynamic ConsoleAgent::convertParams(JSC::ExecState* execState, JSC::JSArray* params) { - auto injectedScript = injectedScriptManager_->injectedScriptFor(execState->lexicalGlobalObject()->globalExec()); - if (injectedScript.hasNoValue()) { - return nullptr; - } - - folly::dynamic remoteParams = folly::dynamic::array; - for (size_t i = 0, size = params->length(); i < size; i++) { - auto scriptValue = Deprecated::ScriptValue(execState->vm(), params->getIndex(execState, i)); - auto remoteValue = injectedScript.wrapObject(std::move(scriptValue), "console", true); - remoteParams.push_back(folly::parseJson(toStdString(remoteValue->toJSONString()))); - } - - return remoteParams; -} - -} -} diff --git a/ReactCommon/inspector/ConsoleAgent.h b/ReactCommon/inspector/ConsoleAgent.h deleted file mode 100644 index 961271923bd674..00000000000000 --- a/ReactCommon/inspector/ConsoleAgent.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Agent.h" - -namespace JSC { -class JSGlobalObject; -class ExecState; -class JSArray; -} - -namespace Inspector { -class InjectedScriptManager; -} - -namespace facebook { -namespace react { - -/** - * Implements the Console agent. Relies on Javascript to call the globally exposed method __inspectorLog - * to send logging events. - */ -class ConsoleAgent : public Agent { -public: - ConsoleAgent(JSC::JSGlobalObject& globalObject, Inspector::InjectedScriptManager* injectedScriptManager); - - void log(JSC::ExecState* execState, std::string message); - void log(JSC::ExecState* execState, std::string level, std::string message, JSC::JSArray* params, size_t framesToSkip); -private: - bool enabled_{false}; - JSC::JSGlobalObject& globalObject_; - Inspector::InjectedScriptManager* injectedScriptManager_; - - folly::dynamic convertParams(JSC::ExecState* execState, JSC::JSArray* params); - - std::string getDomain() override { - return "Console"; - } -}; - -} -} diff --git a/ReactCommon/inspector/Dispatcher.cpp b/ReactCommon/inspector/Dispatcher.cpp deleted file mode 100644 index 54bbd03f2da076..00000000000000 --- a/ReactCommon/inspector/Dispatcher.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "Dispatcher.h" diff --git a/ReactCommon/inspector/Dispatcher.h b/ReactCommon/inspector/Dispatcher.h deleted file mode 100644 index c864bea0aaf389..00000000000000 --- a/ReactCommon/inspector/Dispatcher.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include - -#include -// Both double-conversions and WTF define the ASSERT macro -#undef ASSERT - -namespace facebook { -namespace react { - -/* - * A bidrectional channel that allows both sending events to the remote inspector and registering - * to receive events for a specific domain. - */ -class Channel { -public: - using MessageHandler = std::function; - - virtual ~Channel() = default; - - virtual void sendMessage(std::string message) = 0; - virtual void registerDomain(std::string domain, MessageHandler handler) = 0; -}; - -/* - * A dispatcher is responsible for one or multiple domains and registering them with the Channel - * when it is connected. - */ -class Dispatcher { -public: - virtual ~Dispatcher() {} - - virtual void onConnect(std::shared_ptr channel) = 0; - virtual void onDisconnect() = 0; -}; - -} -} diff --git a/ReactCommon/inspector/Error.cpp b/ReactCommon/inspector/Error.cpp deleted file mode 100644 index 5bb780f4471e1b..00000000000000 --- a/ReactCommon/inspector/Error.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "Error.h" - -namespace facebook { -namespace react { - -InspectorException::InspectorException(int callId, ErrorCode code, std::string message) - : error_(callId, code, std::move(message)) {} - -InspectorException::InspectorException(ErrorCode code, std::string message) - : error_(code, std::move(message)) {} - -InspectorException InspectorException::withCallId(int callId) const { - return InspectorException(callId, error_.code(), error_.message()); -} - -} -} diff --git a/ReactCommon/inspector/Error.h b/ReactCommon/inspector/Error.h deleted file mode 100644 index f373035a321a32..00000000000000 --- a/ReactCommon/inspector/Error.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Protocol.h" - -#include -#include - -#include - -namespace facebook { -namespace react { - -class InspectorException : public std::exception { -public: - InspectorException(int callId, ErrorCode code, std::string message); - explicit InspectorException(ErrorCode code, std::string message); - - const char* what() const throw() override { - return error_.message().c_str(); - } - - const Error& error() const { - return error_; - } - - InspectorException withCallId(int callId) const; -private: - Error error_; -}; - -} -} diff --git a/ReactCommon/inspector/Inspector.cpp b/ReactCommon/inspector/Inspector.cpp deleted file mode 100644 index 68233b15e71627..00000000000000 --- a/ReactCommon/inspector/Inspector.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "Inspector.h" - -#include "InspectorController.h" - -#include - -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -namespace { - -JSC::JSGlobalObject& getGlobalObject(JSContextRef ctx) { - JSC::ExecState* exec = toJS(ctx); - JSC::JSLockHolder locker(exec); - - JSC::JSGlobalObject* globalObject = exec->vmEntryGlobalObject(); - return *globalObject; -} - -} - -Inspector::LocalConnection::LocalConnection(std::shared_ptr duplexConnection) - : duplexConnection_(std::move(duplexConnection)) {} - -void Inspector::LocalConnection::sendMessage(std::string message) { - duplexConnection_->sendToLocal(std::move(message)); -} - -void Inspector::LocalConnection::disconnect() { - duplexConnection_->terminate(false); -} - -Inspector::PageHolder::PageHolder(std::string name, std::unique_ptr controller) -: name(name) -, controller(std::move(controller)) {} - -Inspector::PageHolder::~PageHolder() = default; - -Inspector& Inspector::instance() { - static Inspector inspector; - return inspector; -} - -std::vector Inspector::getPages() const { - std::lock_guard lock(registrationMutex_); - std::vector pages; - pages.reserve(pages_.size()); - for (auto& entry : pages_) { - pages.emplace_back(Page{entry.first, entry.second.name}); - } - return pages; -} - -void Inspector::registerGlobalContext(std::string title, JSGlobalContextRef ctx) { - std::lock_guard lock(registrationMutex_); - auto controller = folly::make_unique(getGlobalObject(ctx)); - auto pageId = numPages_++; - pages_.emplace( - std::piecewise_construct, - std::forward_as_tuple(pageId), - std::forward_as_tuple(std::move(title), std::move(controller))); -} - -void Inspector::unregisterGlobalContext(JSGlobalContextRef ctx) { - std::lock_guard lock(registrationMutex_); - auto& globalObject = getGlobalObject(ctx); - for (auto it = pages_.begin(); it != pages_.end(); it++) { - auto& page = it->second; - if (page.controller->getGlobalObject().globalExec() == globalObject.globalExec()) { - if (page.connection_) { - page.connection_->terminate(true); - } - pages_.erase(it); - return; - } - } -} - -std::unique_ptr Inspector::connect(int pageId, std::unique_ptr remote) { - std::lock_guard lock(registrationMutex_); - return folly::make_unique(pages_.at(pageId).connect(std::move(remote))); -} - -void Inspector::disconnect(int pageId) { - std::lock_guard lock(registrationMutex_); - pages_.at(pageId).controller->onDisconnect(); -} - -std::shared_ptr Inspector::PageHolder::connect(std::unique_ptr remote) { - if (connection_) { - throw std::runtime_error("Already connected"); - } - connection_ = std::make_shared(*this, std::move(remote)); - controller->onConnect([connection = connection_](std::string message) { - connection->sendToRemote(std::move(message)); - }); - return connection_; -} - -Inspector::DuplexConnection::DuplexConnection(PageHolder& page, std::unique_ptr remoteConnection) - : page_(page) - , remoteConnection_(std::move(remoteConnection)) {} - -Inspector::DuplexConnection::~DuplexConnection() { - if (remoteConnection_) { - LOG(FATAL) << "DuplexConnection wasn't terminated before destruction"; - } -} - -void Inspector::DuplexConnection::sendToRemote(std::string message) { - std::lock_guard lock(remoteMutex_); - if (!remoteConnection_) { - return; - } - - remoteConnection_->onMessage(std::move(message)); -} - -void Inspector::DuplexConnection::sendToLocal(std::string message) { - std::lock_guard lock(localMutex_); - if (!remoteConnection_) { - return; - } - - page_.controller->onMessage(std::move(message)); -} - -void Inspector::DuplexConnection::terminate(bool local) { - std::lock_guard lockLocal(localMutex_); - { - // Temp lock here so we can still send going away message - std::lock_guard lockRemote(remoteMutex_); - if (!remoteConnection_) { - // Already disconnected - return; - } - } - - if (local) { - page_.controller->onGoingAway(); - } - std::lock_guard lockRemote(remoteMutex_); - - auto remoteConnection = std::move(remoteConnection_); - if (local) { - remoteConnection->onDisconnect(); - } - page_.controller->onDisconnect(); - // This kills us - page_.connection_.reset(); -} - -} -} diff --git a/ReactCommon/inspector/Inspector.h b/ReactCommon/inspector/Inspector.h deleted file mode 100644 index 0f4177b53d6db1..00000000000000 --- a/ReactCommon/inspector/Inspector.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include -#include -#include -#include -#include - -#include -#undef WTF_EXPORT_PRIVATE - -namespace facebook { -namespace react { - -class InspectorController; -class Sender; -/** - * The inspector exposes method to query for available 'pages' and connect to a specific one. - * Available Javascript contextes needs to be registered when they are created and removed when - * they are torn down. - */ -class Inspector { -private: - class DuplexConnection; -public: - struct Page { - const int id; - const std::string title; - }; - - struct RemoteConnection { - virtual ~RemoteConnection() = default; - virtual void onMessage(std::string message) = 0; - virtual void onDisconnect() = 0; - }; - - class LocalConnection { - public: - void sendMessage(std::string message); - void disconnect(); - - LocalConnection(std::shared_ptr duplexConnection); - private: - std::shared_ptr duplexConnection_; - }; - - static Inspector& instance(); - - void registerGlobalContext(std::string title, JSGlobalContextRef ctx); - void unregisterGlobalContext(JSGlobalContextRef ctx); - - std::vector getPages() const; - std::unique_ptr connect(int pageId, std::unique_ptr remote); -private: - struct PageHolder; - - class DuplexConnection { - public: - DuplexConnection(PageHolder& page, std::unique_ptr remoteConnection); - ~DuplexConnection(); - - void sendToRemote(std::string message); - void sendToLocal(std::string message); - void terminate(bool local); - private: - PageHolder& page_; - std::unique_ptr remoteConnection_; - std::mutex localMutex_; - std::mutex remoteMutex_; - }; - - struct PageHolder { - PageHolder(std::string name, std::unique_ptr controller); - ~PageHolder(); - - std::shared_ptr connect(std::unique_ptr remote); - - const std::string name; - std::unique_ptr controller; - std::shared_ptr connection_; - }; - - Inspector() {}; - void disconnect(int pageId); - - int numPages_ = 0; - std::unordered_map pages_; - mutable std::mutex registrationMutex_; -}; - -} -} diff --git a/ReactCommon/inspector/InspectorAgent.cpp b/ReactCommon/inspector/InspectorAgent.cpp deleted file mode 100644 index d94176059ae779..00000000000000 --- a/ReactCommon/inspector/InspectorAgent.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "InspectorAgent.h" - -namespace facebook { -namespace react { - -InspectorAgent::InspectorAgent() { - auto emptyMethod = [](folly::dynamic) -> folly::dynamic { - return nullptr; - }; - - registerMethod("enable", emptyMethod); - registerMethod("disable", emptyMethod); -} - -void InspectorAgent::detach() { - sendEvent("detached", folly::dynamic::object("reason", "target_closed")); -} - -} -} diff --git a/ReactCommon/inspector/InspectorAgent.h b/ReactCommon/inspector/InspectorAgent.h deleted file mode 100644 index 569e73f79d5104..00000000000000 --- a/ReactCommon/inspector/InspectorAgent.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Agent.h" - -namespace facebook { -namespace react { - -class InspectorAgent : public Agent { -public: - InspectorAgent(); - - void detach(); -private: - std::string getDomain() override { - return "Inspector"; - } -}; - -} -} diff --git a/ReactCommon/inspector/InspectorController.cpp b/ReactCommon/inspector/InspectorController.cpp deleted file mode 100644 index d04525cdd70fa6..00000000000000 --- a/ReactCommon/inspector/InspectorController.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "InspectorController.h" - -#include "Error.h" -#include "Agent.h" -#include "LegacyInspectorEnvironment.h" -#include "InspectorAgent.h" -#include "PageAgent.h" -#include "ConsoleAgent.h" -#include "JSDispatcher.h" -#include "LegacyAgents.h" - -#include -#include -#include - -namespace facebook { -namespace react { - -class ConcreteChannel : public Channel { -public: - ConcreteChannel(Receiver receiver) - : receiver_(std::move(receiver)) {} - - void sendMessage(std::string message) override { - receiver_(std::move(message)); - } - - void registerDomain(std::string domain, MessageHandler handler) override { - domains_.emplace(std::move(domain), std::move(handler)); - } - - std::unordered_map& getDomains() { - return domains_; - } -private: - std::unordered_map domains_; - Receiver receiver_; -}; - -class MessageRouter { -public: - MessageRouter(ConcreteChannel* channel) - : channel_(channel) { - CHECK(channel_) << "Channel is null"; - } - - /* - * Messages are in JSON, formatted like: - * { - * "id": 1, - * "method": "Debugger.removeBreakpoint", - * "params": { "removeBreakpoint": "xyz" } - * } - */ - void route(std::string message) { - try { - auto json = parseJson(message); - auto callId = getCallId(json); - receive(callId, std::move(message), std::move(json)); - } catch (const InspectorException& e) { - channel_->sendMessage(e.error()); - } - } -private: - void receive(int callId, std::string message, folly::dynamic json) { - try { - auto method = Method::parse(json["method"].asString()); - auto& handler = getHandler(method.domain()); - handler(std::move(message), callId, method.name(), std::move(json["params"])); - } catch (const InspectorException& e) { - throw e.withCallId(callId); - } catch (const std::exception& e) { - LOG(ERROR) << "Dispatcher failed: " << e.what(); - throw InspectorException(callId, ErrorCode::ServerError, "Internal error"); - } catch (...) { - throw InspectorException(callId, ErrorCode::ServerError, "Internal error"); - } - } - - folly::dynamic parseJson(const std::string& message) { - try { - return folly::parseJson(message); - } catch (const std::runtime_error& e) { - throw InspectorException(ErrorCode::ParseError, "Message must be in JSON format"); - } - } - - int getCallId(folly::dynamic& json) { - auto& id = json["id"]; - if (!id.isInt()) { - throw InspectorException(ErrorCode::InvalidRequest, "The type of 'id' property must be number"); - } else { - return id.asInt(); - } - } - - Channel::MessageHandler& getHandler(const std::string& domain) { - try { - auto& domains = channel_->getDomains(); - return domains.at(domain); - } catch (const std::out_of_range& e) { - throw InspectorException(ErrorCode::MethodNotFound, folly::to("Unknown domain: '", domain, "'")); - } - } - - ConcreteChannel* channel_; -}; - -class SchemaAgent : public Agent { -public: - SchemaAgent() { - registerMethod("getDomains", [this](folly::dynamic) -> folly::dynamic { - CHECK(channel_) << "Channel is null"; - folly::dynamic names = folly::dynamic::array; - auto& domains = channel_->getDomains(); - for (auto& entry : domains) { - // TODO(blom): Actually get version? - names.push_back(folly::dynamic::object("name", entry.first)("version", "1.0")); - } - return names; - }); - } - - void onConnect(std::shared_ptr channel) override { - Agent::onConnect(channel); - channel_ = std::static_pointer_cast(channel); - } -private: - std::shared_ptr channel_; - - std::string getDomain() override { - return "Schema"; - } -}; - -InspectorController::InspectorController(JSC::JSGlobalObject& globalObject) - : globalObject_(globalObject) { - auto environment = folly::make_unique(); - auto inspectorAgent = folly::make_unique(); - inspectorAgent_ = inspectorAgent.get(); - dispatchers_.push_back(std::move(inspectorAgent)); - dispatchers_.push_back(folly::make_unique()); - dispatchers_.push_back(folly::make_unique()); - dispatchers_.push_back(folly::make_unique(globalObject)); - - auto consoleAgent = folly::make_unique(globalObject, environment->injectedScriptManager()); - auto legacyAgents = folly::make_unique(globalObject, std::move(environment), consoleAgent.get()); - - dispatchers_.push_back(std::move(consoleAgent)); - dispatchers_.push_back(std::move(legacyAgents)); -} - -InspectorController::~InspectorController() { - CHECK(!channel_) << "Wasn't disconnected"; -} - -void InspectorController::onConnect(Receiver receiver) { - CHECK(!channel_) << "Already connected"; - - channel_ = std::make_shared(std::move(receiver)); - - for (auto& dispatcher : dispatchers_) { - dispatcher->onConnect(channel_); - } -} - -void InspectorController::onMessage(std::string message) { - CHECK(channel_) << "Not connected"; - - MessageRouter(channel_.get()).route(message); -} - -void InspectorController::onGoingAway() { - CHECK(channel_) << "Not connected"; - - inspectorAgent_->detach(); -} - -void InspectorController::onDisconnect() { - CHECK(channel_) << "Not connected"; - - for (auto& dispatcher : dispatchers_) { - dispatcher->onDisconnect(); - } - - channel_.reset(); -} - -} -} diff --git a/ReactCommon/inspector/InspectorController.h b/ReactCommon/inspector/InspectorController.h deleted file mode 100644 index 2281e9929588c2..00000000000000 --- a/ReactCommon/inspector/InspectorController.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Dispatcher.h" - -#include -#include -#include - -namespace JSC { -class JSGlobalObject; -} - -namespace facebook { -namespace react { - -class ConcreteChannel; -class InspectorAgent; - -using Receiver = std::function; - -class InspectorController { -public: - InspectorController(JSC::JSGlobalObject& globalObject); - ~InspectorController(); - - JSC::JSGlobalObject& getGlobalObject() const { return globalObject_; } - - void onConnect(Receiver receiver); - void onMessage(std::string message); - void onGoingAway(); - void onDisconnect(); -private: - JSC::JSGlobalObject& globalObject_; - std::shared_ptr channel_; - std::vector> dispatchers_; - InspectorAgent* inspectorAgent_; -}; - -} -} diff --git a/ReactCommon/inspector/JSDispatcher.cpp b/ReactCommon/inspector/JSDispatcher.cpp deleted file mode 100644 index 2920626dbdbfd7..00000000000000 --- a/ReactCommon/inspector/JSDispatcher.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "JSDispatcher.h" - -#include "Protocol.h" -#include "Util.h" - -#include - -#include -#include -#include -#include - -#include - -#include - -namespace facebook { -namespace react { - -namespace { - -static JSValueRef nativeRegisterAgent( - JSDispatcher* agent, - JSContextRef ctx, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[]) { - CHECK(argumentCount == 1) << "__registerInspectorAgent takes 1 arg"; - auto execState = toJS(ctx); - JSC::JSLockHolder lock(execState); - auto globalContext = JSContextGetGlobalContext(ctx); - agent->addAgent( - execState, - Value(globalContext, arguments[0]).asObject()); - return JSValueMakeUndefined(ctx); -} - -static JSValueRef nativeSendEvent( - JSDispatcher* agent, - const std::string& domain, - JSContextRef ctx, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[]) { - CHECK(argumentCount == 2) << "sendEvent takes 2 args"; - auto execState = toJS(ctx); - JSC::JSLockHolder lock(execState); - auto globalContext = JSContextGetGlobalContext(ctx); - auto params = folly::parseJson(Value(globalContext, arguments[1]).toJSONString()); - agent->sendEvent( - domain, - Value(globalContext, arguments[0]).toString().str(), - std::move(params)); - return JSValueMakeUndefined(ctx); -} - -static JSValueRef nativeInspectorTimestamp( - JSContextRef ctx, - JSObjectRef function, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[], - JSValueRef *exception) { - return JSValueMakeNumber(ctx, Timestamp::now()); -} - -} - -JSDispatcher::JSDispatcher(JSC::JSGlobalObject& globalObject) { - using namespace std::placeholders; - JSGlobalContextRef context = toGlobalRef(globalObject.globalExec()); - installGlobalFunction(context, "__registerInspectorAgent", std::bind(&nativeRegisterAgent, this, _1, _2, _3, _4)); - installGlobalFunction(context, "__inspectorTimestamp", &nativeInspectorTimestamp); -} - -void JSDispatcher::onConnect(std::shared_ptr channel) { - channel_ = std::move(channel); - - for (auto& pair : agents_) { - registerAgent(pair.first); - } -} - -void JSDispatcher::onDisconnect() { - channel_.reset(); -} - -void JSDispatcher::addAgent(JSC::ExecState* execState, Object agentType) { - auto context = agentType.context(); - auto domainObject = agentType.getProperty("DOMAIN"); - if (domainObject.isUndefined()) { - throw std::invalid_argument("DOMAIN should be string"); - } - auto domain = domainObject.toString().str(); - // Bind the domain to the send event function - using namespace std::placeholders; - Value sendEventFunction = Object( - context, - makeFunction(context, "sendEvent", std::bind(&nativeSendEvent, this, domain, _1, _2, _3, _4))); - auto agent = agentType.callAsConstructor({ sendEventFunction }); - agent.makeProtected(); - - if (channel_) { - registerAgent(domain); - } - agents_.emplace(std::move(domain), std::move(agent)); -} - -void JSDispatcher::registerAgent(const std::string& name) { - channel_->registerDomain( - name, - [this, name](std::string, int callId, const std::string& methodName, folly::dynamic args) { - Object& agent = agents_.at(name); - auto context = agent.context(); - JSC::JSLockHolder lock(toJS(context)); - // TODO(blom): Check undefined before asObject - auto method = agent.getProperty(methodName.c_str()).asObject(); - if (args.isNull()) { - args = folly::dynamic::object; - } - auto response = method.callAsFunction(agent, { Value::fromDynamic(context, args) }); - auto result = (response.isUndefined() || response.isNull()) ? folly::dynamic::object() : folly::parseJson(response.toJSONString()); - auto message = folly::dynamic::object("id", callId)("result", std::move(result)); - channel_->sendMessage(folly::toJson(std::move(message))); - }); -} - -void JSDispatcher::sendEvent(std::string domain, std::string name, folly::dynamic params) { - channel_->sendMessage(Event(std::move(domain), std::move(name), std::move(params))); -} - -} -} diff --git a/ReactCommon/inspector/JSDispatcher.h b/ReactCommon/inspector/JSDispatcher.h deleted file mode 100644 index b9330301e05678..00000000000000 --- a/ReactCommon/inspector/JSDispatcher.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Dispatcher.h" - -#include -#include - -#include - -#include - -namespace JSC { -class JSGlobalObject; -class JSObject; -class ExecState; -class JSArray; -} - -namespace facebook { -namespace react { - -/* - * A dispatcher that allows agents to be implemented in Javascript. Provides the global method - * __registerInspectorAgent to register a JS agent. - */ -class JSDispatcher : public Dispatcher { -public: - JSDispatcher(JSC::JSGlobalObject& globalObject); - - void onConnect(std::shared_ptr channel) override; - void onDisconnect() override; - - void addAgent(JSC::ExecState* execState, Object agentType); - void registerAgent(const std::string& name); - - void sendEvent(std::string domain, std::string name, folly::dynamic params); -private: - std::shared_ptr channel_; - - std::unordered_map agents_; -}; - -} -} diff --git a/ReactCommon/inspector/LegacyAgents.cpp b/ReactCommon/inspector/LegacyAgents.cpp deleted file mode 100644 index 2ccd2eb693018b..00000000000000 --- a/ReactCommon/inspector/LegacyAgents.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "LegacyAgents.h" - -#include "LegacyInspectorEnvironment.h" -#include "LegacyRuntimeAgent.h" -#include "LegacyDebuggerAgent.h" - -#include -#include -#include - -namespace facebook { -namespace react { - -using namespace Inspector; - -LegacyAgents::LegacyAgents( - JSC::JSGlobalObject& globalObject, - std::unique_ptr environment, - ConsoleAgent* consoleAgent) - : LegacyDispatcher(globalObject) - , environment_(std::move(environment)) { - auto injectedScriptManager = environment_->injectedScriptManager(); - auto runtimeAgent = folly::make_unique(injectedScriptManager, globalObject); - auto debuggerAgent = folly::make_unique(injectedScriptManager, globalObject, consoleAgent); - - runtimeAgent->setScriptDebugServer(&debuggerAgent->scriptDebugServer()); - - addAgent("Runtime", std::move(runtimeAgent)); - addAgent("Debugger", std::move(debuggerAgent)); -} - -} -} diff --git a/ReactCommon/inspector/LegacyAgents.h b/ReactCommon/inspector/LegacyAgents.h deleted file mode 100644 index 36650d7a8885cc..00000000000000 --- a/ReactCommon/inspector/LegacyAgents.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "LegacyDispatcher.h" - -namespace JSC { -class JSGlobalObject; -} - -namespace facebook { -namespace react { - -class LegacyInspectorEnvironment; -class ConsoleAgent; - -/* - * An dispatcher that provides the existing agents in JavaScriptCore. - */ -class LegacyAgents : public LegacyDispatcher { -public: - LegacyAgents( - JSC::JSGlobalObject& globalObject, - std::unique_ptr environment, - ConsoleAgent* consoleAgent); -private: - std::unique_ptr environment_; - ConsoleAgent* consoleAgent_; -}; - -} -} diff --git a/ReactCommon/inspector/LegacyDebuggerAgent.cpp b/ReactCommon/inspector/LegacyDebuggerAgent.cpp deleted file mode 100644 index 20b71a0dabf1c4..00000000000000 --- a/ReactCommon/inspector/LegacyDebuggerAgent.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "LegacyDebuggerAgent.h" - -#include "Util.h" - -#include -#include -#include - -namespace facebook { -namespace react { - -using namespace Inspector; - -LegacyDebuggerAgent::LegacyDebuggerAgent(InjectedScriptManager* injectedScriptManager, JSC::JSGlobalObject& globalObject, ConsoleAgent* consoleAgent) - : InspectorDebuggerAgent(injectedScriptManager) - , scriptDebugServer_(globalObject) - , consoleAgent_(consoleAgent) {} - -void LegacyDebuggerAgent::startListeningScriptDebugServer() { - scriptDebugServer().addListener(this); -} - -void LegacyDebuggerAgent::stopListeningScriptDebugServer(bool isBeingDestroyed) { - scriptDebugServer().removeListener(this, isBeingDestroyed); -} - -InjectedScript LegacyDebuggerAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) { - if (executionContextId) { - *error = ASCIILiteral("Execution context id is not supported for JSContext inspection as there is only one execution context."); - return InjectedScript(); - } - - JSC::ExecState* exec = scriptDebugServer_.globalObject().globalExec(); - return injectedScriptManager()->injectedScriptFor(exec); -} - -void LegacyDebuggerAgent::breakpointActionLog(JSC::ExecState* exec, const String& message) { - consoleAgent_->log(exec, toStdString(message)); -} - -} -} diff --git a/ReactCommon/inspector/LegacyDebuggerAgent.h b/ReactCommon/inspector/LegacyDebuggerAgent.h deleted file mode 100644 index 08bf34ce59e62a..00000000000000 --- a/ReactCommon/inspector/LegacyDebuggerAgent.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "ConsoleAgent.h" -#include "LegacyScriptDebugServer.h" - -#include -#include -#include - -namespace JSC { -class JSGlobalObject; -} - -namespace facebook { -namespace react { - -class LegacyDebuggerAgent : public Inspector::InspectorDebuggerAgent { -public: - LegacyDebuggerAgent(Inspector::InjectedScriptManager*, JSC::JSGlobalObject&, ConsoleAgent*); - - virtual LegacyScriptDebugServer& scriptDebugServer() override { return scriptDebugServer_; } - - virtual void startListeningScriptDebugServer() override; - virtual void stopListeningScriptDebugServer(bool isBeingDestroyed) override; - virtual Inspector::InjectedScript injectedScriptForEval(Inspector::ErrorString*, const int* executionContextId) override; - - virtual void breakpointActionLog(JSC::ExecState*, const String&) override; - - virtual void muteConsole() override { } - virtual void unmuteConsole() override { } - -private: - LegacyScriptDebugServer scriptDebugServer_; - ConsoleAgent* consoleAgent_; -}; - -} -} diff --git a/ReactCommon/inspector/LegacyDispatcher.cpp b/ReactCommon/inspector/LegacyDispatcher.cpp deleted file mode 100644 index 8fa7d7aebb602e..00000000000000 --- a/ReactCommon/inspector/LegacyDispatcher.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "LegacyDispatcher.h" - -#include "Util.h" - -#include -#include -#include - -namespace facebook { -namespace react { - -using namespace Inspector; - -LegacyDispatcher::FrontendChannel::FrontendChannel(std::shared_ptr channel) -: channel_(channel) {} - -bool LegacyDispatcher::FrontendChannel::sendMessageToFrontend(const WTF::String& message) { - channel_->sendMessage(toStdString(message)); - return true; -} - -LegacyDispatcher::LegacyDispatcher(JSC::JSGlobalObject& globalObject) - : globalObject_(globalObject) {} - -void LegacyDispatcher::addAgent(std::string domain, std::unique_ptr agent) { - domains_.emplace_back(std::move(domain)); - agents_.append(std::move(agent)); -} - -void LegacyDispatcher::onConnect(std::shared_ptr channel) { - // TODO: Should perhaps only create this once and then connect each time instead - frontendChannel_ = std::make_unique(channel); - dispatcher_.reset(InspectorBackendDispatcher::create(frontendChannel_.get()).leakRef()); - - auto messageHandler = [this](std::string message, int, const std::string&, folly::dynamic) { - JSC::JSLockHolder lock(globalObject_.globalExec()); - dispatcher_->dispatch(message.c_str()); - }; - for (auto& domain : domains_) { - channel->registerDomain(domain, messageHandler); - } - - agents_.didCreateFrontendAndBackend(frontendChannel_.get(), dispatcher_.get()); -} - -void LegacyDispatcher::onDisconnect() { - // TODO: Perhaps support InspectedTargetDestroyed - agents_.willDestroyFrontendAndBackend(InspectorDisconnectReason::InspectorDestroyed); -} - -} -} diff --git a/ReactCommon/inspector/LegacyDispatcher.h b/ReactCommon/inspector/LegacyDispatcher.h deleted file mode 100644 index 96e617d7394880..00000000000000 --- a/ReactCommon/inspector/LegacyDispatcher.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Dispatcher.h" - -#include -#include -#include -#include -#include - -#include -#include - -namespace JSC { -class JSGlobalObject; -} - -namespace facebook { -namespace react { - -/* - * An dispatcher that is able to register JavaScriptCore agents that extend the InspectorAgentBase - * base class. - */ -class LegacyDispatcher : public Dispatcher { -public: - LegacyDispatcher(JSC::JSGlobalObject& globalObject); - void addAgent(std::string domain, std::unique_ptr agent); - - void onConnect(std::shared_ptr channel) override; - void onDisconnect() override; -private: - class FrontendChannel : public Inspector::InspectorFrontendChannel { - public: - FrontendChannel(std::shared_ptr channel); - bool sendMessageToFrontend(const WTF::String& message) override; - private: - std::shared_ptr channel_; - }; - - JSC::JSGlobalObject& globalObject_; - std::vector domains_; - Inspector::InspectorAgentRegistry agents_; - - std::unique_ptr frontendChannel_; - std::unique_ptr dispatcher_; -}; - -} -} diff --git a/ReactCommon/inspector/LegacyInspectorEnvironment.cpp b/ReactCommon/inspector/LegacyInspectorEnvironment.cpp deleted file mode 100644 index 2a010d923a5a23..00000000000000 --- a/ReactCommon/inspector/LegacyInspectorEnvironment.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "LegacyInspectorEnvironment.h" - -#include -#include -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -using namespace Inspector; - -LegacyInspectorEnvironment::LegacyInspectorEnvironment() - : injectedScriptManager_(folly::make_unique(*this, InjectedScriptHost::create())) {} - -LegacyInspectorEnvironment::~LegacyInspectorEnvironment() { - injectedScriptManager_->disconnect(); -} - -InspectorFunctionCallHandler LegacyInspectorEnvironment::functionCallHandler() const { - return JSC::call; -} - -InspectorEvaluateHandler LegacyInspectorEnvironment::evaluateHandler() const { - return JSC::evaluate; -} - -} -} diff --git a/ReactCommon/inspector/LegacyInspectorEnvironment.h b/ReactCommon/inspector/LegacyInspectorEnvironment.h deleted file mode 100644 index 00ec902058c8ce..00000000000000 --- a/ReactCommon/inspector/LegacyInspectorEnvironment.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include -#include - -namespace Inspector { -class InjectedScriptManager; -} - -namespace facebook { -namespace react { - -class LegacyInspectorEnvironment : public Inspector::InspectorEnvironment { -public: - LegacyInspectorEnvironment(); - ~LegacyInspectorEnvironment(); - - Inspector::InjectedScriptManager* injectedScriptManager() const { - return injectedScriptManager_.get(); - } -private: - std::unique_ptr injectedScriptManager_; - - bool developerExtrasEnabled() const override { return true; } - bool canAccessInspectedScriptState(JSC::ExecState*) const override { return true; } - Inspector::InspectorFunctionCallHandler functionCallHandler() const override; - Inspector::InspectorEvaluateHandler evaluateHandler() const override; - void willCallInjectedScriptFunction(JSC::ExecState*, const WTF::String& scriptName, int scriptLine) override {}; - void didCallInjectedScriptFunction(JSC::ExecState*) override {} -}; - -} -} diff --git a/ReactCommon/inspector/LegacyRuntimeAgent.cpp b/ReactCommon/inspector/LegacyRuntimeAgent.cpp deleted file mode 100644 index f08a9d178aa4b6..00000000000000 --- a/ReactCommon/inspector/LegacyRuntimeAgent.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "LegacyRuntimeAgent.h" - -#include -#include -#include - -namespace facebook { -namespace react { - -using namespace Inspector; - -LegacyRuntimeAgent::LegacyRuntimeAgent(InjectedScriptManager* injectedScriptManager, JSC::JSGlobalObject& globalObject) - : InspectorRuntimeAgent(injectedScriptManager) - , m_globalObject(globalObject) {} - -void LegacyRuntimeAgent::didCreateFrontendAndBackend(InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) { - // m_frontendDispatcher = folly::make_unique(frontendChannel); - frontendChannel_ = frontendChannel; - m_backendDispatcher.reset(InspectorRuntimeBackendDispatcher::create(backendDispatcher, this).leakRef()); -} - -void LegacyRuntimeAgent::enable(ErrorString* error) { - InspectorRuntimeAgent::enable(error); - - auto contextObject = InspectorObject::create(); - contextObject->setNumber(ASCIILiteral("id"), 1); - contextObject->setBoolean(ASCIILiteral("isDefault"), true); - contextObject->setBoolean(ASCIILiteral("isPageContext"), true); - contextObject->setString(ASCIILiteral("origin"), ASCIILiteral("")); - contextObject->setString(ASCIILiteral("name"), ASCIILiteral("React Native")); - - auto jsonMessage = InspectorObject::create(); - jsonMessage->setString(ASCIILiteral("method"), ASCIILiteral("Runtime.executionContextCreated")); - auto paramsObject = InspectorObject::create(); - paramsObject->setValue(ASCIILiteral("context"), contextObject); - jsonMessage->setObject(ASCIILiteral("params"), paramsObject); - - frontendChannel_->sendMessageToFrontend(jsonMessage->toJSONString()); -} - -void LegacyRuntimeAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) { - frontendChannel_ = nullptr; - m_backendDispatcher = nullptr; -} - -JSC::VM& LegacyRuntimeAgent::globalVM() { - return m_globalObject.vm(); -} - -InjectedScript LegacyRuntimeAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) { - JSC::ExecState* scriptState = m_globalObject.globalExec(); - InjectedScript injectedScript = injectedScriptManager()->injectedScriptFor(scriptState); - if (injectedScript.hasNoValue()) { - *error = ASCIILiteral("Internal error: main world execution context not found."); - } - - return injectedScript; -} - -} -} diff --git a/ReactCommon/inspector/LegacyRuntimeAgent.h b/ReactCommon/inspector/LegacyRuntimeAgent.h deleted file mode 100644 index e0b04aa74ff79a..00000000000000 --- a/ReactCommon/inspector/LegacyRuntimeAgent.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include - -#include -#include - -namespace JSC { -class JSGlobalObject; -} - -namespace facebook { -namespace react { - -class LegacyRuntimeAgent : public Inspector::InspectorRuntimeAgent { -public: - LegacyRuntimeAgent(Inspector::InjectedScriptManager*, JSC::JSGlobalObject&); - - void enable(Inspector::ErrorString* error) override; - - void didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel*, Inspector::InspectorBackendDispatcher*) override; - void willDestroyFrontendAndBackend(Inspector::InspectorDisconnectReason) override; - - JSC::VM& globalVM() override; - Inspector::InjectedScript injectedScriptForEval(Inspector::ErrorString* error, const int* executionContextId) override; - - void muteConsole() override { } - void unmuteConsole() override { } -private: - Inspector::InspectorFrontendChannel* frontendChannel_; - // std::unique_ptr m_frontendDispatcher; - std::unique_ptr m_backendDispatcher; - JSC::JSGlobalObject& m_globalObject; -}; - -} -} diff --git a/ReactCommon/inspector/LegacyScriptDebugServer.cpp b/ReactCommon/inspector/LegacyScriptDebugServer.cpp deleted file mode 100644 index 3453e708df2689..00000000000000 --- a/ReactCommon/inspector/LegacyScriptDebugServer.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "LegacyScriptDebugServer.h" - -#include - -namespace facebook { -namespace react { - -using namespace Inspector; - -LegacyScriptDebugServer::LegacyScriptDebugServer(JSC::JSGlobalObject& globalObject) - : Inspector::ScriptDebugServer(false) - , globalObject_(globalObject) {} - -void LegacyScriptDebugServer::addListener(ScriptDebugListener* listener) -{ - if (!listener) { - return; - } - - bool wasEmpty = listeners_.isEmpty(); - listeners_.add(listener); - - // First listener. Attach the debugger to the JSGlobalObject. - if (wasEmpty) { - attach(&globalObject_); - recompileAllJSFunctions(); - } -} - -void LegacyScriptDebugServer::removeListener(ScriptDebugListener* listener, bool isBeingDestroyed) { - if (!listener) { - return; - } - - listeners_.remove(listener); - - // Last listener. Detach the debugger from the JSGlobalObject. - if (listeners_.isEmpty()) { - detach(&globalObject_, isBeingDestroyed ? Debugger::GlobalObjectIsDestructing : Debugger::TerminatingDebuggingSession); - if (!isBeingDestroyed) { - recompileAllJSFunctions(); - } - } -} - -void LegacyScriptDebugServer::recompileAllJSFunctions() { - JSC::Debugger::recompileAllJSFunctions(&globalObject_.vm()); -} - -void LegacyScriptDebugServer::runEventLoopWhilePaused() { - // Drop all locks so another thread can work in the VM while we are nested. - JSC::JSLock::DropAllLocks dropAllLocks(&globalObject_.vm()); - - // Spinning here is our best option, we could override the method - // notifyDoneProcessingDebuggerEvents but it's marked as final :( - while (!m_doneProcessingDebuggerEvents) { - usleep(10 * 1000); - } -} - -} -} diff --git a/ReactCommon/inspector/LegacyScriptDebugServer.h b/ReactCommon/inspector/LegacyScriptDebugServer.h deleted file mode 100644 index fe83804515419a..00000000000000 --- a/ReactCommon/inspector/LegacyScriptDebugServer.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include -#include - -namespace JSC { -class JSGlobalObject; -} - -namespace facebook { -namespace react { - -class LegacyScriptDebugServer : public Inspector::ScriptDebugServer { -public: - LegacyScriptDebugServer(JSC::JSGlobalObject& object); - - void addListener(Inspector::ScriptDebugListener* listener); - void removeListener(Inspector::ScriptDebugListener* listener, bool isBeingDestroyed); - - JSC::JSGlobalObject& globalObject() const { return globalObject_; } - - void recompileAllJSFunctions() override; - -private: - ListenerSet* getListenersForGlobalObject(JSC::JSGlobalObject*) override { return &listeners_; } - void didPause(JSC::JSGlobalObject*) override { } - void didContinue(JSC::JSGlobalObject*) override { } - void runEventLoopWhilePaused() override; - bool isContentScript(JSC::ExecState*) const override { return false; } - - // NOTE: Currently all exceptions are reported at the API boundary through reportAPIException. - // Until a time comes where an exception can be caused outside of the API (e.g. setTimeout - // or some other async operation in a pure JSContext) we can ignore exceptions reported here. - // TODO: Should we actually ignore them? - void reportException(JSC::ExecState*, JSC::JSValue) const override { } - - ListenerSet listeners_; - JSC::JSGlobalObject& globalObject_; -}; - - -} -} diff --git a/ReactCommon/inspector/PageAgent.cpp b/ReactCommon/inspector/PageAgent.cpp deleted file mode 100644 index 02dbb586162737..00000000000000 --- a/ReactCommon/inspector/PageAgent.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "PageAgent.h" - -namespace facebook { -namespace react { - -PageAgent::PageAgent() { - auto emptyMethod = [](folly::dynamic args) -> folly::dynamic { - return nullptr; - }; - - registerMethod("enable", emptyMethod); - registerMethod("disable", emptyMethod); - registerMethod("getResourceTree", [](folly::dynamic args) -> folly::dynamic { - return folly::dynamic::object - ("frameTree", folly::dynamic::object - ("childFrames", folly::dynamic::array) - ("resources", folly::dynamic::array) - ("frame", folly::dynamic::object - ("id", "1") - ("loaderId", "1") - ("name", "main") - ("url", "") - ("securityOrigin", "") - ("mimeType", "application/octet-stream"))); - }); -} - -} -} diff --git a/ReactCommon/inspector/PageAgent.h b/ReactCommon/inspector/PageAgent.h deleted file mode 100644 index c2f747eb78990b..00000000000000 --- a/ReactCommon/inspector/PageAgent.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include "Agent.h" - -namespace facebook { -namespace react { - -class PageAgent : public Agent { -public: - PageAgent(); -private: - std::string getDomain() override { - return "Page"; - } -}; - -} -} diff --git a/ReactCommon/inspector/Protocol.cpp b/ReactCommon/inspector/Protocol.cpp deleted file mode 100644 index eb7428ede49d69..00000000000000 --- a/ReactCommon/inspector/Protocol.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "Protocol.h" - -#include "Error.h" - -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -namespace { - -folly::dynamic getCallId(const folly::Optional& callId) { - if (callId.hasValue()) { - return callId.value(); - } else { - return nullptr; - } -} - -} - -Method Method::parse(const std::string& formatted) { - auto splitPos = formatted.find_first_of('.'); - if (splitPos == std::string::npos) { - throw InspectorException(ErrorCode::InvalidRequest, "Invalid method format"); - } - return Method(formatted.substr(0, splitPos), formatted.substr(splitPos + 1)); -} - -Method::Method(std::string domain, std::string name) - : domain_(std::move(domain)) - , name_(std::move(name)) {} - -std::string Method::formatted() const { - return folly::to(domain_, '.', name_); -} - -Event::Event(std::string domain, std::string method, folly::dynamic params) - : method_(std::move(domain), std::move(method)) - , params_(std::move(params)) {} - -Event::operator std::string() const { - auto event = folly::dynamic::object("method", method_.formatted()); - if (!params_.isNull()) { - event("params", params_); - } - - return folly::toJson(std::move(event)); -} - -namespace Timestamp { -double now() { - using duration = std::chrono::duration>; - auto epoch = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch() - ); - return epoch.count(); -} -} - -Error::Error(int callId, ErrorCode code, std::string message) - : callId_(callId) - , code_(code) - , message_(std::move(message)) {} - -Error::Error(ErrorCode code, std::string message) - : code_(code) - , message_(std::move(message)) {} - -Error::operator std::string() const { - auto errorCode = static_cast(code_); - return folly::toJson( - folly::dynamic::object("id", getCallId(callId_)) - ("error", folly::dynamic::object("code", errorCode)("message", message_)) - ); -} - - -} -} diff --git a/ReactCommon/inspector/Protocol.h b/ReactCommon/inspector/Protocol.h deleted file mode 100644 index 3b36ae7977dca3..00000000000000 --- a/ReactCommon/inspector/Protocol.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include - -#include -#include - -namespace facebook { -namespace react { - -class Method { -public: - static Method parse(const std::string& formatted); - - Method(std::string domain, std::string name); - - const std::string& domain() const { - return domain_; - } - - const std::string& name() const { - return name_; - } - - std::string formatted() const; -private: - std::string domain_; - std::string name_; -}; - -class Event { -public: - explicit Event(std::string domain, std::string method, folly::dynamic params); - - operator std::string() const; -private: - Method method_; - folly::dynamic params_; -}; - -namespace Timestamp { - double now(); -} - -enum class ErrorCode { - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - ServerError = -32000, -}; - -class Error { -public: - Error(int callId, ErrorCode code, std::string message); - Error(ErrorCode code, std::string message); - - const std::string& message() const { - return message_; - } - - ErrorCode code() const { - return code_; - } - - operator std::string() const; -private: - folly::Optional callId_; - ErrorCode code_; - std::string message_; -}; - -} -} diff --git a/ReactCommon/inspector/README.md b/ReactCommon/inspector/README.md deleted file mode 100644 index 67da1c7c31ede7..00000000000000 --- a/ReactCommon/inspector/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Inspector - -This directory implements an the Chrome debugging protocol [1]. The version used is roughly 1.1 of -the protocol. The code here doesn't specify a transport and doesn't implement an actual server. This -is left up to higher parts of the stack. - -The implementation uses multiple "dispatchers" to route messages for a specific domain. It reuses -existing code in JavaScriptCore to handle the domains for Debugger and Runtime. For Console, Page -and Inspector there are new implementations. - -## Open source - -The inspector currently doesn't compile in open source. This is due to how the build on Android -where we download the JSC sources and build an artifact separately, later download the headers we -need. The number of headers download would have to be expanded and verify that it builds correctly. - -[1]: https://developer.chrome.com/devtools/docs/debugger-protocol diff --git a/ReactCommon/inspector/Util.cpp b/ReactCommon/inspector/Util.cpp deleted file mode 100644 index 23c9ee6e156846..00000000000000 --- a/ReactCommon/inspector/Util.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "Util.h" - -namespace facebook { -namespace react { - -} -} diff --git a/ReactCommon/inspector/Util.h b/ReactCommon/inspector/Util.h deleted file mode 100644 index fb858bf30c66e8..00000000000000 --- a/ReactCommon/inspector/Util.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -namespace facebook { -namespace react { - -inline std::string toStdString(const WTF::String& str) { - return std::string(str.utf8().data()); -} - -template -class AgentMap { -public: - void add(JSGlobalContextRef ctx, T* agent) { - map_[ctx] = agent; - } - - void remove(T* agent) { - auto it = std::find_if( - map_.begin(), - map_.end(), - [agent](const typename MapType::value_type& entry) { return entry.second == agent; }); - map_.erase(it); - } - - T* get(JSGlobalContextRef ctx) { - return map_.at(ctx); - } -private: - using MapType = std::unordered_map; - MapType map_; -}; - -} -} From 95b61925b3e7c91fbdd0a94b12c4b04afc3591c5 Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Tue, 14 Mar 2017 12:57:17 -0700 Subject: [PATCH 020/366] measure time between building ReactInstanceManager and creating ReactContext Reviewed By: alexeylang Differential Revision: D4705021 fbshipit-source-id: 555ad512aed5b44165091a4bcc25a8a5564d55d3 --- .../java/com/facebook/react/bridge/ReactMarkerConstants.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java index 996ef7e4f90e55..a3f2f8e355ceb3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java @@ -68,4 +68,5 @@ public class ReactMarkerConstants { public static final String ON_HOST_PAUSE_END = "ON_HOST_PAUSE_END"; public static final String CONVERT_CONSTANTS_START = "CONVERT_CONSTANTS_START"; public static final String CONVERT_CONSTANTS_END = "CONVERT_CONSTANTS_END"; + public static final String PRE_REACT_CONTEXT_END = "PRE_REACT_CONTEXT_END"; } From 1ca41a56c65bda2c20e9d7714cb06413e44347c9 Mon Sep 17 00:00:00 2001 From: Jonathan Keljo Date: Tue, 14 Mar 2017 13:00:53 -0700 Subject: [PATCH 021/366] Fix ReactNative build break Reviewed By: asp2insp Differential Revision: D4707219 fbshipit-source-id: a7da2aa50fa812bb63ac7697c022f7b770eeb479 --- ReactAndroid/DEFS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactAndroid/DEFS b/ReactAndroid/DEFS index d54f4a601d2191..64b8d05b028c8f 100644 --- a/ReactAndroid/DEFS +++ b/ReactAndroid/DEFS @@ -46,7 +46,7 @@ original_android_library=android_library def android_library( name, deps=[], - plugins=[] + plugins=[], *args, **kwargs): From 5f8e46b8b43c752642ded162b0d1a18ee46c13fa Mon Sep 17 00:00:00 2001 From: Daniel Woelfel Date: Tue, 14 Mar 2017 13:08:50 -0700 Subject: [PATCH 022/366] prevent undefined is not an Object exception Reviewed By: sebmarkbage Differential Revision: D4707119 fbshipit-source-id: 403d7c26a1910d0ed1b980f305aa2b6326f371a2 --- .../src/renderers/shared/shared/event/EventPluginHub.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js b/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js index 6c4235ea423015..bba563c8c6d39d 100644 --- a/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js +++ b/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js @@ -147,6 +147,9 @@ var EventPluginHub = { return null; } const props = inst._currentElement.props; + if (!props) { + return null; + } listener = props[registrationName]; if (shouldPreventMouseEvent(registrationName, inst._currentElement.type, props)) { return null; From f7a81d18373f95ff5a9b8cc78b1d59080ba97cbb Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Tue, 14 Mar 2017 13:38:39 -0700 Subject: [PATCH 023/366] Add a bit more spacing between p elements on website Summary: Closes https://github.com/facebook/react-native/pull/12936 Differential Revision: D4707719 Pulled By: hramos fbshipit-source-id: 1b6c8e144f5a5904b8babdc001f50db8d68086c6 --- website/styles/react-native.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/styles/react-native.scss b/website/styles/react-native.scss index af927e0fea8371..dcb765bd8e70be 100644 --- a/website/styles/react-native.scss +++ b/website/styles/react-native.scss @@ -817,7 +817,7 @@ h2 { } p { - margin: 10px 0; + margin: 16px 0; } .highlight { From 0a475d2b32cc933ee9802e253754b6ec9cda8987 Mon Sep 17 00:00:00 2001 From: Alexey Lang Date: Tue, 14 Mar 2017 13:44:54 -0700 Subject: [PATCH 024/366] Use performanceNow to calculate __BUNDLE_START_TIME__ Reviewed By: javache Differential Revision: D4706142 fbshipit-source-id: aa983d9e5d5324f97ae34ec0190b1fc988d012c8 --- packager/src/Resolver/polyfills/prelude.js | 4 +++- packager/src/Resolver/polyfills/prelude_dev.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packager/src/Resolver/polyfills/prelude.js b/packager/src/Resolver/polyfills/prelude.js index 0451a75bed8646..38170fa115c566 100644 --- a/packager/src/Resolver/polyfills/prelude.js +++ b/packager/src/Resolver/polyfills/prelude.js @@ -13,4 +13,6 @@ global.__DEV__ = false; -global.__BUNDLE_START_TIME__ = Date.now(); +global.__BUNDLE_START_TIME__ = global.nativePerformanceNow + ? global.nativePerformanceNow() + : Date.now(); diff --git a/packager/src/Resolver/polyfills/prelude_dev.js b/packager/src/Resolver/polyfills/prelude_dev.js index 0f086fb9bfb426..caf05c283bd83a 100644 --- a/packager/src/Resolver/polyfills/prelude_dev.js +++ b/packager/src/Resolver/polyfills/prelude_dev.js @@ -13,4 +13,6 @@ global.__DEV__ = true; -global.__BUNDLE_START_TIME__ = Date.now(); +global.__BUNDLE_START_TIME__ = global.nativePerformanceNow + ? global.nativePerformanceNow() + : Date.now(); From bacee5ad560e5d4a6d6a090565f406f658026528 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Tue, 14 Mar 2017 14:53:37 -0700 Subject: [PATCH 025/366] Update worker to match command line args passed by Buck Reviewed By: cpojer Differential Revision: D4673955 fbshipit-source-id: 1886cef97a96efbe0e90b02b98f03544d5f3f155 --- packager/src/ModuleGraph/ModuleGraph.js | 2 +- .../ModuleGraph/__tests__/ModuleGraph-test.js | 19 ------------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/packager/src/ModuleGraph/ModuleGraph.js b/packager/src/ModuleGraph/ModuleGraph.js index b26eca8e985e64..757fe16fbc9352 100644 --- a/packager/src/ModuleGraph/ModuleGraph.js +++ b/packager/src/ModuleGraph/ModuleGraph.js @@ -52,7 +52,7 @@ exports.createBuildSetup = ( parallel({ graph: cb => graphWithOptions( - concat(defaults.runBeforeMainModule, entryPoints), + entryPoints, cb, ), moduleSystem: cb => graphOnlyModules( diff --git a/packager/src/ModuleGraph/__tests__/ModuleGraph-test.js b/packager/src/ModuleGraph/__tests__/ModuleGraph-test.js index d183feebcae434..4a3844c93c0e21 100644 --- a/packager/src/ModuleGraph/__tests__/ModuleGraph-test.js +++ b/packager/src/ModuleGraph/__tests__/ModuleGraph-test.js @@ -70,16 +70,6 @@ describe('build setup', () => { }); }); - it('places all modules from `defaults.runBeforeMainModule` after the polyfills', done => { - buildSetup(noEntryPoints, noOptions, (error, result) => { - const additionalModules = - Array.from(result.modules).slice(-defaults.runBeforeMainModule.length); - expect(additionalModules) - .toEqual(defaults.runBeforeMainModule.map(moduleFromPath)); - done(); - }); - }); - it('places all entry points at the end', done => { const entryPoints = ['a', 'b', 'c']; buildSetup(entryPoints, noOptions, (error, result) => { @@ -88,15 +78,6 @@ describe('build setup', () => { done(); }); }); - - it('concatenates `runBeforeMainModule` and entry points as `entryModules`', done => { - const entryPoints = ['a', 'b', 'c']; - buildSetup(entryPoints, noOptions, (error, result) => { - expect(Array.from(result.entryModules)).toEqual( - defaults.runBeforeMainModule.concat(entryPoints).map(moduleFromPath)); - done(); - }); - }); }); function moduleFromPath(path) { From 7e4b8ff000439a6fa1fcaaf5eff9c39295bfba51 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Tue, 14 Mar 2017 15:28:49 -0700 Subject: [PATCH 026/366] Support passing native modules to JSContext Reviewed By: amnn Differential Revision: D4561036 fbshipit-source-id: b096a222103e895b14cba1ec5b2bb6e72dd72c37 --- ReactCommon/cxxreact/BUCK | 1 - ReactCommon/cxxreact/Instance.cpp | 10 ++++++++++ ReactCommon/cxxreact/NativeToJsBridge.cpp | 14 ++++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index 45496978a27648..c82d6a8769d2eb 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -55,7 +55,6 @@ if THIS_IS_FBANDROID: # `initOnJSVMThread` to be called before the platform-specific hooks # have been properly initialised. Bad Times(TM). # -- @ashokmenon (2017/01/03) - '//java/com/facebook/java2js:jni', react_native_target('jni/xreact/jni:jni'), react_native_xplat_target('cxxreact/...'), ], diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index c673958435e227..12a9dbe1e443ff 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -2,6 +2,7 @@ #include "Instance.h" +#include "CxxMessageQueue.h" #include "Executor.h" #include "MethodCall.h" #include "RecoverableError.h" @@ -36,6 +37,15 @@ void Instance::initializeBridge( std::shared_ptr moduleRegistry) { callback_ = std::move(callback); + if (!nativeQueue) { + // TODO pass down a thread/queue from java, instead of creating our own. + + auto queue = std::make_unique(); + std::thread t(queue->getUnregisteredRunLoop()); + t.detach(); + nativeQueue = std::move(queue); + } + jsQueue->runOnQueueSync( [this, &jsef, moduleRegistry, jsQueue, nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable { diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 347d5ad9e750c8..56a71adfd2281e 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -95,9 +95,13 @@ NativeToJsBridge::NativeToJsBridge( std::shared_ptr callback) : m_destroyed(std::make_shared(false)) , m_mainExecutorToken(callback->createExecutorToken()) - , m_delegate( - std::make_shared( - this, registry, std::move(nativeQueue), callback)) { + , m_delegate(registry + ? std::make_shared( + this, registry, std::move(nativeQueue), callback) + : nullptr) { + if (!m_delegate) { + nativeQueue->quitSynchronous(); + } std::unique_ptr mainExecutor = jsExecutorFactory->createJSExecutor(m_delegate, jsQueue); // cached to avoid locked map lookup in the common case @@ -314,7 +318,9 @@ ExecutorToken NativeToJsBridge::getTokenForExecutor(JSExecutor& executor) { } void NativeToJsBridge::destroy() { - m_delegate->quitQueueSynchronous(); + if (m_delegate) { + m_delegate->quitQueueSynchronous(); + } auto* executorMessageQueueThread = getMessageQueueThread(m_mainExecutorToken); // All calls made through runOnExecutorQueue have an early exit if // m_destroyed is true. Setting this before the runOnQueueSync will cause From 6410e256c5777147dc6f8f59ca1f7996e14b0353 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Tue, 14 Mar 2017 15:28:52 -0700 Subject: [PATCH 027/366] Decouple module callback mechanism from CatalystInstance Summary: Create a JSInstance superinterface which doesn't include all the lifecycle stuff. Reviewed By: AaaChiuuu Differential Revision: D4614410 fbshipit-source-id: 16047bbcb1bb69bf36a0a13ef68f3a6aa396a991 --- .../facebook/react/bridge/BaseJavaModule.java | 32 +++++++++---------- .../facebook/react/bridge/CallbackImpl.java | 8 ++--- .../react/bridge/CatalystInstance.java | 10 ++++-- .../com/facebook/react/bridge/JSInstance.java | 24 ++++++++++++++ .../facebook/react/bridge/NativeModule.java | 2 +- .../react/cxxbridge/CatalystInstanceImpl.java | 20 ++++++------ .../react/cxxbridge/JavaModuleWrapper.java | 10 +++--- .../react/cxxbridge/NativeModuleRegistry.java | 13 ++++---- 8 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java index 9fac0a18efd41f..f911938b400975 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java @@ -59,14 +59,14 @@ public int getJSArgumentsNeeded() { } public abstract @Nullable T extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex); + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex); } static final private ArgumentExtractor ARGUMENT_EXTRACTOR_BOOLEAN = new ArgumentExtractor() { @Override public Boolean extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return jsArguments.getBoolean(atIndex); } }; @@ -75,7 +75,7 @@ public Boolean extractArgument( new ArgumentExtractor() { @Override public Double extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return jsArguments.getDouble(atIndex); } }; @@ -84,7 +84,7 @@ public Double extractArgument( new ArgumentExtractor() { @Override public Float extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return (float) jsArguments.getDouble(atIndex); } }; @@ -93,7 +93,7 @@ public Float extractArgument( new ArgumentExtractor() { @Override public Integer extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return (int) jsArguments.getDouble(atIndex); } }; @@ -102,7 +102,7 @@ public Integer extractArgument( new ArgumentExtractor() { @Override public String extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return jsArguments.getString(atIndex); } }; @@ -111,7 +111,7 @@ public String extractArgument( new ArgumentExtractor() { @Override public ReadableNativeArray extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return jsArguments.getArray(atIndex); } }; @@ -120,7 +120,7 @@ public ReadableNativeArray extractArgument( new ArgumentExtractor() { @Override public Dynamic extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return DynamicFromArray.create(jsArguments, atIndex); } }; @@ -129,7 +129,7 @@ public Dynamic extractArgument( new ArgumentExtractor() { @Override public ReadableMap extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { return jsArguments.getMap(atIndex); } }; @@ -138,12 +138,12 @@ public ReadableMap extractArgument( new ArgumentExtractor() { @Override public @Nullable Callback extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { if (jsArguments.isNull(atIndex)) { return null; } else { int id = (int) jsArguments.getDouble(atIndex); - return new CallbackImpl(catalystInstance, executorToken, id); + return new CallbackImpl(jsInstance, executorToken, id); } } }; @@ -157,11 +157,11 @@ public int getJSArgumentsNeeded() { @Override public Promise extractArgument( - CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { + JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) { Callback resolve = ARGUMENT_EXTRACTOR_CALLBACK - .extractArgument(catalystInstance, executorToken, jsArguments, atIndex); + .extractArgument(jsInstance, executorToken, jsArguments, atIndex); Callback reject = ARGUMENT_EXTRACTOR_CALLBACK - .extractArgument(catalystInstance, executorToken, jsArguments, atIndex + 1); + .extractArgument(jsInstance, executorToken, jsArguments, atIndex + 1); return new PromiseImpl(resolve, reject); } }; @@ -307,7 +307,7 @@ private String getAffectedRange(int startIndex, int jsArgumentsNeeded) { } @Override - public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) { + public void invoke(JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray parameters) { SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "callJavaModuleMethod") .arg("method", mTraceName) .flush(); @@ -329,7 +329,7 @@ public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToke try { for (; i < mArgumentExtractors.length; i++) { mArguments[i + executorTokenOffset] = mArgumentExtractors[i].extractArgument( - catalystInstance, executorToken, parameters, jsArgumentsConsumed); + jsInstance, executorToken, parameters, jsArgumentsConsumed); jsArgumentsConsumed += mArgumentExtractors[i].getJSArgumentsNeeded(); } } catch (UnexpectedNativeTypeException e) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CallbackImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CallbackImpl.java index 7ba988a959fd51..aeef58ec4e8b6f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CallbackImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CallbackImpl.java @@ -14,13 +14,13 @@ */ public final class CallbackImpl implements Callback { - private final CatalystInstance mCatalystInstance; + private final JSInstance mJSInstance; private final ExecutorToken mExecutorToken; private final int mCallbackId; private boolean mInvoked; - public CallbackImpl(CatalystInstance bridge, ExecutorToken executorToken, int callbackId) { - mCatalystInstance = bridge; + public CallbackImpl(JSInstance jsInstance, ExecutorToken executorToken, int callbackId) { + mJSInstance = jsInstance; mExecutorToken = executorToken; mCallbackId = callbackId; mInvoked = false; @@ -33,7 +33,7 @@ public void invoke(Object... args) { "module. This callback type only permits a single invocation from "+ "native code."); } - mCatalystInstance.invokeCallback(mExecutorToken, mCallbackId, Arguments.fromJavaArgs(args)); + mJSInstance.invokeCallback(mExecutorToken, mCallbackId, Arguments.fromJavaArgs(args)); mInvoked = true; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java index 725182227d2719..4f3640aede6dc2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -23,7 +23,8 @@ * Java APIs be invokable from JavaScript as well. */ @DoNotStrip -public interface CatalystInstance extends MemoryPressureListener { +public interface CatalystInstance + extends MemoryPressureListener, JSInstance { void runJSBundle(); /** @@ -34,8 +35,11 @@ public interface CatalystInstance extends MemoryPressureListener { // This is called from java code, so it won't be stripped anyway, but proguard will rename it, // which this prevents. - @DoNotStrip - void invokeCallback(ExecutorToken executorToken, final int callbackID, final NativeArray arguments); + @Override @DoNotStrip + void invokeCallback( + ExecutorToken executorToken, + int callbackID, + NativeArray arguments); @DoNotStrip void callFunction( ExecutorToken executorToken, diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.java new file mode 100644 index 00000000000000..30cb7be79f7508 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.java @@ -0,0 +1,24 @@ +/** + * 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. + */ + +package com.facebook.react.bridge; + +/** + * This interface includes the methods needed to use a running JS + * instance, without specifying any of the bridge-specific + * initialization or lifecycle management. + */ +public interface JSInstance { + void invokeCallback( + ExecutorToken executorToken, + int callbackID, + NativeArray arguments); + // TODO if this interface survives refactoring, think about adding + // callFunction. +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java index b92ed701a9b7ab..830f56301433ab 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java @@ -20,7 +20,7 @@ */ public interface NativeModule { interface NativeMethod { - void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters); + void invoke(JSInstance jsInstance, ExecutorToken executorToken, ReadableNativeArray parameters); String getType(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java index 0b564b42a9649b..4e9d9c0883a5b3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -176,12 +176,13 @@ public void onNativeException(Exception e) { } } - private native void initializeBridge(ReactCallback callback, - JavaScriptExecutor jsExecutor, - MessageQueueThread jsQueue, - MessageQueueThread moduleQueue, - Collection javaModules, - Collection cxxModules); + private native void initializeBridge( + ReactCallback callback, + JavaScriptExecutor jsExecutor, + MessageQueueThread jsQueue, + MessageQueueThread moduleQueue, + Collection javaModules, + Collection cxxModules); /** * This API is used in situations where the JS bundle is being executed not on @@ -232,7 +233,6 @@ public void runJSBundle() { mJSCallsPendingInit.clear(); } - // This is registered after JS starts since it makes a JS call Systrace.registerListener(mTraceListener); } @@ -302,7 +302,7 @@ public void destroy() { mReactQueueConfiguration.getNativeModulesQueueThread().runOnQueue(new Runnable() { @Override public void run() { - mJavaRegistry.notifyCatalystInstanceDestroy(); + mJavaRegistry.notifyJSInstanceDestroy(); } }); boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0); @@ -341,7 +341,7 @@ public void initialize() { mReactQueueConfiguration.getNativeModulesQueueThread().runOnQueue(new Runnable() { @Override public void run() { - mJavaRegistry.notifyCatalystInstanceInitialized(); + mJavaRegistry.notifyJSInstanceInitialized(); } }); } @@ -390,7 +390,7 @@ public void handleMemoryPressure(MemoryPressure level) { if (mDestroyed) { return; } - switch(level) { + switch (level) { case UI_HIDDEN: handleMemoryPressureUiHidden(); break; diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java index e0e07af6b76825..fb3190911472a7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java @@ -16,8 +16,8 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.BaseJavaModule; -import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.ExecutorToken; +import com.facebook.react.bridge.JSInstance; import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactMarker; @@ -54,12 +54,12 @@ public class MethodDescriptor { String type; } - private final CatalystInstance mCatalystInstance; + private final JSInstance mJSInstance; private final ModuleHolder mModuleHolder; private final ArrayList mMethods; - public JavaModuleWrapper(CatalystInstance catalystinstance, ModuleHolder moduleHolder) { - mCatalystInstance = catalystinstance; + public JavaModuleWrapper(JSInstance jsInstance, ModuleHolder moduleHolder) { + mJSInstance = jsInstance; mModuleHolder = moduleHolder; mMethods = new ArrayList<>(); } @@ -135,6 +135,6 @@ public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parame return; } - mMethods.get(methodId).invoke(mCatalystInstance, token, parameters); + mMethods.get(methodId).invoke(mJSInstance, token, parameters); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java index 93d9e276d7fef7..2b0a576259edfa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java @@ -15,6 +15,7 @@ import java.util.Map; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.JSInstance; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.OnBatchCompleteListener; import com.facebook.react.bridge.ReactApplicationContext; @@ -41,12 +42,12 @@ public NativeModuleRegistry( } /* package */ Collection getJavaModules( - CatalystInstanceImpl catalystInstanceImpl) { + JSInstance jsInstance) { ArrayList javaModules = new ArrayList<>(); for (Map.Entry, ModuleHolder> entry : mModules.entrySet()) { Class type = entry.getKey(); if (!CxxModuleWrapper.class.isAssignableFrom(type)) { - javaModules.add(new JavaModuleWrapper(catalystInstanceImpl, entry.getValue())); + javaModules.add(new JavaModuleWrapper(jsInstance, entry.getValue())); } } return javaModules; @@ -63,11 +64,11 @@ public NativeModuleRegistry( return cxxModules; } - /* package */ void notifyCatalystInstanceDestroy() { + /* package */ void notifyJSInstanceDestroy() { mReactApplicationContext.assertOnNativeModulesQueueThread(); Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "NativeModuleRegistry_notifyCatalystInstanceDestroy"); + "NativeModuleRegistry_notifyJSInstanceDestroy"); try { for (ModuleHolder module : mModules.values()) { module.destroy(); @@ -77,7 +78,7 @@ public NativeModuleRegistry( } } - /* package */ void notifyCatalystInstanceInitialized() { + /* package */ void notifyJSInstanceInitialized() { mReactApplicationContext.assertOnNativeModulesQueueThread("From version React Native v0.44, " + "native modules are explicitly not initialized on the UI thread. See " + "https://github.com/facebook/react-native/wiki/Breaking-Changes#d4611211-reactnativeandroidbreaking-move-nativemodule-initialization-off-ui-thread---aaachiuuu " + @@ -85,7 +86,7 @@ public NativeModuleRegistry( ReactMarker.logMarker(ReactMarkerConstants.NATIVE_MODULE_INITIALIZE_START); Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "NativeModuleRegistry_notifyCatalystInstanceInitialized"); + "NativeModuleRegistry_notifyJSInstanceInitialized"); try { for (ModuleHolder module : mModules.values()) { module.initialize(); From e622d51e20ea53160ed534e4d2c2d968d5e5d66d Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Tue, 14 Mar 2017 15:28:53 -0700 Subject: [PATCH 028/366] Support ModuleHolder-based lazy init of C++ modules with C++ bridge on android Reviewed By: AaaChiuuu Differential Revision: D4614479 fbshipit-source-id: 109ac34b8688f0113675e4a4479d1ddcc6169ed4 --- React/CxxBridge/RCTCxxBridge.mm | 3 +- .../react/cxxbridge/CatalystInstanceImpl.java | 6 ++- .../react/cxxbridge/NativeModuleRegistry.java | 6 +-- .../src/main/jni/xreact/jni/Android.mk | 1 + ReactAndroid/src/main/jni/xreact/jni/BUCK | 1 + .../jni/xreact/jni/CatalystInstanceImpl.cpp | 17 ++----- .../jni/xreact/jni/CatalystInstanceImpl.h | 3 +- .../main/jni/xreact/jni/CxxModuleWrapper.h | 8 ++- .../main/jni/xreact/jni/JavaModuleWrapper.h | 2 + .../jni/xreact/jni/ModuleRegistryBuilder.cpp | 51 +++++++++++++++++++ .../jni/xreact/jni/ModuleRegistryBuilder.h | 30 +++++++++++ ReactCommon/cxxreact/Android.mk | 1 + ReactCommon/cxxreact/CxxModule.h | 2 + ReactCommon/cxxreact/CxxNativeModule.cpp | 25 ++++++--- ReactCommon/cxxreact/CxxNativeModule.h | 8 ++- ReactCommon/cxxreact/Instance.cpp | 2 +- ReactCommon/cxxreact/JSCExecutor.cpp | 36 ++++++++----- ReactCommon/cxxreact/JSCNativeModules.cpp | 4 ++ ReactCommon/cxxreact/NativeToJsBridge.cpp | 16 +++--- ReactCommon/jschelpers/Value.h | 4 ++ 20 files changed, 174 insertions(+), 52 deletions(-) create mode 100644 ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp create mode 100644 ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index 431c59f4f8b040..16b8782ec07338 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -553,7 +553,8 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass } modules.emplace_back( new QueueNativeModule(self, std::make_unique( - _reactInstance, [(RCTCxxModule *)(moduleData.instance) move]))); + _reactInstance, [moduleData.name UTF8String], + [moduleData] { return [(RCTCxxModule *)(moduleData.instance) move]; }))); } else { modules.emplace_back(new RCTNativeModule(self, moduleData)); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java index 4e9d9c0883a5b3..38a9d548546131 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -110,7 +110,7 @@ private CatalystInstanceImpl( final JavaScriptModuleRegistry jsModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) { - FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge."); + FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge."); mHybridData = initHybrid(); mReactQueueConfiguration = ReactQueueConfigurationImpl.create( @@ -123,6 +123,7 @@ private CatalystInstanceImpl( mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; mTraceListener = new JSProfilerTraceListener(this); + FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge"); initializeBridge( new BridgeCallback(this), jsExecutor, @@ -130,6 +131,7 @@ private CatalystInstanceImpl( mReactQueueConfiguration.getNativeModulesQueueThread(), mJavaRegistry.getJavaModules(this), mJavaRegistry.getCxxModules()); + FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge"); mMainExecutorToken = getMainExecutorToken(); } @@ -182,7 +184,7 @@ private native void initializeBridge( MessageQueueThread jsQueue, MessageQueueThread moduleQueue, Collection javaModules, - Collection cxxModules); + Collection cxxModules); /** * This API is used in situations where the JS bundle is being executed not on diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java index 2b0a576259edfa..140c4efdb328d2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java @@ -53,12 +53,12 @@ public NativeModuleRegistry( return javaModules; } - /* package */ Collection getCxxModules() { - ArrayList cxxModules = new ArrayList<>(); + /* package */ Collection getCxxModules() { + ArrayList cxxModules = new ArrayList<>(); for (Map.Entry, ModuleHolder> entry : mModules.entrySet()) { Class type = entry.getKey(); if (CxxModuleWrapper.class.isAssignableFrom(type)) { - cxxModules.add((CxxModuleWrapper) entry.getValue().getModule()); + cxxModules.add(entry.getValue()); } } return cxxModules; diff --git a/ReactAndroid/src/main/jni/xreact/jni/Android.mk b/ReactAndroid/src/main/jni/xreact/jni/Android.mk index ea700e03ab6a69..46cebd13e2c30c 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/Android.mk +++ b/ReactAndroid/src/main/jni/xreact/jni/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES := \ JSLogging.cpp \ JniJSModulesUnbundle.cpp \ MethodInvoker.cpp \ + ModuleRegistryBuilder.cpp \ NativeArray.cpp \ NativeCommon.cpp \ NativeMap.cpp \ diff --git a/ReactAndroid/src/main/jni/xreact/jni/BUCK b/ReactAndroid/src/main/jni/xreact/jni/BUCK index ef06da174c223b..f58ec9e4b277d1 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/BUCK +++ b/ReactAndroid/src/main/jni/xreact/jni/BUCK @@ -6,6 +6,7 @@ EXPORTED_HEADERS = [ "JExecutorToken.h", "JSLoader.h", "MethodInvoker.h", + "ModuleRegistryBuilder.h", "NativeArray.h", "NativeCommon.h", "NativeMap.h", diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index b4aa91b631c860..a38ed7713f2e2e 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -20,6 +20,7 @@ #include #include +#include "CxxModuleWrapper.h" #include "JavaScriptExecutorHolder.h" #include "JniJSModulesUnbundle.h" #include "JNativeRunnable.h" @@ -128,21 +129,10 @@ void CatalystInstanceImpl::initializeBridge( jni::alias_ref jsQueue, jni::alias_ref moduleQueue, jni::alias_ref::javaobject> javaModules, - jni::alias_ref::javaobject> cxxModules) { + jni::alias_ref::javaobject> cxxModules) { // TODO mhorowitz: how to assert here? // Assertions.assertCondition(mBridge == null, "initializeBridge should be called once"); - std::vector> modules; - std::weak_ptr winstance(instance_); - for (const auto& jm : *javaModules) { - modules.emplace_back(folly::make_unique(winstance, jm)); - } - for (const auto& cm : *cxxModules) { - modules.emplace_back( - folly::make_unique(winstance, std::move(cthis(cm)->getModule()))); - } - auto moduleRegistry = std::make_shared(std::move(modules)); - // This used to be: // // Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback @@ -163,7 +153,8 @@ void CatalystInstanceImpl::initializeBridge( jseh->getExecutorFactory(), folly::make_unique(jsQueue), folly::make_unique(moduleQueue), - moduleRegistry); + buildModuleRegistry(std::weak_ptr(instance_), + javaModules, cxxModules)); } void CatalystInstanceImpl::jniSetSourceURL(const std::string& sourceURL) { diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index 61e07b9c650be7..804fc927cbc1aa 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -10,6 +10,7 @@ #include "JMessageQueueThread.h" #include "JSLoader.h" #include "JavaModuleWrapper.h" +#include "ModuleRegistryBuilder.h" namespace facebook { namespace react { @@ -48,7 +49,7 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref jsQueue, jni::alias_ref moduleQueue, jni::alias_ref::javaobject> javaModules, - jni::alias_ref::javaobject> cxxModules); + jni::alias_ref::javaobject> cxxModules); /** * Sets the source URL of the underlying bridge without loading any JS code. diff --git a/ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h b/ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h index 7e80094644b06f..1d5e8ed0c43c95 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h @@ -12,7 +12,13 @@ namespace facebook { namespace react { -class CxxModuleWrapper : public jni::HybridClass { +struct JNativeModule : jni::JavaClass { + constexpr static const char *const kJavaDescriptor = + "Lcom/facebook/react/bridge/NativeModule;"; +}; + +class CxxModuleWrapper : + public jni::HybridClass { public: constexpr static const char *const kJavaDescriptor = "Lcom/facebook/react/cxxbridge/CxxModuleWrapper;"; diff --git a/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h b/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h index 9ef6c483bd8b7a..544b6eb167c169 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h +++ b/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h @@ -27,6 +27,8 @@ struct JavaModuleWrapper : jni::JavaClass { static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/JavaModuleWrapper;"; jni::local_ref getModule() { + // This is the call which causes a lazy Java module to actually be + // created. static auto getModule = javaClassStatic()->getMethod("getModule"); return getModule(self()); } diff --git a/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp new file mode 100644 index 00000000000000..108091a8dfd934 --- /dev/null +++ b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp @@ -0,0 +1,51 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "ModuleRegistryBuilder.h" + +#include +#include + +namespace facebook { +namespace react { + +std::string ModuleHolder::getName() const { + static auto method = getClass()->getMethod("getName"); + return method(self())->toStdString(); +} + +xplat::module::CxxModule::Provider ModuleHolder::getProvider() const { + return [self=jni::make_global(self())] { + static auto method = + ModuleHolder::javaClassStatic()->getMethod( + "getModule"); + // This is the call which uses the lazy Java Provider to instantiate the + // Java CxxModuleWrapper which contains the CxxModule. + auto module = method(self); + CHECK(module->isInstanceOf(CxxModuleWrapper::javaClassStatic())) + << "module isn't a C++ module"; + auto cxxModule = jni::static_ref_cast(module); + // Then, we grab the CxxModule from the wrapper, which is no longer needed. + return cxxModule->cthis()->getModule(); + }; +} + +std::unique_ptr buildModuleRegistry( + std::weak_ptr winstance, + jni::alias_ref::javaobject> javaModules, + jni::alias_ref::javaobject> cxxModules) { + std::vector> modules; + for (const auto& jm : *javaModules) { + modules.emplace_back(folly::make_unique(winstance, jm)); + } + for (const auto& cm : *cxxModules) { + modules.emplace_back( + folly::make_unique(winstance, cm->getName(), cm->getProvider())); + } + if (modules.empty()) { + return nullptr; + } else { + return folly::make_unique(std::move(modules)); + } +} + +}} diff --git a/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h new file mode 100644 index 00000000000000..554473601173f8 --- /dev/null +++ b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h @@ -0,0 +1,30 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include + +#include +#include +#include + +#include "CxxModuleWrapper.h" +#include "JavaModuleWrapper.h" + +namespace facebook { +namespace react { + +class ModuleHolder : public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = + "Lcom/facebook/react/cxxbridge/ModuleHolder;"; + + std::string getName() const; + xplat::module::CxxModule::Provider getProvider() const; +}; + +std::unique_ptr buildModuleRegistry( + std::weak_ptr winstance, + jni::alias_ref::javaobject> javaModules, + jni::alias_ref::javaobject> cxxModules); + +} +} diff --git a/ReactCommon/cxxreact/Android.mk b/ReactCommon/cxxreact/Android.mk index f9cdb1976ab87e..6f33a13ef738f8 100644 --- a/ReactCommon/cxxreact/Android.mk +++ b/ReactCommon/cxxreact/Android.mk @@ -5,6 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libreactnativefb LOCAL_SRC_FILES := \ + CxxMessageQueue.cpp \ CxxNativeModule.cpp \ Instance.cpp \ JSCExecutor.cpp \ diff --git a/ReactCommon/cxxreact/CxxModule.h b/ReactCommon/cxxreact/CxxModule.h index 0b420b01fc6c3d..897123008bc8b8 100644 --- a/ReactCommon/cxxreact/CxxModule.h +++ b/ReactCommon/cxxreact/CxxModule.h @@ -50,6 +50,8 @@ class CxxModule { class SyncTagType {}; public: + typedef std::function()> Provider; + typedef std::function)> Callback; constexpr static AsyncTagType AsyncTag = AsyncTagType(); diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index f73cd30005d9dd..c0cacd0b85b927 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -47,18 +47,19 @@ CxxModule::Callback convertCallback( } CxxNativeModule::CxxNativeModule(std::weak_ptr instance, - std::unique_ptr module) + std::string name, + CxxModule::Provider provider) : instance_(instance) - , module_(std::move(module)) - , methods_(module_->getMethods()) { - module_->setInstance(instance); - } + , name_(std::move(name)) + , provider_(provider) {} std::string CxxNativeModule::getName() { - return module_->getName(); + return name_; } std::vector CxxNativeModule::getMethods() { + lazyInit(); + std::vector descs; for (auto& method : methods_) { assert(method.func || method.syncFunc); @@ -68,6 +69,8 @@ std::vector CxxNativeModule::getMethods() { } folly::dynamic CxxNativeModule::getConstants() { + lazyInit(); + folly::dynamic constants = folly::dynamic::object(); for (auto& pair : module_->getConstants()) { constants.insert(std::move(pair.first), std::move(pair.second)); @@ -168,5 +171,15 @@ MethodCallResult CxxNativeModule::callSerializableNativeHook( return method.syncFunc(std::move(args)); } +void CxxNativeModule::lazyInit() { + if (module_) { + return; + } + + module_ = provider_(); + methods_ = module_->getMethods(); + module_->setInstance(instance_); +} + } } diff --git a/ReactCommon/cxxreact/CxxNativeModule.h b/ReactCommon/cxxreact/CxxNativeModule.h index 308d004bb4892d..ab6f3df87ffd4c 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.h +++ b/ReactCommon/cxxreact/CxxNativeModule.h @@ -15,8 +15,8 @@ std::function makeCallback( class CxxNativeModule : public NativeModule { public: - CxxNativeModule(std::weak_ptr instance, - std::unique_ptr module); + CxxNativeModule(std::weak_ptr instance, std::string name, + xplat::module::CxxModule::Provider provider); std::string getName() override; std::vector getMethods() override; @@ -27,7 +27,11 @@ class CxxNativeModule : public NativeModule { ExecutorToken token, unsigned int hookId, folly::dynamic&& args) override; private: + void lazyInit(); + std::weak_ptr instance_; + std::string name_; + xplat::module::CxxModule::Provider provider_; std::unique_ptr module_; std::vector methods_; }; diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index 12a9dbe1e443ff..f7ad78ed4e0266 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -40,7 +40,7 @@ void Instance::initializeBridge( if (!nativeQueue) { // TODO pass down a thread/queue from java, instead of creating our own. - auto queue = std::make_unique(); + auto queue = folly::make_unique(); std::thread t(queue->getUnregisteredRunLoop()); t.detach(); nativeQueue = std::move(queue); diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index 606db4ab4434be..cee2f2ece2e350 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -361,12 +361,8 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr scrip evaluateSourceCode(m_context, bcSourceCode, jsSourceURL); - // TODO(luk): t13903306 Remove this check once we make native modules - // working for java2js - if (m_delegate) { - bindBridge(); - flush(); - } + bindBridge(); + flush(); ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); ReactMarker::logMarker("RUN_JS_BUNDLE_END"); @@ -416,11 +412,8 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr scrip evaluateScript(m_context, jsScript, jsSourceURL); } - // TODO(luk): t13903306 Remove this check once we make native modules working for java2js - if (m_delegate) { - bindBridge(); - flush(); - } + bindBridge(); + flush(); ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); ReactMarker::logMarker("RUN_JS_BUNDLE_END"); @@ -435,6 +428,9 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr unbund void JSCExecutor::bindBridge() throw(JSException) { SystraceSection s("JSCExecutor::bindBridge"); + if (!m_delegate || !m_delegate->getModuleRegistry()) { + return; + } auto global = Object::getGlobalObject(m_context); auto batchedBridgeValue = global.getProperty("__fbBatchedBridge"); if (batchedBridgeValue.isUndefined()) { @@ -466,7 +462,18 @@ void JSCExecutor::callNativeModules(Value&& value) { void JSCExecutor::flush() { SystraceSection s("JSCExecutor::flush"); - callNativeModules(m_flushedQueueJS->callAsFunction({})); + if (!m_delegate) { + // do nothing + } else if (!m_delegate->getModuleRegistry()) { + callNativeModules(Value::makeNull(m_context)); + } else { + // If this is failing, chances are you have provided a delegate with a + // module registry, but haven't loaded the JS which enables native function + // queueing. Add BatchedBridge.js to your bundle, pass a nullptr delegate, + // or make delegate->getModuleRegistry() return nullptr. + CHECK(m_flushedQueueJS) << "Attempting to use native methods without loading BatchedBridge.js"; + callNativeModules(m_flushedQueueJS->callAsFunction({})); + } } void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) { @@ -476,6 +483,9 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m auto result = [&] { try { + // See flush() + CHECK(m_callFunctionReturnFlushedQueueJS) + << "Attempting to call native methods without loading BatchedBridge.js"; return m_callFunctionReturnFlushedQueueJS->callAsFunction({ Value(m_context, String::createExpectingAscii(m_context, moduleId)), Value(m_context, String::createExpectingAscii(m_context, methodId)), @@ -511,6 +521,8 @@ Value JSCExecutor::callFunctionSyncWithValue( const std::string& module, const std::string& method, Value args) { SystraceSection s("JSCExecutor::callFunction"); + // See flush() + CHECK(m_callFunctionReturnResultAndFlushedQueueJS); Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({ Value(m_context, String::createExpectingAscii(m_context, module)), Value(m_context, String::createExpectingAscii(m_context, method)), diff --git a/ReactCommon/cxxreact/JSCNativeModules.cpp b/ReactCommon/cxxreact/JSCNativeModules.cpp index b1036ba376c0e9..105873cfd9fbc8 100644 --- a/ReactCommon/cxxreact/JSCNativeModules.cpp +++ b/ReactCommon/cxxreact/JSCNativeModules.cpp @@ -11,6 +11,10 @@ JSCNativeModules::JSCNativeModules(std::shared_ptr moduleRegistr m_moduleRegistry(std::move(moduleRegistry)) {} JSValueRef JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName) { + if (!m_moduleRegistry) { + return Value::makeUndefined(context); + } + std::string moduleName = String::ref(context, jsName).str(); const auto it = m_objects.find(moduleName); diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 56a71adfd2281e..5c4399766c0704 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -47,6 +47,9 @@ class JsToNativeBridge : public react::ExecutorDelegate { void callNativeModules( JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override { + + CHECK(m_registry || calls.empty()) << + "native module calls cannot be completed with no native modules"; ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor); m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable { // An exception anywhere in here stops processing of the batch. This @@ -95,13 +98,8 @@ NativeToJsBridge::NativeToJsBridge( std::shared_ptr callback) : m_destroyed(std::make_shared(false)) , m_mainExecutorToken(callback->createExecutorToken()) - , m_delegate(registry - ? std::make_shared( - this, registry, std::move(nativeQueue), callback) - : nullptr) { - if (!m_delegate) { - nativeQueue->quitSynchronous(); - } + , m_delegate(std::make_shared( + this, registry, std::move(nativeQueue), callback)) { std::unique_ptr mainExecutor = jsExecutorFactory->createJSExecutor(m_delegate, jsQueue); // cached to avoid locked map lookup in the common case @@ -318,9 +316,7 @@ ExecutorToken NativeToJsBridge::getTokenForExecutor(JSExecutor& executor) { } void NativeToJsBridge::destroy() { - if (m_delegate) { - m_delegate->quitQueueSynchronous(); - } + m_delegate->quitQueueSynchronous(); auto* executorMessageQueueThread = getMessageQueueThread(m_mainExecutorToken); // All calls made through runOnExecutorQueue have an early exit if // m_destroyed is true. Setting this before the runOnQueueSync will cause diff --git a/ReactCommon/jschelpers/Value.h b/ReactCommon/jschelpers/Value.h index f2879bd1d4b9ba..b5d79c53df283f 100644 --- a/ReactCommon/jschelpers/Value.h +++ b/ReactCommon/jschelpers/Value.h @@ -305,6 +305,10 @@ class Value : public noncopyable { return Value(ctx, JSC_JSValueMakeUndefined(ctx)); } + static Value makeNull(JSContextRef ctx) { + return Value(ctx, JSC_JSValueMakeNull(ctx)); + } + std::string toJSONString(unsigned indent = 0) const; static Value fromJSON(JSContextRef ctx, const String& json); static JSValueRef fromDynamic(JSContextRef ctx, const folly::dynamic& value); From 87137cadd7ba5c23c4962f01bb4a73347f6cb835 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Tue, 14 Mar 2017 15:28:55 -0700 Subject: [PATCH 029/366] rename javaModules -> nativeModules Reviewed By: AaaChiuuu Differential Revision: D4631758 fbshipit-source-id: 3b62ada1bf32467b67cd8529d4b7873b43898ee4 --- .../main/java/com/facebook/react/CoreModulesPackage.java | 2 +- .../facebook/react/module/annotations/ReactModuleList.java | 6 +++--- .../react/module/processing/ReactModuleSpecProcessor.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java b/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java index d9ff55e0dbe566..2827c47203bb8c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java @@ -56,7 +56,7 @@ * view managers from). */ @ReactModuleList( - javaModules = { + nativeModules = { AndroidInfoModule.class, AnimationsDebugModule.class, DeviceEventManagerModule.class, diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/annotations/ReactModuleList.java b/ReactAndroid/src/main/java/com/facebook/react/module/annotations/ReactModuleList.java index bf7cd7d9cd0914..fc47945ac9be8d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/annotations/ReactModuleList.java +++ b/ReactAndroid/src/main/java/com/facebook/react/module/annotations/ReactModuleList.java @@ -19,10 +19,10 @@ public @interface ReactModuleList { /** - * The Java modules in this list should be annotated with {@link ReactModule}. - * @return List of Java modules in the package. + * The Native modules in this list should be annotated with {@link ReactModule}. + * @return List of Native modules in the package. */ - Class[] javaModules(); + Class[] nativeModules(); /** * The View Managers in this list should be annotated with {@link ReactModule}. diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java b/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java index cf585894ba5800..add1ef0b6ef904 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java +++ b/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java @@ -87,7 +87,7 @@ public boolean process(Set annotations, RoundEnvironment ReactModuleList reactModuleList = typeElement.getAnnotation(ReactModuleList.class); List nativeModules = new ArrayList<>(); try { - reactModuleList.javaModules(); // throws MirroredTypesException + reactModuleList.nativeModules(); // throws MirroredTypesException } catch (MirroredTypesException mirroredTypesException) { List typeMirrors = mirroredTypesException.getTypeMirrors(); for (TypeMirror typeMirror : typeMirrors) { From 957b55c769661edeee691109e45dc99a06f87bc4 Mon Sep 17 00:00:00 2001 From: Petter Hesselberg Date: Tue, 14 Mar 2017 16:06:57 -0700 Subject: [PATCH 030/366] Don't crash in StackTraceHelper.convertJsStackTrace Summary: Don't crash in `StackTraceHelper.convertJsStackTrace` on missing `lineNumber` I've seen the native Android app crash upon displaying a stack trace because of this bug. Incidental and unrelated change: Refactor `StackTraceHelper.formatFrameSource` to use `StringBuilder` Closes https://github.com/facebook/react-native/pull/12920 Differential Revision: D4709273 fbshipit-source-id: 95b3eb303f259460e33d8ee32a2db652360c645d --- .../react/devsupport/StackTraceHelper.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java index ef4c6c0cdfb177..93c6833415c683 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java @@ -22,6 +22,9 @@ */ public class StackTraceHelper { + public static final java.lang.String COLUMN_KEY = "column"; + public static final java.lang.String LINE_NUMBER_KEY = "lineNumber"; + /** * Represents a generic entry in a stack trace, be it originally from JS or Java. */ @@ -101,10 +104,13 @@ public static StackFrame[] convertJsStackTrace(@Nullable ReadableArray stack) { ReadableMap frame = stack.getMap(i); String methodName = frame.getString("methodName"); String fileName = frame.getString("file"); - int lineNumber = frame.getInt("lineNumber"); + int lineNumber = -1; + if (frame.hasKey(LINE_NUMBER_KEY) && !frame.isNull(LINE_NUMBER_KEY)) { + lineNumber = frame.getInt(LINE_NUMBER_KEY); + } int columnNumber = -1; - if (frame.hasKey("column") && !frame.isNull("column")) { - columnNumber = frame.getInt("column"); + if (frame.hasKey(COLUMN_KEY) && !frame.isNull(COLUMN_KEY)) { + columnNumber = frame.getInt(COLUMN_KEY); } result[i] = new StackFrameImpl(fileName, methodName, lineNumber, columnNumber); } @@ -132,12 +138,17 @@ public static StackFrame[] convertJavaStackTrace(Throwable exception) { * Format a {@link StackFrame} to a String (method name is not included). */ public static String formatFrameSource(StackFrame frame) { - String lineInfo = ""; - final int column = frame.getColumn(); - // If the column is 0, don't show it in red box. - final String columnString = column <= 0 ? "" : ":" + column; - lineInfo += frame.getFileName() + ":" + frame.getLine() + columnString; - return lineInfo; + StringBuilder lineInfo = new StringBuilder(); + lineInfo.append(frame.getFileName()); + final int line = frame.getLine(); + if (line > 0) { + lineInfo.append(":").append(line); + final int column = frame.getColumn(); + if (column > 0) { + lineInfo.append(":").append(column); + } + } + return lineInfo.toString(); } /** From a0b5a6efe55f5413048c6a752ba4e90c79ac7137 Mon Sep 17 00:00:00 2001 From: "tfallon@mail.depaul.edu" Date: Tue, 14 Mar 2017 16:20:04 -0700 Subject: [PATCH 031/366] Explaining how to package ReactNative for Nexus/Maven deployment. Summary: **Motivation** I needed to do this today. It's explained in code in the repository, but not included in the actual documentation. As React Native continues to grow, this is something that will be used more and more by the community. **Test plan (required)** No test plan required, this is simply a documentation change to add things that are only explained in code comments currently. Closes https://github.com/facebook/react-native/pull/12938 Differential Revision: D4709339 Pulled By: mkonicek fbshipit-source-id: a56c7573c3fa25a4059657b95b482b641ff229ff --- docs/AndroidBuildingFromSource.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/AndroidBuildingFromSource.md b/docs/AndroidBuildingFromSource.md index d6ebe9ae0f6237..326828a7f6e366 100644 --- a/docs/AndroidBuildingFromSource.md +++ b/docs/AndroidBuildingFromSource.md @@ -143,6 +143,18 @@ gradle.projectsLoaded { } ``` +## Building for Maven/Nexus deployment + +If you find that you need to push up a locally compiled React Native .aar and related files to a remote Nexus repository, you can. + +Start by following the `Point Gradle to your Android SDK` section of this page. Once you do this, assuming you have Gradle configured properly, you can then run the following command from the root of your React Native checkout to build and package all required files: + +``` +./gradlew ReactAndroid:installArchives +``` + +This will package everything that would typically be included in the `android` directory of your `node_modules/react-native/` installation in the root directory of your React Native checkout. + ## Testing If you made changes to React Native and submit a pull request, all tests will run on your pull request automatically. To run the tests locally, see [Testing](docs/testing.html). From 0d5620bf5e8e07ee1951d84baf6ca6061257cdb1 Mon Sep 17 00:00:00 2001 From: sunnylqm Date: Tue, 14 Mar 2017 16:33:11 -0700 Subject: [PATCH 032/366] fix a typo in navigation doc Summary: Closes https://github.com/facebook/react-native/pull/12921 Differential Revision: D4709474 Pulled By: mkonicek fbshipit-source-id: 3c7da6ee87d350d52074a05abe168345f936858f --- docs/Navigation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Navigation.md b/docs/Navigation.md index 4be9f26f676789..b245f079fdf876 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -56,7 +56,7 @@ class MainScreen extends React.Component { } ``` -React Navigation routers make it easy to override navigation logic or integrate it into redux. Because routers can be nested inside eachother, developers can override navigation logic for one area of the app without making widespread changes. +React Navigation routers make it easy to override navigation logic or integrate it into redux. Because routers can be nested inside each other, developers can override navigation logic for one area of the app without making widespread changes. The views in React Navigation use native components and the `Animated` library to deliver 60fps animations that are run on the native thread. Plus, the animations and gestures can be easily customized. From 22151d28d4ac538a37f399f9187a59a0236f401e Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Tue, 14 Mar 2017 16:39:47 -0700 Subject: [PATCH 033/366] Fix ReactCxx xcodeproj Summary: The ReactCxx project was not updated when RCTMap was removed in 48f30eca7e3d4c239501de515a7cc35615ed6bd1, this fixes it. It was also missing the systemJSCWrapper.cpp file. **Test plan** Tested that UIExplorerCxx now builds cc mkonicek Closes https://github.com/facebook/react-native/pull/12919 Differential Revision: D4709498 Pulled By: mkonicek fbshipit-source-id: 995a1a914ab0a3227b1219c575a84d136800ff19 --- React/ReactCxx.xcodeproj/project.pbxproj | 84 ++---------------------- 1 file changed, 4 insertions(+), 80 deletions(-) diff --git a/React/ReactCxx.xcodeproj/project.pbxproj b/React/ReactCxx.xcodeproj/project.pbxproj index 1c3ebe943e2f52..1d9ac80584e181 100644 --- a/React/ReactCxx.xcodeproj/project.pbxproj +++ b/React/ReactCxx.xcodeproj/project.pbxproj @@ -61,7 +61,6 @@ 1339578B1DF76D3500EC27BE /* Yoga.h in Headers */ = {isa = PBXBuildFile; fileRef = 130A77081DF767AF001F9587 /* Yoga.h */; }; 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */; }; 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; }; - 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; }; 134FCB3D1A6E7F0800051CC8 /* RCTJSCExecutor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */; }; 13513F3C1B1F43F400FCE529 /* RCTProgressViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13513F3B1B1F43F400FCE529 /* RCTProgressViewManager.m */; }; 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; }; @@ -124,7 +123,6 @@ 13AB5E021DF777F2001A8C30 /* Yoga.c in Sources */ = {isa = PBXBuildFile; fileRef = 130A77071DF767AF001F9587 /* Yoga.c */; }; 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AB90C01B6FA36700713B4F /* RCTComponentData.m */; }; 13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AF20441AE707F9005F5298 /* RCTSlider.m */; }; - 13AFBCA01C07247D00BBAEAA /* RCTMapOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AFBC9F1C07247D00BBAEAA /* RCTMapOverlay.m */; }; 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; }; 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */; }; 13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEE1A69327A00A75B9A /* RCTTiming.m */; }; @@ -136,7 +134,6 @@ 13B0801D1A69489C00A75B9A /* RCTNavItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080131A69489C00A75B9A /* RCTNavItemManager.m */; }; 13B080201A69489C00A75B9A /* RCTActivityIndicatorViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080191A69489C00A75B9A /* RCTActivityIndicatorViewManager.m */; }; 13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */; }; - 13B202041BFB948C00C07393 /* RCTMapAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B202031BFB948C00C07393 /* RCTMapAnnotation.m */; }; 13BB3D021BECD54500932C10 /* RCTImageSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BB3D011BECD54500932C10 /* RCTImageSource.m */; }; 13BCE8091C99CB9D00DD7AAD /* RCTRootShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BCE8081C99CB9D00DD7AAD /* RCTRootShadowView.m */; }; 13C156051AB1A2840079392D /* RCTWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156021AB1A2840079392D /* RCTWebView.m */; }; @@ -215,8 +212,6 @@ 13F8879F1E29741900C3C7A1 /* BitsFunctexcept.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F8879C1E29740700C3C7A1 /* BitsFunctexcept.cpp */; }; 13F887A21E2977FF00C3C7A1 /* MallocImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F887A01E2977D800C3C7A1 /* MallocImpl.cpp */; }; 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 */; }; 1450FF861BCFF28A00208362 /* RCTProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF811BCFF28A00208362 /* RCTProfile.m */; }; 1450FF871BCFF28A00208362 /* RCTProfileTrampoline-arm.S in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF821BCFF28A00208362 /* RCTProfileTrampoline-arm.S */; }; 1450FF881BCFF28A00208362 /* RCTProfileTrampoline-arm64.S in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF831BCFF28A00208362 /* RCTProfileTrampoline-arm64.S */; }; @@ -233,6 +228,7 @@ 14F7A0F01BDA714B003C6C10 /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */; }; 191E3EBE1C29D9AF00C180A6 /* RCTRefreshControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */; }; 191E3EC11C29DC3800C180A6 /* RCTRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */; }; + 19DED2291E77E29200F089BB /* systemJSCWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */; }; 27595AA31E575C7800CCE2B1 /* CxxMessageQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */; }; 27595AA41E575C7800CCE2B1 /* CxxModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A71E03699D0018521A /* CxxModule.h */; }; 27595AA51E575C7800CCE2B1 /* CxxNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A91E03699D0018521A /* CxxNativeModule.h */; }; @@ -338,12 +334,7 @@ 2D3B5EC91D9B095C00451313 /* RCTBorderDrawing.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */; }; 2D3B5ECA1D9B095F00451313 /* RCTComponentData.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AB90C01B6FA36700713B4F /* RCTComponentData.m */; }; 2D3B5ECB1D9B096200451313 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; }; - 2D3B5ECC1D9B096500451313 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; }; 2D3B5ECF1D9B096F00451313 /* RCTFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D37B5811D522B190042D5B5 /* RCTFont.mm */; }; - 2D3B5ED01D9B097200451313 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; }; - 2D3B5ED11D9B097500451313 /* RCTMapAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B202031BFB948C00C07393 /* RCTMapAnnotation.m */; }; - 2D3B5ED21D9B097800451313 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; }; - 2D3B5ED31D9B097B00451313 /* RCTMapOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AFBC9F1C07247D00BBAEAA /* RCTMapOverlay.m */; }; 2D3B5ED41D9B097D00451313 /* RCTModalHostView.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A1FE8B1B62640A00BE0E65 /* RCTModalHostView.m */; }; 2D3B5ED51D9B098000451313 /* RCTModalHostViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83392EB21B6634E10013B15F /* RCTModalHostViewController.m */; }; 2D3B5ED61D9B098400451313 /* RCTModalHostViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A1FE8E1B62643A00BE0E65 /* RCTModalHostViewManager.m */; }; @@ -454,12 +445,7 @@ 3D302F701DF828F800D6DDAE /* RCTComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C325281AA63B6A0048765F /* RCTComponent.h */; }; 3D302F711DF828F800D6DDAE /* RCTComponentData.h in Headers */ = {isa = PBXBuildFile; fileRef = 13AB90BF1B6FA36700713B4F /* RCTComponentData.h */; }; 3D302F721DF828F800D6DDAE /* RCTConvert+CoreLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */; }; - 3D302F731DF828F800D6DDAE /* RCTConvert+MapKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */; }; 3D302F761DF828F800D6DDAE /* RCTFont.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D37B5801D522B190042D5B5 /* RCTFont.h */; }; - 3D302F771DF828F800D6DDAE /* RCTMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 14435CE11AAC4AE100FC20F4 /* RCTMap.h */; }; - 3D302F781DF828F800D6DDAE /* RCTMapAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */; }; - 3D302F791DF828F800D6DDAE /* RCTMapManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */; }; - 3D302F7A1DF828F800D6DDAE /* RCTMapOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 13AFBC9E1C07247D00BBAEAA /* RCTMapOverlay.h */; }; 3D302F7B1DF828F800D6DDAE /* RCTModalHostView.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8A1B62640A00BE0E65 /* RCTModalHostView.h */; }; 3D302F7C1DF828F800D6DDAE /* RCTModalHostViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 83392EB11B6634E10013B15F /* RCTModalHostViewController.h */; }; 3D302F7D1DF828F800D6DDAE /* RCTModalHostViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8D1B62643A00BE0E65 /* RCTModalHostViewManager.h */; }; @@ -594,14 +580,9 @@ 3D80D96B1DF6FA890028D040 /* RCTComponent.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13C325281AA63B6A0048765F /* RCTComponent.h */; }; 3D80D96C1DF6FA890028D040 /* RCTComponentData.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13AB90BF1B6FA36700713B4F /* RCTComponentData.h */; }; 3D80D96D1DF6FA890028D040 /* RCTConvert+CoreLocation.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */; }; - 3D80D96E1DF6FA890028D040 /* RCTConvert+MapKit.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */; }; 3D80D96F1DF6FA890028D040 /* RCTDatePicker.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */; }; 3D80D9701DF6FA890028D040 /* RCTDatePickerManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */; }; 3D80D9711DF6FA890028D040 /* RCTFont.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D37B5801D522B190042D5B5 /* RCTFont.h */; }; - 3D80D9721DF6FA890028D040 /* RCTMap.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14435CE11AAC4AE100FC20F4 /* RCTMap.h */; }; - 3D80D9731DF6FA890028D040 /* RCTMapAnnotation.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */; }; - 3D80D9741DF6FA890028D040 /* RCTMapManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */; }; - 3D80D9751DF6FA890028D040 /* RCTMapOverlay.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13AFBC9E1C07247D00BBAEAA /* RCTMapOverlay.h */; }; 3D80D9761DF6FA890028D040 /* RCTModalHostView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8A1B62640A00BE0E65 /* RCTModalHostView.h */; }; 3D80D9771DF6FA890028D040 /* RCTModalHostViewController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83392EB11B6634E10013B15F /* RCTModalHostViewController.h */; }; 3D80D9781DF6FA890028D040 /* RCTModalHostViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8D1B62643A00BE0E65 /* RCTModalHostViewManager.h */; }; @@ -717,14 +698,9 @@ 3D80DA651DF820620028D040 /* RCTComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C325281AA63B6A0048765F /* RCTComponent.h */; }; 3D80DA661DF820620028D040 /* RCTComponentData.h in Headers */ = {isa = PBXBuildFile; fileRef = 13AB90BF1B6FA36700713B4F /* RCTComponentData.h */; }; 3D80DA671DF820620028D040 /* RCTConvert+CoreLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */; }; - 3D80DA681DF820620028D040 /* RCTConvert+MapKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */; }; 3D80DA691DF820620028D040 /* RCTDatePicker.h in Headers */ = {isa = PBXBuildFile; fileRef = 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */; }; 3D80DA6A1DF820620028D040 /* RCTDatePickerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */; }; 3D80DA6B1DF820620028D040 /* RCTFont.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D37B5801D522B190042D5B5 /* RCTFont.h */; }; - 3D80DA6C1DF820620028D040 /* RCTMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 14435CE11AAC4AE100FC20F4 /* RCTMap.h */; }; - 3D80DA6D1DF820620028D040 /* RCTMapAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */; }; - 3D80DA6E1DF820620028D040 /* RCTMapManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */; }; - 3D80DA6F1DF820620028D040 /* RCTMapOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 13AFBC9E1C07247D00BBAEAA /* RCTMapOverlay.h */; }; 3D80DA701DF820620028D040 /* RCTModalHostView.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8A1B62640A00BE0E65 /* RCTModalHostView.h */; }; 3D80DA711DF820620028D040 /* RCTModalHostViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 83392EB11B6634E10013B15F /* RCTModalHostViewController.h */; }; 3D80DA721DF820620028D040 /* RCTModalHostViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8D1B62643A00BE0E65 /* RCTModalHostViewManager.h */; }; @@ -871,15 +847,10 @@ 3DA982051E5B0F7F004F2374 /* RCTComponent.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13C325281AA63B6A0048765F /* RCTComponent.h */; }; 3DA982061E5B0F7F004F2374 /* RCTComponentData.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13AB90BF1B6FA36700713B4F /* RCTComponentData.h */; }; 3DA982071E5B0F7F004F2374 /* RCTConvert+CoreLocation.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */; }; - 3DA982081E5B0F7F004F2374 /* RCTConvert+MapKit.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */; }; 3DA982091E5B0F7F004F2374 /* RCTConvert+Transform.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 130443C31E401A8C00D93A67 /* RCTConvert+Transform.h */; }; 3DA9820A1E5B0F7F004F2374 /* RCTDatePicker.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */; }; 3DA9820B1E5B0F7F004F2374 /* RCTDatePickerManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */; }; 3DA9820C1E5B0F7F004F2374 /* RCTFont.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D37B5801D522B190042D5B5 /* RCTFont.h */; }; - 3DA9820D1E5B0F7F004F2374 /* RCTMap.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14435CE11AAC4AE100FC20F4 /* RCTMap.h */; }; - 3DA9820E1E5B0F7F004F2374 /* RCTMapAnnotation.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */; }; - 3DA9820F1E5B0F7F004F2374 /* RCTMapManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */; }; - 3DA982101E5B0F7F004F2374 /* RCTMapOverlay.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13AFBC9E1C07247D00BBAEAA /* RCTMapOverlay.h */; }; 3DA982111E5B0F7F004F2374 /* RCTModalHostView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8A1B62640A00BE0E65 /* RCTModalHostView.h */; }; 3DA982121E5B0F7F004F2374 /* RCTModalHostViewController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83392EB11B6634E10013B15F /* RCTModalHostViewController.h */; }; 3DA982131E5B0F7F004F2374 /* RCTModalHostViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83A1FE8D1B62643A00BE0E65 /* RCTModalHostViewManager.h */; }; @@ -1122,15 +1093,10 @@ 3DA982051E5B0F7F004F2374 /* RCTComponent.h in Copy Headers */, 3DA982061E5B0F7F004F2374 /* RCTComponentData.h in Copy Headers */, 3DA982071E5B0F7F004F2374 /* RCTConvert+CoreLocation.h in Copy Headers */, - 3DA982081E5B0F7F004F2374 /* RCTConvert+MapKit.h in Copy Headers */, 3DA982091E5B0F7F004F2374 /* RCTConvert+Transform.h in Copy Headers */, 3DA9820A1E5B0F7F004F2374 /* RCTDatePicker.h in Copy Headers */, 3DA9820B1E5B0F7F004F2374 /* RCTDatePickerManager.h in Copy Headers */, 3DA9820C1E5B0F7F004F2374 /* RCTFont.h in Copy Headers */, - 3DA9820D1E5B0F7F004F2374 /* RCTMap.h in Copy Headers */, - 3DA9820E1E5B0F7F004F2374 /* RCTMapAnnotation.h in Copy Headers */, - 3DA9820F1E5B0F7F004F2374 /* RCTMapManager.h in Copy Headers */, - 3DA982101E5B0F7F004F2374 /* RCTMapOverlay.h in Copy Headers */, 3DA982111E5B0F7F004F2374 /* RCTModalHostView.h in Copy Headers */, 3DA982121E5B0F7F004F2374 /* RCTModalHostViewController.h in Copy Headers */, 3DA982131E5B0F7F004F2374 /* RCTModalHostViewManager.h in Copy Headers */, @@ -1367,14 +1333,9 @@ 3D80D96B1DF6FA890028D040 /* RCTComponent.h in Copy Headers */, 3D80D96C1DF6FA890028D040 /* RCTComponentData.h in Copy Headers */, 3D80D96D1DF6FA890028D040 /* RCTConvert+CoreLocation.h in Copy Headers */, - 3D80D96E1DF6FA890028D040 /* RCTConvert+MapKit.h in Copy Headers */, 3D80D96F1DF6FA890028D040 /* RCTDatePicker.h in Copy Headers */, 3D80D9701DF6FA890028D040 /* RCTDatePickerManager.h in Copy Headers */, 3D80D9711DF6FA890028D040 /* RCTFont.h in Copy Headers */, - 3D80D9721DF6FA890028D040 /* RCTMap.h in Copy Headers */, - 3D80D9731DF6FA890028D040 /* RCTMapAnnotation.h in Copy Headers */, - 3D80D9741DF6FA890028D040 /* RCTMapManager.h in Copy Headers */, - 3D80D9751DF6FA890028D040 /* RCTMapOverlay.h in Copy Headers */, 3D80D9761DF6FA890028D040 /* RCTModalHostView.h in Copy Headers */, 3D80D9771DF6FA890028D040 /* RCTModalHostViewController.h in Copy Headers */, 3D80D9781DF6FA890028D040 /* RCTModalHostViewManager.h in Copy Headers */, @@ -1538,8 +1499,6 @@ 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; }; 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+CoreLocation.h"; sourceTree = ""; }; 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CoreLocation.m"; sourceTree = ""; }; - 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+MapKit.h"; sourceTree = ""; }; - 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+MapKit.m"; sourceTree = ""; }; 1345A83A1B265A0E00583190 /* RCTURLRequestDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTURLRequestDelegate.h; sourceTree = ""; }; 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTURLRequestHandler.h; sourceTree = ""; }; 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTJSCExecutor.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -1626,8 +1585,6 @@ 13AF1F851AE6E777005F5298 /* RCTDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; 13AF20431AE707F8005F5298 /* RCTSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSlider.h; sourceTree = ""; }; 13AF20441AE707F9005F5298 /* RCTSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSlider.m; sourceTree = ""; }; - 13AFBC9E1C07247D00BBAEAA /* RCTMapOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMapOverlay.h; sourceTree = ""; }; - 13AFBC9F1C07247D00BBAEAA /* RCTMapOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapOverlay.m; sourceTree = ""; }; 13AFBCA11C07287B00BBAEAA /* RCTBridgeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeMethod.h; sourceTree = ""; }; 13AFBCA21C07287B00BBAEAA /* RCTRootViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootViewDelegate.h; sourceTree = ""; }; 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAlertManager.h; sourceTree = ""; }; @@ -1652,8 +1609,6 @@ 13B080191A69489C00A75B9A /* RCTActivityIndicatorViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActivityIndicatorViewManager.m; sourceTree = ""; }; 13B080231A694A8400A75B9A /* RCTWrapperViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWrapperViewController.h; sourceTree = ""; }; 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = ""; }; - 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMapAnnotation.h; sourceTree = ""; }; - 13B202031BFB948C00C07393 /* RCTMapAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapAnnotation.m; sourceTree = ""; }; 13BB3D001BECD54500932C10 /* RCTImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTImageSource.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 13BB3D011BECD54500932C10 /* RCTImageSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageSource.m; sourceTree = ""; }; 13BCE8071C99CB9D00DD7AAD /* RCTRootShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootShadowView.h; sourceTree = ""; }; @@ -1694,10 +1649,6 @@ 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 = ""; }; - 14435CE11AAC4AE100FC20F4 /* RCTMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMap.h; sourceTree = ""; }; - 14435CE21AAC4AE100FC20F4 /* RCTMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMap.m; sourceTree = ""; }; - 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMapManager.h; sourceTree = ""; }; - 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = ""; }; 1450FF801BCFF28A00208362 /* RCTProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTProfile.h; sourceTree = ""; }; 1450FF811BCFF28A00208362 /* RCTProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTProfile.m; sourceTree = ""; }; 1450FF821BCFF28A00208362 /* RCTProfileTrampoline-arm.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "RCTProfileTrampoline-arm.S"; sourceTree = ""; }; @@ -1728,6 +1679,7 @@ 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControlManager.m; sourceTree = ""; }; 191E3EBF1C29DC3800C180A6 /* RCTRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControl.h; sourceTree = ""; }; 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControl.m; sourceTree = ""; }; + 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = systemJSCWrapper.cpp; path = ../jschelpers/systemJSCWrapper.cpp; sourceTree = ""; }; 27B958731E57587D0096647A /* JSBigString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBigString.cpp; sourceTree = ""; }; 2D2A28131D9B038B00D4039D /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; 352DCFEE1D19F4C20056D623 /* RCTI18nUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTI18nUtil.h; sourceTree = ""; }; @@ -2141,8 +2093,6 @@ 13AB90C01B6FA36700713B4F /* RCTComponentData.m */, 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */, 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */, - 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */, - 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */, 130443C31E401A8C00D93A67 /* RCTConvert+Transform.h */, 130443C41E401A8C00D93A67 /* RCTConvert+Transform.m */, 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */, @@ -2151,14 +2101,6 @@ 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */, 3D37B5801D522B190042D5B5 /* RCTFont.h */, 3D37B5811D522B190042D5B5 /* RCTFont.mm */, - 14435CE11AAC4AE100FC20F4 /* RCTMap.h */, - 14435CE21AAC4AE100FC20F4 /* RCTMap.m */, - 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */, - 13B202031BFB948C00C07393 /* RCTMapAnnotation.m */, - 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */, - 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */, - 13AFBC9E1C07247D00BBAEAA /* RCTMapOverlay.h */, - 13AFBC9F1C07247D00BBAEAA /* RCTMapOverlay.m */, 83A1FE8A1B62640A00BE0E65 /* RCTModalHostView.h */, 83A1FE8B1B62640A00BE0E65 /* RCTModalHostView.m */, 83392EB11B6634E10013B15F /* RCTModalHostViewController.h */, @@ -2517,6 +2459,7 @@ 3D7454791E54757500E74ADD /* RecoverableError.h */, 3D92B0D31E03699D0018521A /* SampleCxxModule.cpp */, 3D92B0D41E03699D0018521A /* SampleCxxModule.h */, + 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */, 3D92B0D51E03699D0018521A /* SystraceSection.h */, ); path = cxxreact; @@ -2635,12 +2578,7 @@ 3D302F701DF828F800D6DDAE /* RCTComponent.h in Headers */, 3D302F711DF828F800D6DDAE /* RCTComponentData.h in Headers */, 3D302F721DF828F800D6DDAE /* RCTConvert+CoreLocation.h in Headers */, - 3D302F731DF828F800D6DDAE /* RCTConvert+MapKit.h in Headers */, 3D302F761DF828F800D6DDAE /* RCTFont.h in Headers */, - 3D302F771DF828F800D6DDAE /* RCTMap.h in Headers */, - 3D302F781DF828F800D6DDAE /* RCTMapAnnotation.h in Headers */, - 3D302F791DF828F800D6DDAE /* RCTMapManager.h in Headers */, - 3D302F7A1DF828F800D6DDAE /* RCTMapOverlay.h in Headers */, 3D302F7B1DF828F800D6DDAE /* RCTModalHostView.h in Headers */, 3D302F7C1DF828F800D6DDAE /* RCTModalHostViewController.h in Headers */, 3D302F7D1DF828F800D6DDAE /* RCTModalHostViewManager.h in Headers */, @@ -2907,16 +2845,11 @@ 3D80DA661DF820620028D040 /* RCTComponentData.h in Headers */, 3DA9819E1E5B0DBB004F2374 /* NSDataBigString.h in Headers */, 3D80DA671DF820620028D040 /* RCTConvert+CoreLocation.h in Headers */, - 3D80DA681DF820620028D040 /* RCTConvert+MapKit.h in Headers */, 3D80DA691DF820620028D040 /* RCTDatePicker.h in Headers */, 3D80DA6A1DF820620028D040 /* RCTDatePickerManager.h in Headers */, 3D80DA6B1DF820620028D040 /* RCTFont.h in Headers */, 139D7EE41E25DBDC00323FB7 /* demangle.h in Headers */, - 3D80DA6C1DF820620028D040 /* RCTMap.h in Headers */, - 3D80DA6D1DF820620028D040 /* RCTMapAnnotation.h in Headers */, - 3D80DA6E1DF820620028D040 /* RCTMapManager.h in Headers */, 139D7E561E25C5A300323FB7 /* fast-dtoa.h in Headers */, - 3D80DA6F1DF820620028D040 /* RCTMapOverlay.h in Headers */, 3D80DA701DF820620028D040 /* RCTModalHostView.h in Headers */, 3D80DA711DF820620028D040 /* RCTModalHostViewController.h in Headers */, 3D80DA721DF820620028D040 /* RCTModalHostViewManager.h in Headers */, @@ -3337,7 +3270,6 @@ 3D80D91B1DF6F8200028D040 /* RCTPlatform.m in Sources */, 2DD0EFE11DA84F2800B0C975 /* RCTStatusBarManager.m in Sources */, 2D3B5EC91D9B095C00451313 /* RCTBorderDrawing.m in Sources */, - 2D3B5ED31D9B097B00451313 /* RCTMapOverlay.m in Sources */, 2D3B5E991D9B089A00451313 /* RCTDisplayLink.m in Sources */, 2D3B5EBF1D9B093300451313 /* RCTJSCProfiler.m in Sources */, 2D3B5EA11D9B08B600451313 /* RCTModuleData.mm in Sources */, @@ -3359,7 +3291,6 @@ 130443DF1E401B0D00D93A67 /* RCTTVView.m in Sources */, 130443A41E3FEAC600D93A67 /* RCTFollyConvert.mm in Sources */, 2D3B5EB71D9B091800451313 /* RCTRedBox.m in Sources */, - 2D3B5ED11D9B097500451313 /* RCTMapAnnotation.m in Sources */, 3D7AA9C61E548CDD001955CF /* NSDataBigString.mm in Sources */, 13134C8F1E296B2A00B9F3CB /* RCTMessageThread.mm in Sources */, 2D3B5EAF1D9B08FB00451313 /* RCTAccessibilityManager.m in Sources */, @@ -3375,7 +3306,6 @@ 130E3D8B1E6A083900ACE484 /* RCTDevSettings.mm in Sources */, 2D3B5ED81D9B098A00451313 /* RCTNavigatorManager.m in Sources */, 2D3B5E951D9B087C00451313 /* RCTAssert.m in Sources */, - 2D3B5ED21D9B097800451313 /* RCTMapManager.m in Sources */, 2D3B5EB61D9B091400451313 /* RCTExceptionsManager.m in Sources */, 2D3B5EEB1D9B09D000451313 /* RCTTabBarItem.m in Sources */, 2D3B5E961D9B088500451313 /* RCTBatchedBridge.m in Sources */, @@ -3389,7 +3319,6 @@ 13134C931E296B2A00B9F3CB /* RCTNativeModule.mm in Sources */, 2D3B5ECB1D9B096200451313 /* RCTConvert+CoreLocation.m in Sources */, 2D3B5EEE1D9B09DA00451313 /* RCTView.m in Sources */, - 2D3B5ECC1D9B096500451313 /* RCTConvert+MapKit.m in Sources */, 2D3B5E981D9B089500451313 /* RCTConvert.m in Sources */, 2D3B5EA71D9B08CE00451313 /* RCTTouchHandler.m in Sources */, 3D05745A1DE5FFF500184BB4 /* RCTJavaScriptLoader.mm in Sources */, @@ -3407,7 +3336,6 @@ 130443DB1E401ADD00D93A67 /* RCTConvert+Transform.m in Sources */, 2D3B5EC61D9B095000451313 /* RCTProfileTrampoline-x86_64.S in Sources */, 3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */, - 2D3B5ED01D9B097200451313 /* RCTMap.m in Sources */, 2D3B5EA61D9B08CA00451313 /* RCTTouchEvent.m in Sources */, 2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */, 2D3B5EF01D9B09E300451313 /* RCTWrapperViewController.m in Sources */, @@ -3535,7 +3463,6 @@ files = ( 13134C9A1E296B2A00B9F3CB /* RCTCxxMethod.mm in Sources */, 59FBEFB61E46D91C0095D885 /* RCTScrollContentViewManager.m in Sources */, - 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */, 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */, 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */, 14A43DF31C20B1C900794BC8 /* RCTJSCProfiler.m in Sources */, @@ -3551,7 +3478,6 @@ 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */, 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */, 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */, - 13B202041BFB948C00C07393 /* RCTMapAnnotation.m in Sources */, 13A0C28A1B74F71200B29F6F /* RCTDevMenu.m in Sources */, 13BCE8091C99CB9D00DD7AAD /* RCTRootShadowView.m in Sources */, 14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */, @@ -3609,14 +3535,12 @@ 13A6E20E1C19AA0C00845B82 /* RCTParserUtils.m in Sources */, 13E067571A70F44B002CDEE1 /* RCTView.m in Sources */, 3D7749441DC1065C007EC8D8 /* RCTPlatform.m in Sources */, - 13AFBCA01C07247D00BBAEAA /* RCTMapOverlay.m in Sources */, 13D9FEEE1CDCD93000158BD7 /* RCTKeyboardObserver.m in Sources */, B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */, 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */, 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */, 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */, 83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */, - 14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */, 130443C61E401A8C00D93A67 /* RCTConvert+Transform.m in Sources */, 191E3EC11C29DC3800C180A6 /* RCTRefreshControl.m in Sources */, 13C156051AB1A2840079392D /* RCTWebView.m in Sources */, @@ -3639,8 +3563,8 @@ 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */, 13F17A851B8493E5007D4C75 /* RCTRedBox.m in Sources */, 83392EB31B6634E10013B15F /* RCTModalHostViewController.m in Sources */, - 14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */, 13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */, + 19DED2291E77E29200F089BB /* systemJSCWrapper.cpp in Sources */, 83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */, 83A1FE8F1B62643A00BE0E65 /* RCTModalHostViewManager.m in Sources */, 13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */, From 6dab5dcd7cd8214311d5b8aaa447a9c549d43757 Mon Sep 17 00:00:00 2001 From: Hector Ramos Date: Tue, 14 Mar 2017 16:57:57 -0700 Subject: [PATCH 034/366] Link to Sketch and CRNA Summary: rnplay.org is shutting down on April 1st, 2017. Recommend instead using Expo's improved prototyping tool, Sketch. Closes https://github.com/facebook/react-native/pull/12935 Differential Revision: D4709631 Pulled By: mkonicek fbshipit-source-id: ebc020ca7973a23658dc3b63ad2ac81c2ecefe9c --- .github/ISSUE_TEMPLATE.md | 4 ++-- CONTRIBUTING.md | 2 +- docs/MoreResources.md | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index c7b878bad1c8c9..7acc05e46e1629 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -7,7 +7,7 @@ We use GitHub Issues for tracking bugs in React Native. Your issue may be closed without explanation if it does not provide the information required by this template. ---- Please use this template, and delete everything above this line before submitting your issue --- +--- Please use this template, and delete everything above this line before submitting your issue --- ### Description @@ -15,7 +15,7 @@ Your issue may be closed without explanation if it does not provide the informat ### Reproduction -[FILL THIS OUT: Try to reproduce your bug on rnplay.org and provide a link. If you can't reproduce the bug on rnplay.org, provide a sample project.] +[FILL THIS OUT: Try to reproduce your bug on https://sketch.expo.io/ and provide a link. If you can't reproduce the bug on Sketch, provide a sample project.] ### Solution diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53f7c5c889af66..d460766a81a844 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,7 +69,7 @@ We are using GitHub Issues for our public bugs. We keep a close eye on this and ### Reporting New Issues -The best way to get your bug fixed is to provide a reduced test case. Please provide either a public repository with a runnable example or a [React Native Playground](https://rnplay.org/) snippet. +The best way to get your bug fixed is to provide a reduced test case. Please provide either a public repository with a runnable example or a [Sketch](https://sketch.expo.io/). ### Security Bugs diff --git a/docs/MoreResources.md b/docs/MoreResources.md index cc32bb1b3bf664..3c3b849ab5f7d5 100644 --- a/docs/MoreResources.md +++ b/docs/MoreResources.md @@ -20,7 +20,7 @@ If you're looking for a library that does a specific thing, check out [Awesome R ## Example Apps -There are a lot of example apps at the [React Native Playground](https://rnplay.org/apps/picks). You can see the code running on a real device, which is a neat feature. +There are some example apps in the [`Examples/` directory](https://github.com/facebook/react-native/tree/master/Examples) on GitHub. You can run the apps on a simulator or device, and you can see the source code for these apps, which is neat. The folks who built the app for Facebook's F8 conference in 2016 also [open-sourced the code](https://github.com/fbsamples/f8app) and wrote up a [detailed series of tutorials](http://makeitopen.com/tutorials/building-the-f8-app/planning/). This is useful if you want a more in-depth example that's more realistic than most sample apps out there. @@ -28,6 +28,8 @@ The folks who built the app for Facebook's F8 conference in 2016 also [open-sour [Nuclide](https://nuclide.io/) is the IDE that Facebook uses internally for React Native development. The killer feature of Nuclide is its debugging ability. It also has great inline Flow support. +[Create React Native App](https://github.com/react-community/create-react-native-app) makes it significantly easier to get started with a React Native project. There's no need to use Xcode or Android Studio, and you can develop for your iOS device using Linux or Windows. + [Ignite](https://github.com/infinitered/ignite) is a starter kit that uses Redux and a few different common UI libraries. It has a CLI to generate apps, components, and containers. If you like all of the individual tech choices, Ignite could be perfect for you. [CodePush](https://microsoft.github.io/code-push/) is a service from Microsoft that makes it easy to deploy live updates to your React Native app. If you don't like going through the app store process to deploy little tweaks, and you also don't like setting up your own backend, give CodePush a try. From 1195a8f3e6d4a2c46d7d937547a4f95b5c4da79f Mon Sep 17 00:00:00 2001 From: ShiHui Date: Tue, 14 Mar 2017 17:45:39 -0700 Subject: [PATCH 035/366] Fix RCTPicker crash Summary: If user slide picker when picker item is zero, `UIPickerViewDelegate` will call `pickerView:didSelectRow:inComponent` row=0, `_items[row][@"value"]` will crash. Closes https://github.com/facebook/react-native/pull/12187 Differential Revision: D4709882 Pulled By: mkonicek fbshipit-source-id: 772c819d4eaef41ac983287877bda2918f40b1a7 --- React/Views/RCTPicker.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/Views/RCTPicker.m b/React/Views/RCTPicker.m index ce8598b48d4a0b..0e5295a9427e29 100644 --- a/React/Views/RCTPicker.m +++ b/React/Views/RCTPicker.m @@ -98,7 +98,7 @@ - (void)pickerView:(__unused UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(__unused NSInteger)component { _selectedIndex = row; - if (_onChange) { + if (_onChange && _items.count > row) { _onChange(@{ @"newIndex": @(row), @"newValue": RCTNullIfNil(_items[row][@"value"]), From 6cbb57d0dfedd30ffcd0e1b45e9bcca837f05329 Mon Sep 17 00:00:00 2001 From: Liu Zhanhong <275368990@qq.com> Date: Tue, 14 Mar 2017 19:18:12 -0700 Subject: [PATCH 036/366] BREAKING: Remove @provides support from packager Summary: see https://github.com/facebook/react-native/commit/d82f2553fb0da40b02ca5515767211105e2eec07 Closes https://github.com/facebook/react-native/pull/12355 Differential Revision: D4710001 Pulled By: davidaurelio fbshipit-source-id: 1c2225fa3bd2881c3eaedfd02514c29b920948b6 --- packager/src/ModuleGraph/worker/transform-module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/src/ModuleGraph/worker/transform-module.js b/packager/src/ModuleGraph/worker/transform-module.js index c451e03ffa1d10..1dca4e559bb26f 100644 --- a/packager/src/ModuleGraph/worker/transform-module.js +++ b/packager/src/ModuleGraph/worker/transform-module.js @@ -82,7 +82,7 @@ function transformModule( callback(null, { code, file: filename, - hasteID: annotations.providesModule || annotations.provide || null, + hasteID: annotations.providesModule || null, transformed, type: options.polyfill ? 'script' : 'module', }); From 18c239ee22adf55f9698c03066c033d7992bfaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20W=C3=B6hrl?= Date: Wed, 15 Mar 2017 05:20:38 -0700 Subject: [PATCH 037/366] Fix align-content: center, flex-end alignment with margin Summary: This fixes ```align-content: center``` and ```align-content: flex-end``` when the child exceeds the parents size. See #476. It also fixes those layouts if the child has ```margin: auto``` set. Closes https://github.com/facebook/yoga/pull/477 Differential Revision: D4697833 Pulled By: emilsjolander fbshipit-source-id: d081ec7ea559a5f2bd3271c3a4dc272960beddfa --- ReactCommon/yoga/yoga/Yoga.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ReactCommon/yoga/yoga/Yoga.c b/ReactCommon/yoga/yoga/Yoga.c index dd3ebd9d2b0472..323bb881545626 100644 --- a/ReactCommon/yoga/yoga/Yoga.c +++ b/ReactCommon/yoga/yoga/Yoga.c @@ -2624,10 +2624,6 @@ static void YGNodelayoutImpl(const YGNodeRef node, crossAxisParentSize, parentWidth) - paddingAndBorderAxisCross; - - if (measureModeCrossDim == YGMeasureModeAtMost) { - containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim); - } } // If there's no flex wrap, the cross dimension is defined by the container. @@ -2735,11 +2731,11 @@ static void YGNodelayoutImpl(const YGNodeRef node, if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto && YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) { - leadingCrossDim += remainingCrossDim / 2; + leadingCrossDim += fmaxf(0.0f, remainingCrossDim / 2); } else if (YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) { // No-Op } else if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto) { - leadingCrossDim += remainingCrossDim; + leadingCrossDim += fmaxf(0.0f, remainingCrossDim); } else if (alignItem == YGAlignFlexStart) { // No-Op } else if (alignItem == YGAlignCenter) { From 7098c450d8274e441135405adf4e83871c74a8af Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Wed, 15 Mar 2017 06:05:59 -0700 Subject: [PATCH 038/366] packager: attachHMRServer.js: Flow Summary: The breakage fixed by changeset [1] could have been identified earlier if we had typing on `attachHMRServer`, so I spent some time on that. This has revealed in turn a few functions across the codebase that were incorrectly typed, and that are now fixed. [1] packager: attachHMRServer.js: fix callsite of Server#getModuleForPath() Reviewed By: davidaurelio Differential Revision: D4706241 fbshipit-source-id: fc4285245921ae45d5781a47d626fc0559dba998 --- local-cli/server/util/attachHMRServer.js | 66 +++++++++++++++++------- packager/src/Bundler/index.js | 4 +- packager/src/Resolver/index.js | 2 +- packager/src/Server/index.js | 18 +++---- packager/src/node-haste/index.js | 5 +- 5 files changed, 62 insertions(+), 33 deletions(-) diff --git a/local-cli/server/util/attachHMRServer.js b/local-cli/server/util/attachHMRServer.js index 9e1dd4241fd132..02c4f45ece80e8 100644 --- a/local-cli/server/util/attachHMRServer.js +++ b/local-cli/server/util/attachHMRServer.js @@ -5,7 +5,10 @@ * 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. + * + * @flow */ + 'use strict'; const querystring = require('querystring'); @@ -13,15 +16,27 @@ const url = require('url'); const {getInverseDependencies} = require('../../../packager/src//node-haste'); +import type HMRBundle from '../../../packager/src/Bundler/HMRBundle'; +import type Server from '../../../packager/src/Server'; +import type ResolutionResponse from '../../../packager/src/node-haste/DependencyGraph/ResolutionResponse'; +import type Module from '../../../packager/src/node-haste/Module'; +import type {Server as HTTPServer} from 'http'; + const blacklist = [ 'Libraries/Utilities/HMRClient.js', ]; +type HMROptions = { + httpServer: HTTPServer, + path: string, + packagerServer: Server, +}; + /** * Attaches a WebSocket based connection to the Packager to expose * Hot Module Replacement updates to the simulator. */ -function attachHMRServer({httpServer, path, packagerServer}) { +function attachHMRServer({httpServer, path, packagerServer}: HMROptions) { let client = null; function disconnect() { @@ -33,18 +48,25 @@ function attachHMRServer({httpServer, path, packagerServer}) { // - The full list of dependencies. // - The shallow dependencies each file on the dependency list has // - Inverse shallow dependencies map - function getDependencies(platform, bundleEntry) { + function getDependencies(platform: string, bundleEntry: string): Promise<{ + dependenciesCache: Array, + dependenciesModulesCache: {[mixed]: Module}, + shallowDependencies: {[string]: Array}, + inverseDependenciesCache: mixed, + resolutionResponse: ResolutionResponse, + }> { return packagerServer.getDependencies({ platform: platform, dev: true, hot: true, entryFile: bundleEntry, }).then(response => { - const {getModuleId} = response; + /* $FlowFixMe: getModuleId might be null */ + const {getModuleId}: {getModuleId: () => number} = response; // for each dependency builds the object: // `{path: '/a/b/c.js', deps: ['modA', 'modB', ...]}` - return Promise.all(Object.values(response.dependencies).map(dep => { + return Promise.all(response.dependencies.map((dep: Module) => { return dep.getName().then(depName => { if (dep.isAsset() || dep.isJSON()) { return Promise.resolve({path: dep.path, deps: []}); @@ -54,23 +76,23 @@ function attachHMRServer({httpServer, path, packagerServer}) { dev: true, hot: true, entryFile: dep.path - }) - .then(deps => { - return { - path: dep.path, - name: depName, - deps, - }; - }); + }).then(deps => { + return { + path: dep.path, + name: depName, + deps, + }; + }); }); })) - .then(deps => { + .then((deps: Array<{path: string, name?: string, deps: Array}>) => { // list with all the dependencies' filenames the bundle entry has const dependenciesCache = response.dependencies.map(dep => dep.path); // map from module name to path const moduleToFilenameCache = Object.create(null); deps.forEach(dep => { + /* $FlowFixMe: `name` could be null, but `deps` would be as well. */ moduleToFilenameCache[dep.name] = dep.path; }); @@ -114,6 +136,7 @@ function attachHMRServer({httpServer, path, packagerServer}) { }); wss.on('connection', ws => { + /* $FlowFixMe: url might be null */ const params = querystring.parse(url.parse(ws.upgradeReq.url).query); getDependencies(params.platform, params.bundleEntry) @@ -195,11 +218,13 @@ function attachHMRServer({httpServer, path, packagerServer}) { return {}; } + const nonNullClient = client; + return packagerServer.getModuleForPath(filename).then(moduleToUpdate => { // build list of modules for which we'll send HMR updates const modulesToUpdate = [moduleToUpdate]; Object.keys(depsModulesCache).forEach(module => { - if (!client.dependenciesModulesCache[module]) { + if (!nonNullClient.dependenciesModulesCache[module]) { modulesToUpdate.push(depsModulesCache[module]); } }); @@ -215,10 +240,10 @@ function attachHMRServer({httpServer, path, packagerServer}) { modulesToUpdate.reverse(); // invalidate caches - client.dependenciesCache = depsCache; - client.dependenciesModulesCache = depsModulesCache; - client.shallowDependencies = shallowDeps; - client.inverseDependenciesCache = inverseDepsCache; + nonNullClient.dependenciesCache = depsCache; + nonNullClient.dependenciesModulesCache = depsModulesCache; + nonNullClient.shallowDependencies = shallowDeps; + nonNullClient.inverseDependenciesCache = inverseDepsCache; return resolutionResponse.copy({ dependencies: modulesToUpdate @@ -252,7 +277,7 @@ function attachHMRServer({httpServer, path, packagerServer}) { resolutionResponse, }, packagerHost, httpServerAddress.port); }) - .then(bundle => { + .then((bundle: HMRBundle) => { if (!client || !bundle || bundle.isEmpty()) { return; } @@ -299,6 +324,7 @@ function attachHMRServer({httpServer, path, packagerServer}) { }); promise.then(() => { + /* $FlowFixMe: assume `client` non-null */ client.ws.send(JSON.stringify({type: 'update-done'})); }); }); @@ -316,7 +342,7 @@ function attachHMRServer({httpServer, path, packagerServer}) { }); } -function arrayEquals(arrayA, arrayB) { +function arrayEquals(arrayA: Array, arrayB: Array): boolean { arrayA = arrayA || []; arrayB = arrayB || []; return ( diff --git a/packager/src/Bundler/index.js b/packager/src/Bundler/index.js index ce940a7f6cbf85..878e39ee26fd4b 100644 --- a/packager/src/Bundler/index.js +++ b/packager/src/Bundler/index.js @@ -277,7 +277,7 @@ class Bundler { ); } - hmrBundle(options: {platform: ?string}, host: string, port: number) { + hmrBundle(options: {platform: ?string}, host: string, port: number): Promise { return this._bundle({ ...options, bundle: new HMRBundle({ @@ -492,7 +492,7 @@ class Bundler { minify?: boolean, hot?: boolean, generateSourceMaps?: boolean, - }) { + }): Promise> { return this.getTransformOptions( entryFile, { diff --git a/packager/src/Resolver/index.js b/packager/src/Resolver/index.js index e73706bf8eea16..55e899a138354a 100644 --- a/packager/src/Resolver/index.js +++ b/packager/src/Resolver/index.js @@ -87,7 +87,7 @@ class Resolver { getShallowDependencies( entryFile: string, transformOptions: TransformOptions, - ): Array { + ): Promise> { return this._depGraph.getShallowDependencies(entryFile, transformOptions); } diff --git a/packager/src/Server/index.js b/packager/src/Server/index.js index e4b7e9f9eca0b2..0750e6aa6a2343 100644 --- a/packager/src/Server/index.js +++ b/packager/src/Server/index.js @@ -31,6 +31,7 @@ import type {Stats} from 'fs'; import type {IncomingMessage, ServerResponse} from 'http'; import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse'; import type Bundle from '../Bundler/Bundle'; +import type HMRBundle from '../Bundler/HMRBundle'; import type {Reporter} from '../lib/reporting'; import type {GetTransformOptions} from '../Bundler'; import type GlobalTransformCache from '../lib/GlobalTransformCache'; @@ -157,7 +158,7 @@ class Server { _assetServer: AssetServer; _bundler: Bundler; _debouncedFileChangeHandler: (filePath: string) => mixed; - _hmrFileChangeListener: (type: string, filePath: string) => mixed; + _hmrFileChangeListener: ?(type: string, filePath: string) => mixed; _reporter: Reporter; _symbolicateInWorker: Symbolicate; @@ -244,9 +245,7 @@ class Server { return this._bundler.end(); } - setHMRFileChangeListener( - listener: (type: string, filePath: string) => mixed, - ) { + setHMRFileChangeListener(listener: ?(type: string, filePath: string) => mixed) { this._hmrFileChangeListener = listener; } @@ -276,7 +275,7 @@ class Server { return bundle; } - buildBundleFromUrl(reqUrl: string): Promise { + buildBundleFromUrl(reqUrl: string): Promise { const options = this._getOptionsFromUrl(reqUrl); return this.buildBundle(options); } @@ -285,14 +284,14 @@ class Server { options: {platform: ?string}, host: string, port: number, - ): Promise { + ): Promise { return this._bundler.hmrBundle(options, host, port); } getShallowDependencies(options: { entryFile: string, platform?: string, - }): Promise { + }): Promise> { return Promise.resolve().then(() => { if (!options.platform) { options.platform = getPlatformExtension(options.entryFile); @@ -335,10 +334,11 @@ class Server { // If Hot Loading is enabled avoid rebuilding bundles and sending live // updates. Instead, send the HMR updates right away and clear the bundles // cache so that if the user reloads we send them a fresh bundle - if (this._hmrFileChangeListener) { + const {_hmrFileChangeListener} = this; + if (_hmrFileChangeListener) { // Clear cached bundles in case user reloads this._clearBundles(); - this._hmrFileChangeListener(type, filePath); + _hmrFileChangeListener(type, filePath); return; } else if (type !== 'change' && filePath.indexOf(NODE_MODULES) !== -1) { // node module resolution can be affected by added or removed files diff --git a/packager/src/node-haste/index.js b/packager/src/node-haste/index.js index 6af6a1901f2e85..fef220c58b473b 100644 --- a/packager/src/node-haste/index.js +++ b/packager/src/node-haste/index.js @@ -173,7 +173,10 @@ class DependencyGraph extends EventEmitter { * Returns a promise with the direct dependencies the module associated to * the given entryPath has. */ - getShallowDependencies(entryPath: string, transformOptions: TransformOptions) { + getShallowDependencies( + entryPath: string, + transformOptions: TransformOptions, + ): Promise> { return this._moduleCache .getModule(entryPath) .getDependencies(transformOptions); From af590b0c747e096d7a7856e88380307d81142f84 Mon Sep 17 00:00:00 2001 From: Charles Dick Date: Wed, 15 Mar 2017 06:31:38 -0700 Subject: [PATCH 039/366] remove the old heap profiler visualization code Differential Revision: D4650983 fbshipit-source-id: 1f791acdd3e2d96e7881ea037045fafa2c6d781a --- .../server/middleware/heapCapture/.gitignore | 3 - .../server/middleware/heapCapture/Makefile | 8 - .../middleware/heapCapture/heapCapture.html | 12 - .../middleware/heapCapture/package.json | 21 - .../middleware/heapCapture/src/Aggrow.js | 190 ----- .../middleware/heapCapture/src/AggrowData.js | 216 ------ .../heapCapture/src/AggrowExpander.js | 694 ------------------ .../heapCapture/src/AggrowTable.jsx | 439 ----------- .../heapCapture/src/DataColumnSelector.jsx | 42 -- .../middleware/heapCapture/src/Draggable.jsx | 26 - .../middleware/heapCapture/src/DropTarget.jsx | 33 - .../heapCapture/src/ExpanderConfiguration.jsx | 29 - .../heapCapture/src/StackExpanderCreator.jsx | 152 ---- .../heapCapture/src/StackRegistry.js | 96 --- .../heapCapture/src/StringInterner.js | 26 - .../heapCapture/src/TableConfiguration.jsx | 86 --- .../heapCapture/src/TableHeader.jsx | 103 --- .../middleware/heapCapture/src/heapCapture.js | 412 ----------- .../middleware/heapCapture/src/index.js | 24 - .../middleware/heapCapture/webpack.config.js | 29 - .../middleware/heapCaptureMiddleware.js | 171 ----- local-cli/server/runServer.js | 2 - 22 files changed, 2814 deletions(-) delete mode 100644 local-cli/server/middleware/heapCapture/.gitignore delete mode 100644 local-cli/server/middleware/heapCapture/Makefile delete mode 100644 local-cli/server/middleware/heapCapture/heapCapture.html delete mode 100644 local-cli/server/middleware/heapCapture/package.json delete mode 100644 local-cli/server/middleware/heapCapture/src/Aggrow.js delete mode 100644 local-cli/server/middleware/heapCapture/src/AggrowData.js delete mode 100644 local-cli/server/middleware/heapCapture/src/AggrowExpander.js delete mode 100644 local-cli/server/middleware/heapCapture/src/AggrowTable.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/Draggable.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/DropTarget.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/StackRegistry.js delete mode 100644 local-cli/server/middleware/heapCapture/src/StringInterner.js delete mode 100644 local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/TableHeader.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/heapCapture.js delete mode 100644 local-cli/server/middleware/heapCapture/src/index.js delete mode 100644 local-cli/server/middleware/heapCapture/webpack.config.js delete mode 100644 local-cli/server/middleware/heapCaptureMiddleware.js diff --git a/local-cli/server/middleware/heapCapture/.gitignore b/local-cli/server/middleware/heapCapture/.gitignore deleted file mode 100644 index 99ddc99ebc0737..00000000000000 --- a/local-cli/server/middleware/heapCapture/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/captures/* -preLoadedCapture.js -bundle.js diff --git a/local-cli/server/middleware/heapCapture/Makefile b/local-cli/server/middleware/heapCapture/Makefile deleted file mode 100644 index c3083416324513..00000000000000 --- a/local-cli/server/middleware/heapCapture/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -SHELL := /bin/bash - -all: - NODE_PATH="../../../../node_modules/" babel --presets babel-preset-react-native --source-maps inline -d out src - for f in out/*.js; do echo -e "\n// @generated" >> $$f; done - -watch: - NODE_PATH="../../../../node_modules/" babel --watch --presets babel-preset-react-native -d out src diff --git a/local-cli/server/middleware/heapCapture/heapCapture.html b/local-cli/server/middleware/heapCapture/heapCapture.html deleted file mode 100644 index 2faf4387f4a844..00000000000000 --- a/local-cli/server/middleware/heapCapture/heapCapture.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - JSC Heap Capture - - - Loading... This could take a while depending on how big the profile is. Check devtools console for errors. - - - - diff --git a/local-cli/server/middleware/heapCapture/package.json b/local-cli/server/middleware/heapCapture/package.json deleted file mode 100644 index 1533027d785e62..00000000000000 --- a/local-cli/server/middleware/heapCapture/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "jsc-heap-capture", - "version": "1.0.0", - "description": "processes captured heaps from javascript core", - "main": "bundle.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "webpack" - }, - "author": "cwdick", - "devDependencies": { - "babel-core": "^6.17.0", - "babel-loader": "^6.2.5", - "babel-plugin-transform-class-properties": "^6.16.0", - "babel-preset-es2015": "^6.16.0", - "babel-preset-react": "^6.16.0", - "react": "^0.14.1", - "react-dom": "^0.14.1", - "webpack": "^1.13.2" - } -} diff --git a/local-cli/server/middleware/heapCapture/src/Aggrow.js b/local-cli/server/middleware/heapCapture/src/Aggrow.js deleted file mode 100644 index 198bc9bd353fd0..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/Aggrow.js +++ /dev/null @@ -1,190 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -import AggrowData, { - AggrowDoubleColumn, - AggrowIntColumn, - AggrowStackColumn, - AggrowStringColumn } from './AggrowData'; -import AggrowExpander from './AggrowExpander'; -import type { FlattenedStack } from './StackRegistry'; -import StackRegistry from './StackRegistry'; - -export type FocusConfig = { - pattern: RegExp, - firstMatch: boolean, - leftSide: boolean, -} -type FocusPredicate = (frameId: number) => boolean; - -export default class Aggrow { - data: AggrowData; - expander: AggrowExpander; - - constructor(aggrowData: AggrowData) { - aggrowData.flattenStacks(); - this.data = aggrowData; - this.expander = new AggrowExpander(aggrowData.rowCount); - } - - addSumAggregator(aggregatorName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - - invariant(column, `Column ${columnName} does not exist.`); - invariant(column instanceof AggrowIntColumn || column instanceof AggrowDoubleColumn, - `Sum aggregator does not support ${column.constructor.name} columns!`); - return this.expander.addAggregator( - aggregatorName, - (indices: Int32Array): number => { - let size = 0; - indices.forEach((i: number) => { size += column.get(i); }); - return size; - }, - (value: any): string => value.toLocaleString(), - (a: number, b: number): number => b - a, - ); - } - - addCountAggregator(aggregatorName: string): number { - return this.expander.addAggregator( - aggregatorName, - (indices: Int32Array): number => indices.length, - (value: any): string => value.toLocaleString(), - (a: number, b: number): number => b - a, - ); - } - - addStringExpander(expanderName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant(column instanceof AggrowStringColumn, 'String expander needs a string column.'); - const strings = column.strings; - return this.expander.addFieldExpander( - expanderName, - (rowA: number, rowB: number): number => column.get(rowA) - column.get(rowB), - (row: number): string => strings.get(column.get(row)), - (s: string): string => s, - ); - } - - addNumberExpander(expanderName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant( - column instanceof AggrowIntColumn || column instanceof AggrowDoubleColumn, - `Number expander does not support ${column.constructor.name} columns.`); - return this.expander.addFieldExpander( - expanderName, - (rowA: number, rowB: number): number => column.get(rowA) - column.get(rowB), - (row: number): number => column.get(row), - (n: any): string => n.toLocaleString(), - ); - } - - addPointerExpander(expanderName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant( - column instanceof AggrowIntColumn, - `Pointer expander does not support ${column.constructor.name} columns.`); - return this.expander.addFieldExpander( - expanderName, - (rowA: number, rowB: number): number => column.get(rowA) - column.get(rowB), - (row: number): number => column.get(row), - (p: number): string => `0x${(p >>> 0).toString(16)}`, // eslint-disable-line no-bitwise - ); - } - - addStackExpander( - expanderName: string, - columnName: string, - reverse: boolean, - focus: ?FocusConfig): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant( - column instanceof AggrowStackColumn, - `Stack expander does not support ${column.constructor.name} columns.`); - let stacks = column.stacks; - const getter = column.getter; - const formatter = column.formatter; - if (focus) { - const re = focus.pattern; - const predicate = (frameId: number): boolean => re.test(formatter(getter(frameId))); - stacks = focusStacks(stacks, predicate, focus.firstMatch, focus.leftSide); - } - return this.expander.addStackExpander( - expanderName, - stacks.maxDepth, - (row: number): FlattenedStack => stacks.get(column.get(row)), - getter, - formatter, - !!reverse, - ); - } -} - -function focusStacks( - stacks: StackRegistry, - predicate: FocusPredicate, - firstMatch: boolean, - leftSide: boolean): FocusedStackRegistry { - let stackMapper; - if (firstMatch && leftSide) { - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = 0; i < stack.length; i++) { - if (predicate(stack[i])) { - return stack.subarray(0, i + 1); - } - } - return stack.subarray(0, 0); - }; - } else if (firstMatch && !leftSide) { - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = 0; i < stack.length; i++) { - if (predicate(stack[i])) { - return stack.subarray(i, stack.length); - } - } - return stack.subarray(0, 0); - }; - } else if (!firstMatch && leftSide) { - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = stack.length - 1; i >= 0; i--) { - if (predicate(stack[i])) { - return stack.subarray(0, i + 1); - } - } - return stack.subarray(0, 0); - }; - } else { // !firstMatch && !leftSide - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = stack.length - 1; i >= 0; i--) { - if (predicate(stack[i])) { - return stack.subarray(i, stack.length); - } - } - return stack.subarray(0, 0); - }; - } - - invariant(stacks.stackIdMap, 'Stacks were not flattened.'); - return new FocusedStackRegistry( - stacks.stackIdMap.map(stackMapper), - stacks.maxDepth); -} - -class FocusedStackRegistry { - maxDepth: number; - stackIdMap: Array; - - constructor(stackIdMap: Array, maxDepth: number) { - this.maxDepth = maxDepth; - this.stackIdMap = stackIdMap; - } - - get(id: number): FlattenedStack { - return this.stackIdMap[id]; - } -} diff --git a/local-cli/server/middleware/heapCapture/src/AggrowData.js b/local-cli/server/middleware/heapCapture/src/AggrowData.js deleted file mode 100644 index 856f1ea865ccca..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/AggrowData.js +++ /dev/null @@ -1,216 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -import type { FrameGetter, FrameFormatter } from './AggrowExpander'; -import type { Stack } from './StackRegistry'; -import StackRegistry from './StackRegistry'; -import StringInterner from './StringInterner'; - -export type AggrowColumnDef = - AggrowStringColumnDef | - AggrowIntColumnDef | - AggrowDoubleColumnDef | - AggrowStackColumnDef; - -type AggrowStringColumnDef = { - type: 'string'; - name: string; - strings: StringInterner; -} - -type AggrowIntColumnDef = { - type: 'int'; - name: string; -} - -type AggrowDoubleColumnDef = { - type: 'double'; - name: string; -} - -type AggrowStackColumnDef = { - type: 'stack'; - name: string; - stacks: StackRegistry, - getter: FrameGetter, - formatter: FrameFormatter, -} - -export interface AggrowColumn { - name: string; - get(row: number): number; - insert(row: number, s: any): void; - extend(count: number): void; -} - -class AggrowColumnBase { - name: string; - - constructor(def: AggrowColumnDef) { - this.name = def.name; - } -} - -export class AggrowStringColumn extends AggrowColumnBase { - strings: StringInterner; - data: Int32Array = new Int32Array(0); - - constructor(def: AggrowStringColumnDef) { - super(def); - this.strings = def.strings; - } - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, s: string) { - this.data[row] = this.strings.intern(s); - } - - extend(count: number) { - const newData = new Int32Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -export class AggrowIntColumn extends AggrowColumnBase { - data: Int32Array = new Int32Array(0); - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, i: number) { - this.data[row] = i; - } - - extend(count: number) { - const newData = new Int32Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -export class AggrowDoubleColumn extends AggrowColumnBase { - data: Float64Array = new Float64Array(0); - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, d: number) { - this.data[row] = d; - } - - extend(count: number) { - const newData = new Float64Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -export class AggrowStackColumn extends AggrowColumnBase { - data: Int32Array = new Int32Array(0); - stacks: StackRegistry; - getter: FrameGetter; - formatter: FrameFormatter; - - constructor(def: AggrowStackColumnDef) { - super(def); - this.stacks = def.stacks; - this.getter = def.getter; - this.formatter = def.formatter; - } - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, s: Stack) { - this.data[row] = s.id; - } - - extend(count: number) { - const newData = new Int32Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -function newColumn(def: AggrowColumnDef): AggrowColumn { - switch (def.type) { - case 'string': - return new AggrowStringColumn(def); - case 'int': - return new AggrowIntColumn(def); - case 'double': - return new AggrowDoubleColumn(def); - case 'stack': - return new AggrowStackColumn(def); - default: - throw new Error(`Unknown column type: ${def.type}`); - } -} - -export default class AggrowData { - columns: Array; - rowCount = 0; - - constructor(columnDefs: Array) { - this.columns = columnDefs.map(newColumn); - } - - rowInserter(numRows: number): RowInserter { - const columns = this.columns; - columns.forEach((c: AggrowColumn): void => c.extend(numRows)); - const currRow = this.rowCount; - const endRow = currRow + numRows; - this.rowCount = endRow; - - return new RowInserter(columns, { currRow, endRow }); - } - - getColumn(name: string): ?AggrowColumn { - return this.columns.find((c: AggrowColumn): boolean => c.name === name); - } - - flattenStacks() { - this.columns.forEach((c: AggrowColumn) => { - if (c instanceof AggrowStackColumn) { - c.stacks.flatten(); - } - }); - } -} - -class RowInserter { - columns: Array; - currRow: number; - endRow: number; - - constructor( - columns: Array, - params: { currRow: number, endRow: number }) { - this.columns = columns; - this.currRow = params.currRow; - this.endRow = params.endRow; - } - - insertRow(...args: Array) { - invariant(this.currRow < this.endRow, 'Tried to insert data off end of added range!'); - invariant( - args.length === this.columns.length, - `Expected data for ${this.columns.length} columns, got ${args.length} columns`); - - args.forEach((arg: number | string | Stack, i: number): void => - this.columns[i].insert(this.currRow, arg)); - this.currRow += 1; - } - - done() { - invariant(this.currRow === this.endRow, 'Unfilled rows!'); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/AggrowExpander.js b/local-cli/server/middleware/heapCapture/src/AggrowExpander.js deleted file mode 100644 index 5222901989e2e7..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/AggrowExpander.js +++ /dev/null @@ -1,694 +0,0 @@ -// @flow -import invariant from 'invariant'; - -import type { FlattenedStack } from './StackRegistry'; - -// expander ID definitions -const FIELD_EXPANDER_ID_MIN = 0x0000; -const FIELD_EXPANDER_ID_MAX = 0x7fff; -const STACK_EXPANDER_ID_MIN = 0x8000; -const STACK_EXPANDER_ID_MAX = 0xffff; - -// used for row.expander which reference state.activeExpanders (with frame index masked in) -const INVALID_ACTIVE_EXPANDER = -1; -const ACTIVE_EXPANDER_MASK = 0xffff; -const ACTIVE_EXPANDER_FRAME_SHIFT = 16; - -// aggregator ID definitions -const AGGREGATOR_ID_MAX = 0xffff; - -// active aggragators can have sort order changed in the reference -const ACTIVE_AGGREGATOR_MASK = 0xffff; -const ACTIVE_AGGREGATOR_ASC_BIT = 0x10000; - -// tree node state definitions -const NODE_EXPANDED_BIT = 0x0001; // this row is expanded -const NODE_REAGGREGATE_BIT = 0x0002; // children need aggregates -const NODE_REORDER_BIT = 0x0004; // children need to be sorted -const NODE_REPOSITION_BIT = 0x0008; // children need position -const NODE_INDENT_SHIFT = 16; - -function _calleeFrameIdGetter(stack: FlattenedStack, depth: number): number { - return stack[depth]; -} - -function _callerFrameIdGetter(stack: FlattenedStack, depth: number): number { - return stack[stack.length - depth - 1]; -} - -function _createStackComparers( - stackGetter: StackGetter, - frameIdGetter: FrameIdGetter, - maxStackDepth: number): Array> { - const comparers = new Array(maxStackDepth); - for (let depth = 0; depth < maxStackDepth; depth++) { - const captureDepth = depth; // NB: to capture depth per loop iteration - comparers[depth] = function calleeStackComparer(rowA: number, rowB: number): number { - const a = stackGetter(rowA); - const b = stackGetter(rowB); - // NB: we put the stacks that are too short at the top, - // so they can be grouped into the '' bucket - if (a.length <= captureDepth && b.length <= captureDepth) { - return 0; - } else if (a.length <= captureDepth) { - return -1; - } else if (b.length <= captureDepth) { - return 1; - } - return frameIdGetter(a, captureDepth) - frameIdGetter(b, captureDepth); - }; - } - return comparers; -} - -function _createTreeNode( - parent: Row | null, - label: string, - indices: Int32Array, - expander: number): Row { - const indent = parent === null ? 0 : (parent.state >>> NODE_INDENT_SHIFT) + 1; // eslint-disable-line no-bitwise, max-len - const state = NODE_REPOSITION_BIT | // eslint-disable-line no-bitwise - NODE_REAGGREGATE_BIT | - NODE_REORDER_BIT | - (indent << NODE_INDENT_SHIFT); // eslint-disable-line no-bitwise - return { - parent, // null if root - children: null, // array of children nodes - label, // string to show in UI - indices, // row indices under this node - aggregates: null, // result of aggregate on indices - expander, // index into state.activeExpanders - top: 0, // y position of top row (in rows) - height: 1, // number of rows including children - state, // see NODE_* definitions above - }; -} - -const NO_SORT_ORDER: Comparer<*> = (): number => 0; - -type Comparer = (a: T, b: T) => number; - -type Aggregator = { - name: string, // name for column - aggregator: (indexes: Int32Array) => number, // index array -> aggregate value - formatter: (value: number) => string, // aggregate value -> display string - sorter: Comparer, // compare two aggregate values -} - -type FieldExpander = { - name: string, - comparer: Comparer, - getter: (rowIndex: number) => any, - formatter: (value: any) => string, -} - -type StackGetter = (rowIndex: number) => FlattenedStack; // (row) => [frameId int] -type FrameIdGetter = (stack: FlattenedStack, depth: number) => number; // (stack,depth) -> frame id -export type FrameGetter = (id: number) => any; // (frameId int) => frame obj -export type FrameFormatter = (frame: any) => string; // (frame obj) => display string - -type StackExpander = { - name: string, // display name of expander - comparers: Array>, // depth -> comparer - stackGetter: StackGetter, - frameIdGetter: FrameIdGetter, - frameGetter: FrameGetter, - frameFormatter: FrameFormatter, -} - -export type Row = { - top: number, - height: number, - state: number, - parent: Row | null, - indices: Int32Array, - aggregates: Array | null, - children: Array | null, - expander: number, - label: string, -} - -type State = { - fieldExpanders: Array, // tree expanders that expand on simple values - stackExpanders: Array, // tree expanders that expand stacks - activeExpanders: Array, // index into field or stack expanders, hierarchy of tree - aggregators: Array, // all available aggregators, might not be used - activeAggregators: Array, // index into aggregators, to actually compute - sorter: Comparer<*>, - root: Row, -} - -export default class AggrowExpander { // eslint-disable-line no-unused-vars - indices: Int32Array; - state: State; - - constructor(numRows: number) { - this.indices = new Int32Array(numRows); - for (let i = 0; i < numRows; i++) { - this.indices[i] = i; - } - - this.state = { - fieldExpanders: [], - stackExpanders: [], - activeExpanders: [], - aggregators: [], - activeAggregators: [], - sorter: NO_SORT_ORDER, - root: _createTreeNode(null, '', this.indices, INVALID_ACTIVE_EXPANDER), - }; - } - - _evaluateAggregate(row: Row) { - const activeAggregators = this.state.activeAggregators; - const aggregates = new Array(activeAggregators.length); - for (let j = 0; j < activeAggregators.length; j++) { - const aggregator = this.state.aggregators[activeAggregators[j]]; - aggregates[j] = aggregator.aggregator(row.indices); - } - row.aggregates = aggregates; // eslint-disable-line no-param-reassign - row.state |= NODE_REAGGREGATE_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _evaluateAggregates(row: Row) { - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected non-null children'); - for (let i = 0; i < children.length; i++) { - this._evaluateAggregate(children[i]); - } - row.state |= NODE_REORDER_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - row.state ^= NODE_REAGGREGATE_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _evaluateOrder(row: Row) { - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected non-null children'); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - child.state |= NODE_REORDER_BIT; // eslint-disable-line no-bitwise - } - children.sort(this.state.sorter); - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - row.state ^= NODE_REORDER_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _evaluatePosition(row: Row) { // eslint-disable-line class-methods-use-this - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected a children array'); - let childTop = row.top + 1; - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.top !== childTop) { - child.top = childTop; - child.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise - } - childTop += child.height; - } - } - row.state ^= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _getRowsImpl(row: Row, top: number, height: number, result: Array) { - if ((row.state & NODE_REAGGREGATE_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateAggregates(row); - } - if ((row.state & NODE_REORDER_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateOrder(row); - } - if ((row.state & NODE_REPOSITION_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluatePosition(row); - } - - if (row.top >= top && row.top < top + height) { - invariant( - result[row.top - top] === null, - `getRows put more than one row at position ${row.top} into result`); - result[row.top - top] = row; // eslint-disable-line no-param-reassign - } - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected non-null children'); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.top < top + height && top < child.top + child.height) { - this._getRowsImpl(child, top, height, result); - } - } - } - } - - _updateHeight(row: Row | null, heightChange: number) { // eslint-disable-line class-methods-use-this, max-len - while (row !== null) { - row.height += heightChange; // eslint-disable-line no-param-reassign - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - row = row.parent; // eslint-disable-line no-param-reassign - } - } - - _addChildrenWithFieldExpander(row: Row, expander: FieldExpander, nextActiveIndex: number) { // eslint-disable-line class-methods-use-this, max-len - const rowIndices = row.indices; - const comparer = expander.comparer; - const formatter = expander.formatter; - const getter = expander.getter; - rowIndices.sort(comparer); - let begin = 0; - let end = 1; - row.children = []; // eslint-disable-line no-param-reassign - while (end < rowIndices.length) { - if (comparer(rowIndices[begin], rowIndices[end]) !== 0) { - invariant(row.children, 'Expected a children array'); - row.children.push(_createTreeNode( - row, - `${expander.name}: ${formatter(getter(rowIndices[begin]))}`, - rowIndices.subarray(begin, end), - nextActiveIndex)); - begin = end; - } - end += 1; - } - row.children.push(_createTreeNode( - row, - `${expander.name}: ${formatter(getter(rowIndices[begin]))}`, - rowIndices.subarray(begin, end), - nextActiveIndex)); - } - - _addChildrenWithStackExpander( // eslint-disable-line class-methods-use-this - row: Row, - expander: StackExpander, - activeIndex: number, - depth: number, - nextActiveIndex: number) { - const rowIndices = row.indices; - const stackGetter = expander.stackGetter; - const frameIdGetter = expander.frameIdGetter; - const frameGetter = expander.frameGetter; - const frameFormatter = expander.frameFormatter; - const comparer = expander.comparers[depth]; - const expandNextFrame = activeIndex | ((depth + 1) << ACTIVE_EXPANDER_FRAME_SHIFT); // eslint-disable-line no-bitwise, max-len - rowIndices.sort(comparer); - let columnName = ''; - if (depth === 0) { - columnName = `${expander.name}: `; - } - - // put all the too-short stacks under - let begin = 0; - let beginStack = null; - row.children = []; // eslint-disable-line no-param-reassign - while (begin < rowIndices.length) { - beginStack = stackGetter(rowIndices[begin]); - if (beginStack.length > depth) { - break; - } - begin += 1; - } - invariant(beginStack !== null, 'Expected beginStack at this point'); - if (begin > 0) { - row.children.push(_createTreeNode( - row, - `${columnName}`, - rowIndices.subarray(0, begin), - nextActiveIndex)); - } - // aggregate the rest under frames - if (begin < rowIndices.length) { - let end = begin + 1; - while (end < rowIndices.length) { - const endStack = stackGetter(rowIndices[end]); - if (frameIdGetter(beginStack, depth) !== frameIdGetter(endStack, depth)) { - invariant(row.children, 'Expected a children array'); - row.children.push(_createTreeNode( - row, - columnName + frameFormatter(frameGetter(frameIdGetter(beginStack, depth))), - rowIndices.subarray(begin, end), - expandNextFrame)); - begin = end; - beginStack = endStack; - } - end += 1; - } - row.children.push(_createTreeNode( - row, - columnName + frameFormatter(frameGetter(frameIdGetter(beginStack, depth))), - rowIndices.subarray(begin, end), - expandNextFrame)); - } - } - - _contractRow(row: Row) { - invariant( - (row.state & NODE_EXPANDED_BIT) !== 0, // eslint-disable-line no-bitwise - 'Cannot contract row; already contracted!'); - row.state ^= NODE_EXPANDED_BIT; // eslint-disable-line no-bitwise, no-param-reassign - const heightChange = 1 - row.height; - this._updateHeight(row, heightChange); - } - - _pruneExpanders(row: Row, oldExpander: number, newExpander: number) { - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - if (row.expander === oldExpander) { - row.state |= NODE_REAGGREGATE_BIT | NODE_REORDER_BIT | NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign, max-len - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - this._contractRow(row); - } - row.children = null; // eslint-disable-line no-param-reassign - row.expander = newExpander; // eslint-disable-line no-param-reassign - } else { - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - const children = row.children; - if (children != null) { - for (let i = 0; i < children.length; i++) { - const child = children[i]; - this._pruneExpanders(child, oldExpander, newExpander); - } - } - } - } - - addFieldExpander( - name: string, - comparer: Comparer, - getter: (rowIndex: number) => any, - formatter: (value: any) => string): number { - invariant( - FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length < FIELD_EXPANDER_ID_MAX, - 'too many field expanders!'); - this.state.fieldExpanders.push({ name, comparer, getter, formatter }); - return FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length - 1; - } - - addStackExpander( - name: string, - maxStackDepth: number, - stackGetter: StackGetter, - frameGetter: FrameGetter, - frameFormatter: FrameFormatter, - reverse: boolean): number { - invariant( - STACK_EXPANDER_ID_MIN + this.state.fieldExpanders.length < STACK_EXPANDER_ID_MAX, - 'Too many stack expanders!'); - const idGetter = reverse ? _callerFrameIdGetter : _calleeFrameIdGetter; - this.state.stackExpanders.push({ - name, - stackGetter, - comparers: _createStackComparers(stackGetter, idGetter, maxStackDepth), - frameIdGetter: idGetter, - frameGetter, - frameFormatter, - }); - return STACK_EXPANDER_ID_MIN + this.state.stackExpanders.length - 1; - } - - getExpanders(): Array { - const expanders = []; - for (let i = 0; i < this.state.fieldExpanders.length; i++) { - expanders.push(FIELD_EXPANDER_ID_MIN + i); - } - for (let i = 0; i < this.state.stackExpanders.length; i++) { - expanders.push(STACK_EXPANDER_ID_MIN + i); - } - return expanders; - } - - getExpanderName(id: number): string { - if (id >= FIELD_EXPANDER_ID_MIN && id <= FIELD_EXPANDER_ID_MAX) { - return this.state.fieldExpanders[id - FIELD_EXPANDER_ID_MIN].name; - } else if (id >= STACK_EXPANDER_ID_MIN && id <= STACK_EXPANDER_ID_MAX) { - return this.state.stackExpanders[id - STACK_EXPANDER_ID_MIN].name; - } - throw new Error(`Unknown expander ID ${id.toString()}`); - } - - setActiveExpanders(ids: Array) { - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; - if (id >= FIELD_EXPANDER_ID_MIN && id <= FIELD_EXPANDER_ID_MAX) { - invariant( - id - FIELD_EXPANDER_ID_MIN < this.state.fieldExpanders.length, - `field expander for id ${id.toString()} does not exist!`); - } else if (id >= STACK_EXPANDER_ID_MIN && id <= STACK_EXPANDER_ID_MAX) { - invariant(id - STACK_EXPANDER_ID_MIN < this.state.stackExpanders.length, - `stack expander for id ${id.toString()} does not exist!`); - } - } - for (let i = 0; i < ids.length; i++) { - if (this.state.activeExpanders.length <= i) { - this._pruneExpanders(this.state.root, INVALID_ACTIVE_EXPANDER, i); - break; - } else if (ids[i] !== this.state.activeExpanders[i]) { - this._pruneExpanders(this.state.root, i, i); - break; - } - } - // TODO: if ids is prefix of activeExpanders, we need to make an expander invalid - this.state.activeExpanders = ids.slice(); - } - - getActiveExpanders(): Array { - return this.state.activeExpanders.slice(); - } - - addAggregator( - name: string, - aggregator: (indexes: Int32Array) => number, - formatter: (value: number) => string, - sorter: Comparer): number { - invariant(this.state.aggregators.length < AGGREGATOR_ID_MAX, 'too many aggregators!'); - this.state.aggregators.push({ name, aggregator, formatter, sorter }); - return this.state.aggregators.length - 1; - } - - getAggregators(): Array { - const aggregators = []; - for (let i = 0; i < this.state.aggregators.length; i++) { - aggregators.push(i); - } - return aggregators; - } - - getAggregatorName(id: number): string { - return this.state.aggregators[id & ACTIVE_AGGREGATOR_MASK].name; // eslint-disable-line no-bitwise, max-len - } - - setActiveAggregators(ids: Array) { - for (let i = 0; i < ids.length; i++) { - const id = ids[i] & ACTIVE_AGGREGATOR_MASK; // eslint-disable-line no-bitwise - invariant( - id >= 0 && id < this.state.aggregators.length, - `aggregator id ${id.toString()} not valid`); - } - this.state.activeAggregators = ids.slice(); - // NB: evaluate root here because dirty bit is for children - // so someone has to start with root, and it might as well be right away - this._evaluateAggregate(this.state.root); - let sorter = NO_SORT_ORDER; - for (let i = ids.length - 1; i >= 0; i--) { - const ascending = (ids[i] & ACTIVE_AGGREGATOR_ASC_BIT) !== 0; // eslint-disable-line no-bitwise, max-len - const id = ids[i] & ACTIVE_AGGREGATOR_MASK; // eslint-disable-line no-bitwise - const comparer = this.state.aggregators[id].sorter; - const captureSorter = sorter; - const captureIndex = i; - sorter = (a: Row, b: Row): number => { - invariant(a.aggregates && b.aggregates, 'Expected aggregates.'); - const c = comparer(a.aggregates[captureIndex], b.aggregates[captureIndex]); - if (c === 0) { - return captureSorter(a, b); - } - return ascending ? -c : c; - }; - } - this.state.sorter = sorter; // eslint-disable-line no-param-reassign - this.state.root.state |= NODE_REORDER_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - getActiveAggregators(): Array { - return this.state.activeAggregators.slice(); - } - - getRows(top: number, height: number): Array { - const result = new Array(height); - for (let i = 0; i < height; i++) { - result[i] = null; - } - this._getRowsImpl(this.state.root, top, height, result); - return result; - } - - _findRowImpl(fromRow: number, predicate: (row: Row) => boolean, row: Row): number { - if (row.top > fromRow && predicate(row)) { - return row.top; // this row is a match! - } - - // remember how to clean up after ourselves so we only expand as little as possible - const contractChildren = this.canExpand(row); - const cleanUpChildren = row.children === null; - if (contractChildren) { - this.expand(row); - } - - // evaluate position so we search in the correct order - if ((row.state & NODE_REAGGREGATE_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateAggregates(row); - } - if ((row.state & NODE_REORDER_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateOrder(row); - } - if ((row.state & NODE_REPOSITION_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluatePosition(row); - } - // TODO: encapsulate row state management somewhere so logic can be shared with _getRowsImpl - - // search in children - const children = row.children; - if (children !== null) { - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.top + child.height > fromRow) { - const find = this._findRowImpl(fromRow, predicate, child); - if (find >= 0) { - return find; - } - } - } - } - // clean up to leave the tree how it was if we didn't find anything - // this also saves memory - if (contractChildren) { - this.contract(row); - } - if (cleanUpChildren) { - row.children = null; - } - return -1; - } - - // findRow - find the first row that matches a predicate - // parameters - // predicate: returns true when row is found - // fromRow: start search from after this row index (negative for start at beginning) - // returns: index of first row that matches, -1 if no match found - findRow(predicate: (row: Row) => boolean, fromRow: ?number): number { - return this._findRowImpl(!fromRow ? -1 : fromRow, predicate, this.state.root); - } - - getRowLabel(row: Row): string { // eslint-disable-line class-methods-use-this - return row.label; - } - - getRowIndent(row: Row): number { // eslint-disable-line class-methods-use-this - return row.state >>> NODE_INDENT_SHIFT; // eslint-disable-line no-bitwise - } - - getRowExpanderIndex(row: Row): number { // eslint-disable-line class-methods-use-this - if (row.parent) { - return row.parent.expander & ACTIVE_EXPANDER_MASK; // eslint-disable-line no-bitwise - } - return -1; - } - - getRowExpansionPath(row: Row | null): Array { - const path = []; - invariant(row, 'Expected non-null row here'); - const index = row.indices[0]; - row = row.parent; // eslint-disable-line no-param-reassign - while (row) { - const exIndex = row.expander & ACTIVE_EXPANDER_MASK; // eslint-disable-line no-bitwise - const exId = this.state.activeExpanders[exIndex]; - if (exId >= FIELD_EXPANDER_ID_MIN && - exId < FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length) { - const expander = this.state.fieldExpanders[exId - FIELD_EXPANDER_ID_MIN]; // eslint-disable-line no-bitwise, max-len - path.push(expander.getter(index)); - row = row.parent; // eslint-disable-line no-param-reassign - } else if (exId >= STACK_EXPANDER_ID_MIN && - exId < STACK_EXPANDER_ID_MIN + this.state.stackExpanders.length) { - const expander = this.state.stackExpanders[exId - STACK_EXPANDER_ID_MIN]; - const stackGetter = expander.stackGetter; - const frameIdGetter = expander.frameIdGetter; - const frameGetter = expander.frameGetter; - const stack = []; - while (row && (row.expander & ACTIVE_EXPANDER_MASK) === exIndex) { // eslint-disable-line no-bitwise, max-len - const depth = row.expander >>> ACTIVE_EXPANDER_FRAME_SHIFT; // eslint-disable-line no-bitwise, max-len - const rowStack = stackGetter(index); - if (depth >= rowStack.length) { - stack.push(''); - } else { - stack.push(frameGetter(frameIdGetter(rowStack, depth))); - } - row = row.parent; // eslint-disable-line no-param-reassign - } - path.push(stack.reverse()); - } - } - return path.reverse(); - } - - getRowAggregate(row: Row, index: number): string { - const aggregator = this.state.aggregators[this.state.activeAggregators[index]]; - invariant(row.aggregates, 'Expected aggregates'); - return aggregator.formatter(row.aggregates[index]); - } - - getHeight(): number { - return this.state.root.height; - } - - canExpand(row: Row): boolean { // eslint-disable-line class-methods-use-this - return (row.state & NODE_EXPANDED_BIT) === 0 && (row.expander !== INVALID_ACTIVE_EXPANDER); // eslint-disable-line no-bitwise, max-len - } - - canContract(row: Row): boolean { // eslint-disable-line class-methods-use-this - return (row.state & NODE_EXPANDED_BIT) !== 0; // eslint-disable-line no-bitwise - } - - expand(row: Row) { - invariant( - (row.state & NODE_EXPANDED_BIT) === 0, // eslint-disable-line no-bitwise - 'can not expand row, already expanded'); - invariant(row.height === 1, `unexpanded row has height ${row.height.toString()} != 1`); - if (row.children === null) { // first expand, generate children - const activeIndex = row.expander & ACTIVE_EXPANDER_MASK; // eslint-disable-line no-bitwise - let nextActiveIndex = activeIndex + 1; // NB: if next is stack, frame is 0 - if (nextActiveIndex >= this.state.activeExpanders.length) { - nextActiveIndex = INVALID_ACTIVE_EXPANDER; - } - invariant( - activeIndex < this.state.activeExpanders.length, - `invalid active expander index ${activeIndex.toString()}`); - const exId = this.state.activeExpanders[activeIndex]; - if (exId >= FIELD_EXPANDER_ID_MIN && - exId < FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length) { - const expander = this.state.fieldExpanders[exId - FIELD_EXPANDER_ID_MIN]; - this._addChildrenWithFieldExpander(row, expander, nextActiveIndex); - } else if (exId >= STACK_EXPANDER_ID_MIN && - exId < STACK_EXPANDER_ID_MIN + this.state.stackExpanders.length) { - const depth = row.expander >>> ACTIVE_EXPANDER_FRAME_SHIFT; // eslint-disable-line no-bitwise, max-len - const expander = this.state.stackExpanders[exId - STACK_EXPANDER_ID_MIN]; - this._addChildrenWithStackExpander(row, expander, activeIndex, depth, nextActiveIndex); - } else { - throw new Error(`state.activeIndex ${activeIndex} has invalid expander${exId}`); - } - } - row.state |= NODE_EXPANDED_BIT | NODE_REAGGREGATE_BIT | NODE_REORDER_BIT | NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign, max-len - let heightChange = 0; - invariant(row.children, 'Expected a children array'); - for (let i = 0; i < row.children.length; i++) { - heightChange += row.children[i].height; - } - this._updateHeight(row, heightChange); - // if children only contains one node, then expand it as well - invariant(row.children, 'Expected a children array'); - if (row.children.length === 1 && this.canExpand(row.children[0])) { - this.expand(row.children[0]); - } - } - - contract(row: Row) { - this._contractRow(row); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/AggrowTable.jsx b/local-cli/server/middleware/heapCapture/src/AggrowTable.jsx deleted file mode 100644 index 8a54b2ba2f3781..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/AggrowTable.jsx +++ /dev/null @@ -1,439 +0,0 @@ -// @flow - -import invariant from 'invariant'; -import React from 'react'; - -import Aggrow from './Aggrow'; -import type { Row } from './AggrowExpander'; -import TableConfiguration from './TableConfiguration'; -import TableHeader from './TableHeader'; - -const rowHeight = 20; -const treeIndent = 16; - -type Props = { - aggrow: Aggrow, - enableConfigurationPane: boolean, - onSelectionChange?: (row: Row) => void, -} - -type State = { - aggrow: Aggrow, - viewport: { - top: number, - height: number, - }, - cursor: number, - searchValue: string, -} - -export default class AggrowTable extends React.Component { - static defaultProps = { - enableConfigurationPane: true, - }; - - constructor(props: Props) { - super(props); - this.state = { - aggrow: props.aggrow, - viewport: { top: 0, height: 100 }, - cursor: 0, - searchValue: '', - }; - } - - props: Props; - - state: State; - - componentDidMount() { - document.body && document.body.addEventListener('keydown', this.keydown); - } - - componentWillReceiveProps(nextProps: Props) { - if (this.props.aggrow !== nextProps.aggrow) { - this.setState({ - aggrow: nextProps.aggrow, - viewport: { top: 0, height: 100 }, - cursor: 0, - }); - } - } - - componentWillUnmount() { - document.body && document.body.removeEventListener('keydown', this.keydown); - } - - scroll = (e: SyntheticUIEvent) => { - const viewport = e.target; - invariant(viewport instanceof HTMLElement, 'Expected an HTML element'); - const top = Math.floor((viewport.scrollTop - (viewport.clientHeight * 1.0)) / rowHeight); - const height = Math.ceil(viewport.clientHeight * 3.0 / rowHeight); - if (top !== this.state.viewport.top || height !== this.state.viewport.height) { - this.setState({ viewport: { top, height } }); - } - } - - _updateCursor(position: number) { - this.setState({ cursor: position }); - const onSelectionChange = this.props.onSelectionChange; - if (onSelectionChange) { - const row = this.state.aggrow.expander.getRows(position, 1)[0]; - invariant(row, 'Expected a row'); - onSelectionChange(row); - } - } - - _contractRow(row: Row) { - let newCursor = this.state.cursor; - if (newCursor > row.top && newCursor < row.top + row.height) { // in contracted section - newCursor = row.top; - } else if (newCursor >= row.top + row.height) { // below contracted section - newCursor -= row.height - 1; - } - this.state.aggrow.expander.contract(row); - this._updateCursor(newCursor); - } - - _expandRow(row: Row) { - let newCursor = this.state.cursor; - this.state.aggrow.expander.expand(row); - if (newCursor > row.top) { // below expanded section - newCursor += row.height - 1; - } - this._updateCursor(newCursor); - } - - _scrollDiv: ?HTMLDivElement = null; - - _setScrollDiv = (div: ?HTMLDivElement) => { - this._scrollDiv = div; - } - - _keepCursorInViewport() { - if (this._scrollDiv) { - const cursor = this.state.cursor; - const scrollDiv = this._scrollDiv; - if (cursor * rowHeight < scrollDiv.scrollTop + (scrollDiv.clientHeight * 0.1)) { - scrollDiv.scrollTop = (cursor * rowHeight) - (scrollDiv.clientHeight * 0.1); - } else if ((cursor + 1) * rowHeight > scrollDiv.scrollTop + (scrollDiv.clientHeight * 0.9)) { - scrollDiv.scrollTop = ((cursor + 1) * rowHeight) - (scrollDiv.clientHeight * 0.9); - } - } - } - - keydown = (e: KeyboardEvent) => { - const expander = this.state.aggrow.expander; - let cursor = this.state.cursor; - let row = expander.getRows(cursor, 1)[0]; - invariant(row, 'Expected a row'); - switch (e.keyCode) { - case 38: // up - if (cursor > 0) { - this._updateCursor(cursor - 1); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - case 40: // down - if (cursor < expander.getHeight() - 1) { - this._updateCursor(cursor + 1); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - case 37: // left - if (expander.canContract(row)) { - this._contractRow(row); - } else if (expander.getRowIndent(row) > 0) { - const indent = expander.getRowIndent(row) - 1; - while (expander.getRowIndent(row) > indent) { - cursor -= 1; - row = expander.getRows(cursor, 1)[0]; - } - this._updateCursor(cursor); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - case 39: // right - if (expander.canExpand(row)) { - this._expandRow(row); - } else if (cursor < expander.getHeight() - 1) { - this._updateCursor(cursor + 1); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - default: - // Do nothing - break; - } - } - - dropAction = (s: string, d: string) => { - const expander = this.state.aggrow.expander; - if (s.startsWith('aggregate:active:')) { - const sIndex = parseInt(s.substr(17), 10); - let dIndex = -1; - const active = expander.getActiveAggregators(); - const dragged = active[sIndex]; - if (d.startsWith('aggregate:insert:')) { - dIndex = parseInt(d.substr(17), 10); - } else if (d === 'divider:insert') { - dIndex = active.length; - } else { - throw new Error(`not allowed to drag ${s} to ${d}`); - } - if (dIndex > sIndex) { - dIndex -= 1; - } - active.splice(sIndex, 1); - active.splice(dIndex, 0, dragged); - expander.setActiveAggregators(active); - this._updateCursor(0); - } else if (s.startsWith('expander:active:')) { - const sIndex = parseInt(s.substr(16), 10); - let dIndex = -1; - const active = expander.getActiveExpanders(); - const dragged = active[sIndex]; - if (d.startsWith('expander:insert:')) { - dIndex = parseInt(d.substr(16), 10); - } else if (d === 'divider:insert') { - dIndex = 0; - } else { - throw new Error(`not allowed to drag ${s} to ${d}`); - } - if (dIndex > sIndex) { - dIndex -= 1; - } - active.splice(sIndex, 1); - active.splice(dIndex, 0, dragged); - expander.setActiveExpanders(active); - this._updateCursor(0); - } else if (s.startsWith('expander:add:')) { - let dIndex = -1; - const sExpander = parseInt(s.substring(13), 10); - if (d.startsWith('expander:insert:')) { - dIndex = parseInt(d.substr(16), 10); - } else if (d === 'divider:insert') { - dIndex = 0; - } else { - throw new Error(`not allowed to drag ${s} to ${d}`); - } - const active = expander.getActiveExpanders(); - active.splice(dIndex, 0, sExpander); - expander.setActiveExpanders(active); - this._updateCursor(0); - } - } - - _handleUpdate = () => { - this.setState({ aggrow: this.state.aggrow }); - } - - renderVirtualizedRows(): React.Element<*> { - const expander = this.state.aggrow.expander; - const viewport = this.state.viewport; - const rows = expander.getRows(viewport.top, viewport.height); - return ( -
- { rows.map((child: Row | null): ?React.Element<*> => this.renderRow(child)) } -
- ); - } - - renderRow(toRender: Row | null): ?React.Element<*> { - if (toRender === null) { - return null; - } - const row = toRender; - let bg = 'white'; - const expander = this.state.aggrow.expander; - const columns = []; - let rowText = ''; - const indent = 4 + (expander.getRowIndent(row) * treeIndent); - const aggregates = expander.getActiveAggregators(); - if (expander.getRowExpanderIndex(row) % 2 === 1) { - bg = '#f0f0f0'; - } - if (row.top === this.state.cursor) { - bg = '#dfe3ee'; - } - for (let i = 0; i < aggregates.length; i++) { - const aggregate = expander.getRowAggregate(row, i); - columns.push(( -
- )); - columns.push(( -
- {aggregate} -
- )); - } - columns.push(( -
- )); - if (expander.canExpand(row)) { - columns.push(( -
this._expandRow(row)} - style={{ - marginLeft: `${indent}px`, - flexShrink: '0', - width: '12px', - textAlign: 'center', - border: '1px solid gray', - }}> - + -
- )); - } else if (expander.canContract(row)) { - columns.push(( -
this._contractRow(row)} - style={{ - marginLeft: `${indent}px`, - flexShrink: '0', - width: '12px', - textAlign: 'center', - border: '1px solid gray', - }}> - - -
- )); - } else { - columns.push(( -
- )); - } - rowText += expander.getRowLabel(row); - columns.push(( -
- {rowText} -
- )); - return ( -
{ // eslint-disable-line react/jsx-no-bind - this._updateCursor(row.top); - }} - style={{ - position: 'absolute', - height: `${(rowHeight - 1)}px`, - top: `${(rowHeight * row.top)}px`, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - backgroundColor: bg, - borderBottom: '1px solid gray', - }}> - {columns} -
- ); - } - - render(): React.Element<*> { - const expander = this.state.aggrow.expander; - const cursor = this.state.cursor; - const row = expander.getRows(cursor, 1)[0]; - invariant(row, 'Expected a row'); - const selectedExpander = expander.getRowExpanderIndex(row); - return ( -
-
-
- {this.setState({searchValue: event.target.value});}} /> - { - const re = new RegExp(this.state.searchValue); - const i = this.state.aggrow.expander.findRow((row) => re.test(row.label), this.state.cursor); - if (i >= 0) { - this._updateCursor(i); - this._keepCursorInViewport(); - } - }} /> -
- -
-
- { this.renderVirtualizedRows() } -
-
-
- { - this.props.enableConfigurationPane ? - : - undefined - } -
- ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx b/local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx deleted file mode 100644 index e7a1784f8669b6..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx +++ /dev/null @@ -1,42 +0,0 @@ -// @flow - -import invariant from 'invariant'; -import React from 'react'; - -import AggrowData from './AggrowData'; -import type { AggrowColumn } from './AggrowData'; - -type Props = { - aggrow: AggrowData, - filter: (column: AggrowColumn) => boolean, - onSelect: (columnName: string) => void, - selected?: string, -} - -export default class DataColumnSelector extends React.Component { - static defaultProps = { - filter: (): boolean => true, - }; - - props: Props; - - _handleChange = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLSelectElement, 'Expected element'); - const changed = Number.parseInt(e.target.value, 10); - this.props.onSelect(this.props.aggrow.columns[changed].name); - } - - render(): React.Element<*> { - const columns = this.props.aggrow.columns.filter(this.props.filter); - const selected = columns.findIndex( - (c: AggrowColumn): boolean => c.name === this.props.selected); - return ( - - ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/Draggable.jsx b/local-cli/server/middleware/heapCapture/src/Draggable.jsx deleted file mode 100644 index 0bf99039d30d0c..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/Draggable.jsx +++ /dev/null @@ -1,26 +0,0 @@ -// @flow - -import React from 'react'; - -type Props = { - id: string, - children?: any, -} - -export default class Draggable extends React.Component { - props: Props; - - _handleDragStart = (e: SyntheticDragEvent) => { - e.dataTransfer.setData('text', this.props.id); - } - - render(): React.Element<*> { - return React.cloneElement( - React.Children.only(this.props.children), - { - draggable: 'true', - onDragStart: this._handleDragStart, - } - ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/DropTarget.jsx b/local-cli/server/middleware/heapCapture/src/DropTarget.jsx deleted file mode 100644 index 83365eaf0e576a..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/DropTarget.jsx +++ /dev/null @@ -1,33 +0,0 @@ -// @flow - -import React from 'react'; - -type Props = { - id: string, - dropAction: (sourceId: string, thisId: string) => void, - children?: any, -} - -export default class DropTarget extends React.Component { - props: Props; - - _handleDragOver = (e: SyntheticDragEvent) => { - e.preventDefault(); - } - - _handleDrop = (e: SyntheticDragEvent) => { - const sourceId = e.dataTransfer.getData('text'); - e.preventDefault(); - this.props.dropAction(sourceId, this.props.id); - } - - render(): React.Element<*> { - return React.cloneElement( - React.Children.only(this.props.children), - { - onDragOver: this._handleDragOver, - onDrop: this._handleDrop, - } - ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx b/local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx deleted file mode 100644 index 728f1dfb4e0118..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx +++ /dev/null @@ -1,29 +0,0 @@ -// @flow - -import React from 'react'; - -import AggrowExpander from './AggrowExpander'; -import Draggable from './Draggable'; - -type Props = { - expander: AggrowExpander; - id: number; -} - -export default function ExpanderConfiguration(props: Props): React.Element<*> { - const expander = props.expander; - const id = props.id; - return ( - -
- {expander.getExpanderName(id)} -
-
- ); -} diff --git a/local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx b/local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx deleted file mode 100644 index 477165ba5121fb..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx +++ /dev/null @@ -1,152 +0,0 @@ -// @flow -/* eslint-disable jsx-a11y/label-has-for */ - -import invariant from 'invariant'; -import React from 'react'; - -import Aggrow from './Aggrow'; -import { AggrowStackColumn } from './AggrowData'; -import type { AggrowColumn } from './AggrowData'; -import type { FocusConfig } from './Aggrow'; - -type Props = { - aggrow: Aggrow, - onCreate: (expanderId: number) => void, -} - -type State = { - column: string, - pattern: string, - reverse: boolean, - leftSide: boolean, - firstMatch: boolean, -} - -export default class StackExpanderCreator extends React.Component { - constructor(props: Props) { - super(props); - const data = this.props.aggrow.data; - const firstColumn = data.columns.find(isStackColumn); - this.state = { - column: firstColumn ? firstColumn.name : '', - pattern: '', - reverse: false, - leftSide: false, - firstMatch: true, - }; - } - props: Props; - - state: State; - - _handleColumnSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLSelectElement, 'Expected select element'); - this.setState({ column: e.target.value }); - } - - _handlePatternSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ pattern: e.target.value }); - } - - _handleReverseSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ reverse: e.target.checked }); - } - - _handleFirstMatchSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ firstMatch: e.target.checked }); - } - - _handleLeftSideSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ leftSide: e.target.checked }); - } - - _handleCreateClicked = () => { - let focus: FocusConfig; - let expanderName = this.state.column; - if (this.state.pattern !== '') { - focus = { - pattern: new RegExp(this.state.pattern), - firstMatch: this.state.firstMatch, - leftSide: this.state.leftSide, - }; - expanderName += this.state.reverse ? ' reversed' : ''; - expanderName += this.state.leftSide ? ' before' : ' after'; - expanderName += this.state.firstMatch ? ' first ' : ' last '; - expanderName += this.state.pattern; - } - - this.props.onCreate( - this.props.aggrow.addStackExpander( - expanderName, - this.state.column, - this.state.reverse, - focus, - )); - } - - render(): React.Element<*> { - const data = this.props.aggrow.data; - const stackColumns = data.columns.filter(isStackColumn); - return ( -
- - - - - - -
- ); - } -} - -function isStackColumn(c: AggrowColumn): boolean { - return c instanceof AggrowStackColumn; -} diff --git a/local-cli/server/middleware/heapCapture/src/StackRegistry.js b/local-cli/server/middleware/heapCapture/src/StackRegistry.js deleted file mode 100644 index 5964ffcbc84131..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/StackRegistry.js +++ /dev/null @@ -1,96 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -export type Stack = { - id: number, - [frameId: number]: Stack, -} - -export type FlattenedStack = Int32Array; - -type StackIdMap = Array; - -export default class StackRegistry { - root: ?Stack = { id: 0 }; - nodeCount: number = 1; - maxDepth: number = -1; - stackIdMap: ?StackIdMap = null; - - insert(parent: Stack, frameId: number): Stack { - invariant(this.stackIdMap === null, 'Stacks already flattened!'); - let node = parent[frameId]; - if (node === undefined) { - node = { id: this.nodeCount }; - this.nodeCount += 1; - - // TODO: make a builder instead of mutating the array? - parent[frameId] = node; // eslint-disable-line no-param-reassign - } - return node; - } - - get(id: number): FlattenedStack { - invariant(this.stackIdMap, 'Stacks not flattened!'); - return this.stackIdMap[id]; - } - - flatten() { - if (this.stackIdMap !== null) { - return; - } - let stackFrameCount = 0; - function countStacks(tree: Stack, depth: number): boolean { - let leaf = true; - Object.keys(tree).forEach((frameId: any) => { - if (frameId !== 'id') { - leaf = countStacks(tree[Number(frameId)], depth + 1); - } - }); - if (leaf) { - stackFrameCount += depth; - } - return false; - } - const root = this.root; - invariant(root, 'Stacks already flattened'); - countStacks(root, 0); - const stackIdMap = new Array(this.nodeCount); - const stackArray = new Int32Array(stackFrameCount); - let maxStackDepth = 0; - stackFrameCount = 0; - function flattenStacksImpl(tree: Stack, stack: Array): void { - let childStack; - maxStackDepth = Math.max(maxStackDepth, stack.length); - Object.keys(tree).forEach((frameId: any) => { - if (frameId !== 'id') { - stack.push(Number(frameId)); - childStack = flattenStacksImpl(tree[frameId], stack); - stack.pop(); - } - }); - - const id = tree.id; - invariant( - id >= 0 && id < stackIdMap.length && stackIdMap[id] === undefined, - 'Invalid stack ID!'); - - if (childStack !== undefined) { - // each child must have our stack as a prefix, so just use that - stackIdMap[id] = childStack.subarray(0, stack.length); - } else { - const newStack = stackArray.subarray(stackFrameCount, stackFrameCount + stack.length); - stackFrameCount += stack.length; - for (let i = 0; i < stack.length; i++) { - newStack[i] = stack[i]; - } - stackIdMap[id] = newStack; - } - return stackIdMap[id]; - } - flattenStacksImpl(root, []); - this.root = null; - this.stackIdMap = stackIdMap; - this.maxDepth = maxStackDepth; - } -} diff --git a/local-cli/server/middleware/heapCapture/src/StringInterner.js b/local-cli/server/middleware/heapCapture/src/StringInterner.js deleted file mode 100644 index ad7b50804ab5f5..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/StringInterner.js +++ /dev/null @@ -1,26 +0,0 @@ -// @flow - -type InternedStringsTable = { - [key: string]: number, -} - -export default class StringInterner { - strings: Array = []; - ids: InternedStringsTable = {}; - - intern(s: string): number { - const find = this.ids[s]; - if (find === undefined) { - const id = this.strings.length; - this.ids[s] = id; - this.strings.push(s); - return id; - } - - return find; - } - - get(id: number): string { - return this.strings[id]; - } -} diff --git a/local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx b/local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx deleted file mode 100644 index 98dc71aa455cec..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx +++ /dev/null @@ -1,86 +0,0 @@ -// @flow - -import React from 'react'; - -import Aggrow from './Aggrow'; -import ExpanderConfiguration from './ExpanderConfiguration'; -import StackExpanderCreator from './StackExpanderCreator'; - -type State = { - expanded: boolean; -} - -type Props = { - aggrow: Aggrow, - onUpdate: () => void, -} - -export default class TableConfiguration extends React.Component { - props: Props; - - state: State = { - expanded: false, - } - - _handleUpdate = () => { - this.props.onUpdate(); - } - - _toggleExpanded = () => { - this.setState({ expanded: !this.state.expanded }); - } - - renderExpander(id: number): React.Element<*> { - return (); - } - - render(): React.Element<*> { - const expanderText = this.state.expanded ? '>>' : '<<'; - const expander = this.props.aggrow.expander; - let config = []; - if (this.state.expanded) { - config = expander.getExpanders().map( - (ex: number): React.Element<*> => this.renderExpander(ex)); - } - return ( -
-
- { expanderText } -
-
- { config } -
-
- -
-
- ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/TableHeader.jsx b/local-cli/server/middleware/heapCapture/src/TableHeader.jsx deleted file mode 100644 index 3326a2d85fd7fe..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/TableHeader.jsx +++ /dev/null @@ -1,103 +0,0 @@ -// @flow - -import React from 'react'; - -import Aggrow from './Aggrow'; -import Draggable from './Draggable'; -import DropTarget from './DropTarget'; - -type Props = { - aggrow: Aggrow, - dropAction: (sourceId: string, thisId: string) => void, - selectedExpander: ?number, -} - -export default function TableHeader(props: Props): React.Element<*> { - const expander = props.aggrow.expander; - const aggregators = expander.getActiveAggregators(); - const expanders = expander.getActiveExpanders(); - const headers = []; - for (let i = 0; i < aggregators.length; i++) { - const name = expander.getAggregatorName(aggregators[i]); - headers.push(( - -
- )); - headers.push(( - -
{name}
-
)); - } - headers.push(( - -
- )); - for (let i = 0; i < expanders.length; i++) { - const name = expander.getExpanderName(expanders[i]); - headers.push(( - -
- {name} -
-
)); - const sep = i + 1 < expanders.length ? '->' : '...'; - headers.push(( - -
- {sep} -
-
) - ); - } - return ( -
- {headers} -
- ); -} diff --git a/local-cli/server/middleware/heapCapture/src/heapCapture.js b/local-cli/server/middleware/heapCapture/src/heapCapture.js deleted file mode 100644 index a408889886348f..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/heapCapture.js +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Copyright (c) 2016-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. - */ -'use strict'; -/*eslint no-console-disallow: "off"*/ -/*global preLoadedCapture:true*/ - -import ReactDOM from 'react-dom'; -import React from 'react'; -import { - Aggrow, - AggrowData, - AggrowTable, - StringInterner, - StackRegistry, -} from './index.js'; - -function RefVisitor(refs, id) { - this.refs = refs; - this.id = id; -} - -RefVisitor.prototype = { - moveToEdge: function moveToEdge(name) { - const ref = this.refs[this.id]; - if (ref && ref.edges) { - const edges = ref.edges; - for (const edgeId in edges) { - if (edges[edgeId] === name) { - this.id = edgeId; - return this; - } - } - } - this.id = undefined; - return this; - }, - moveToFirst: function moveToFirst(callback) { - const ref = this.refs[this.id]; - if (ref && ref.edges) { - const edges = ref.edges; - for (const edgeId in edges) { - this.id = edgeId; - if (callback(edges[edgeId], this)) { - return this; - } - } - } - this.id = undefined; - return this; - }, - forEachEdge: function forEachEdge(callback) { - const ref = this.refs[this.id]; - if (ref && ref.edges) { - const edges = ref.edges; - const visitor = new RefVisitor(this.refs, undefined); - for (const edgeId in edges) { - visitor.id = edgeId; - callback(edges[edgeId], visitor); - } - } - }, - getType: function getType() { - const ref = this.refs[this.id]; - if (ref) { - return ref.type; - } - return undefined; - }, - getRef: function getRef() { - return this.refs[this.id]; - }, - clone: function clone() { - return new RefVisitor(this.refs, this.id); - }, - isDefined: function isDefined() { - return !!this.id; - }, - getValue: function getValue() { - const ref = this.refs[this.id]; - if (ref) { - if (ref.type === 'string') { - if (ref.value) { - return ref.value; - } else { - const rope = []; - this.forEachEdge((name, visitor) => { - if (name && name.startsWith('[') && name.endsWith(']')) { - const index = parseInt(name.substring(1, name.length - 1), 10); - rope[index] = visitor.getValue(); - } - }); - return rope.join(''); - } - } else if (ref.type === 'ScriptExecutable' - || ref.type === 'EvalExecutable' - || ref.type === 'ProgramExecutable') { - return ref.value.url + ':' + ref.value.line + ':' + ref.value.col; - } else if (ref.type === 'FunctionExecutable') { - return ref.value.name + '@' + ref.value.url + ':' + ref.value.line + ':' + ref.value.col; - } else if (ref.type === 'NativeExecutable') { - return ref.value.function + ' ' + ref.value.constructor + ' ' + ref.value.name; - } else if (ref.type === 'Function') { - const executable = this.clone().moveToEdge('@Executable'); - if (executable.id) { - return executable.getRef().type + ' ' + executable.getValue(); - } - } - } - return '#none'; - } -}; - -function forEachRef(refs, callback) { - const visitor = new RefVisitor(refs, undefined); - for (const id in refs) { - visitor.id = id; - callback(visitor); - } -} - -function firstRef(refs, callback) { - for (const id in refs) { - const ref = refs[id]; - if (callback(id, ref)) { - return new RefVisitor(refs, id); - } - } - return new RefVisitor(refs, undefined); -} - -function getInternalInstanceName(visitor) { - const type = visitor.clone().moveToEdge('_currentElement').moveToEdge('type'); - if (type.getType() === 'string') { // element.type is string - return type.getValue(); - } else if (type.getType() === 'Function') { // element.type is function - const displayName = type.clone().moveToEdge('displayName'); - if (displayName.isDefined()) { - return displayName.getValue(); // element.type.displayName - } - const name = type.clone().moveToEdge('name'); - if (name.isDefined()) { - return name.getValue(); // element.type.name - } - type.moveToEdge('@Executable'); - if (type.getType() === 'FunctionExecutable') { - return type.getRef().value.name; // element.type symbolicated name - } - } - return '#unknown'; -} - -function buildReactComponentTree(visitor, registry, strings) { - const ref = visitor.getRef(); - if (ref.reactTree || ref.reactParent === undefined) { - return; // has one or doesn't need one - } - const parentVisitor = ref.reactParent; - if (parentVisitor === null) { - ref.reactTree = registry.insert(registry.root, strings.intern(getInternalInstanceName(visitor))); - } else if (parentVisitor) { - const parentRef = parentVisitor.getRef(); - buildReactComponentTree(parentVisitor, registry, strings); - let relativeName = getInternalInstanceName(visitor); - if (ref.reactKey) { - relativeName = ref.reactKey + ': ' + relativeName; - } - ref.reactTree = registry.insert(parentRef.reactTree, strings.intern(relativeName)); - } else { - throw 'non react instance parent of react instance'; - } -} - -function markReactComponentTree(refs, registry, strings) { - // annotate all refs that are react internal instances with their parent and name - // ref.reactParent = visitor that points to parent instance, - // null if we know it's an instance, but don't have a parent yet - // ref.reactKey = if a key is used to distinguish siblings - forEachRef(refs, (visitor) => { - const visitorClone = visitor.clone(); // visitor will get stomped on next iteration - const ref = visitor.getRef(); - visitor.forEachEdge((edgeName, edgeVisitor) => { - const edgeRef = edgeVisitor.getRef(); - if (edgeRef) { - if (edgeName === '_renderedChildren') { - if (ref.reactParent === undefined) { - // ref is react component, even if we don't have a parent yet - ref.reactParent = null; - } - edgeVisitor.forEachEdge((childName, childVisitor) => { - const childRef = childVisitor.getRef(); - if (childRef && childName.startsWith('.')) { - childRef.reactParent = visitorClone; - childRef.reactKey = childName; - } - }); - } else if (edgeName === '_renderedComponent') { - if (ref.reactParent === undefined) { - ref.reactParent = null; - } - edgeRef.reactParent = visitorClone; - } - } - }); - }); - // build tree of react internal instances (since that's what has the structure) - // fill in ref.reactTree = path registry node - forEachRef(refs, (visitor) => { - buildReactComponentTree(visitor, registry, strings); - }); - // hook in components by looking at their _reactInternalInstance fields - forEachRef(refs, (visitor) => { - const ref = visitor.getRef(); - const instanceRef = visitor.moveToEdge('_reactInternalInstance').getRef(); - if (instanceRef) { - ref.reactTree = instanceRef.reactTree; - } - }); -} - -function functionUrlFileName(visitor) { - const executable = visitor.clone().moveToEdge('@Executable'); - const ref = executable.getRef(); - if (ref && ref.value && ref.value.url) { - const url = ref.value.url; - let file = url.substring(url.lastIndexOf('/') + 1); - if (file.endsWith('.js')) { - file = file.substring(0, file.length - 3); - } - return file; - } - return undefined; -} - -function markModules(refs) { - const modules = firstRef(refs, (id, ref) => ref.type === 'CallbackGlobalObject'); - modules.moveToEdge('require'); - modules.moveToFirst((name, visitor) => visitor.getType() === 'JSActivation'); - modules.moveToEdge('modules'); - modules.forEachEdge((name, visitor) => { - const ref = visitor.getRef(); - visitor.moveToEdge('exports'); - if (visitor.getType() === 'Object') { - visitor.moveToFirst((memberName, member) => member.getType() === 'Function'); - if (visitor.isDefined()) { - ref.module = functionUrlFileName(visitor); - } - } else if (visitor.getType() === 'Function') { - const displayName = visitor.clone().moveToEdge('displayName'); - if (displayName.isDefined()) { - ref.module = displayName.getValue(); - } - ref.module = functionUrlFileName(visitor); - } - if (ref && !ref.module) { - ref.module = '#unknown ' + name; - } - }); -} - -function registerPathToRootBFS(breadth, registry, strings) { - while (breadth.length > 0) { - const nextBreadth = []; - for (let i = 0; i < breadth.length; i++) { - const visitor = breadth[i]; - const ref = visitor.getRef(); - visitor.forEachEdge((edgeName, edgeVisitor) => { - const edgeRef = edgeVisitor.getRef(); - if (edgeRef && edgeRef.rootPath === undefined) { - let pathName = edgeRef.type; - if (edgeName) { - pathName = edgeName + ': ' + pathName; - } - edgeRef.rootPath = registry.insert(ref.rootPath, strings.intern(pathName)); - nextBreadth.push(edgeVisitor.clone()); - // copy module and react tree forward - if (edgeRef.module === undefined) { - edgeRef.module = ref.module; - } - if (edgeRef.reactTree === undefined) { - edgeRef.reactTree = ref.reactTree; - } - } - }); - } - breadth = nextBreadth; - } -} - -function registerPathToRoot(capture, registry, strings) { - const refs = capture.refs; - const roots = capture.roots; - markReactComponentTree(refs, registry, strings); - markModules(refs); - let breadth = []; - // BFS from global objects first - forEachRef(refs, (visitor) => { - const ref = visitor.getRef(); - if (ref.type === 'CallbackGlobalObject') { - ref.rootPath = registry.insert(registry.root, strings.intern(ref.type)); - breadth.push(visitor.clone()); - } - }); - registerPathToRootBFS(breadth, registry, strings); - breadth = []; - // lower priority, BFS from other roots - for (const id of roots) { - const visitor = new RefVisitor(refs, id); - const ref = visitor.getRef(); - if (ref.rootPath === undefined) { - ref.rootPath = registry.insert(registry.root, strings.intern(`root ${id}: ${ref.type}`)); - breadth.push(visitor.clone()); - } - } - registerPathToRootBFS(breadth, registry, strings); -} - -function registerCapture(data, captureId, capture, stacks, strings) { - // NB: capture.refs is potentially VERY large, so we try to avoid making - // copies, even if iteration is a bit more annoying. - let rowCount = 0; - for (const id in capture.refs) { // eslint-disable-line no-unused-vars - rowCount++; - } - for (const id in capture.markedBlocks) { // eslint-disable-line no-unused-vars - rowCount++; - } - const inserter = data.rowInserter(rowCount); - registerPathToRoot(capture, stacks, strings); - const noneString = strings.intern('#none'); - const noneStack = stacks.insert(stacks.root, noneString); - forEachRef(capture.refs, (visitor) => { - // want to data.append(value, value, value), not IDs - const ref = visitor.getRef(); - const id = visitor.id; - inserter.insertRow( - parseInt(id, 16), - ref.type, - ref.size, - ref.cellSize, - captureId, - ref.rootPath === undefined ? noneStack : ref.rootPath, - ref.reactTree === undefined ? noneStack : ref.reactTree, - visitor.getValue(), - ref.module === undefined ? '#none' : ref.module, - ); - }); - for (const id in capture.markedBlocks) { - const block = capture.markedBlocks[id]; - inserter.insertRow( - parseInt(id, 16), - 'Marked Block Overhead', - block.capacity - block.size, - 0, - captureId, - noneStack, - noneStack, - 'capacity: ' + block.capacity + ', size: ' + block.size + ', granularity: ' + block.cellSize, - '#none', - ); - } - inserter.done(); -} - -if (preLoadedCapture) { - const strings = new StringInterner(); - const stacks = new StackRegistry(); - const columns = [ - { name: 'id', type: 'int' }, - { name: 'type', type: 'string', strings: strings }, - { name: 'size', type: 'int' }, - { name: 'cell', type: 'int' }, - { name: 'trace', type: 'string', strings: strings }, - { name: 'path', type: 'stack', stacks: stacks, getter: x => strings.get(x), formatter: x => x }, - { name: 'react', type: 'stack', stacks: stacks, getter: x => strings.get(x), formatter: x => x }, - { name: 'value', type: 'string', strings: strings }, - { name: 'module', type: 'string', strings: strings }, - ]; - const data = new AggrowData(columns); - registerCapture(data, 'trace', preLoadedCapture, stacks, strings); - preLoadedCapture = undefined; // let GG clean up the capture - const aggrow = new Aggrow(data); - aggrow.addPointerExpander('Id', 'id'); - const typeExpander = aggrow.addStringExpander('Type', 'type'); - aggrow.addNumberExpander('Size', 'size'); - aggrow.addStringExpander('Trace', 'trace'); - const pathExpander = aggrow.addStackExpander('Path', 'path'); - const reactExpander = aggrow.addStackExpander('React Tree', 'react'); - const valueExpander = aggrow.addStringExpander('Value', 'value'); - const moduleExpander = aggrow.addStringExpander('Module', 'module'); - aggrow.expander.setActiveExpanders([ - pathExpander, - reactExpander, - moduleExpander, - typeExpander, - valueExpander, - ]); - const sizeAggregator = aggrow.addSumAggregator('Size', 'size'); - const cellAggregator = aggrow.addSumAggregator('Cell Size', 'cell'); - const countAggregator = aggrow.addCountAggregator('Count'); - aggrow.expander.setActiveAggregators([ - cellAggregator, - sizeAggregator, - countAggregator, - ]); - ReactDOM.render(, document.body); -} diff --git a/local-cli/server/middleware/heapCapture/src/index.js b/local-cli/server/middleware/heapCapture/src/index.js deleted file mode 100644 index dd8acf0d942f52..00000000000000 --- a/local-cli/server/middleware/heapCapture/src/index.js +++ /dev/null @@ -1,24 +0,0 @@ -// @flow - -import Aggrow from './Aggrow'; -import AggrowData from './AggrowData'; -import type { AggrowColumnDef } from './AggrowData'; -import AggrowExpander from './AggrowExpander'; -import AggrowTable from './AggrowTable'; -import StackRegistry from './StackRegistry'; -import type { Stack } from './StackRegistry'; -import StringInterner from './StringInterner'; - -export type { - AggrowColumnDef, - Stack, -}; - -export { - Aggrow, - AggrowData, - AggrowExpander, - AggrowTable, - StackRegistry, - StringInterner, -}; diff --git a/local-cli/server/middleware/heapCapture/webpack.config.js b/local-cli/server/middleware/heapCapture/webpack.config.js deleted file mode 100644 index 1f614fa8429fa5..00000000000000 --- a/local-cli/server/middleware/heapCapture/webpack.config.js +++ /dev/null @@ -1,29 +0,0 @@ -const webpack = require('webpack'); - -module.exports = { - devtool: 'inline-source-map', - entry: './src/heapCapture.js', - resolve: { - extensions: ["", ".js", ".jsx"], - }, - module: { - loaders: [ - { - test: /\.jsx?$/, - include: /\/src\//, - loader: 'babel-loader', - query: { - presets: [ 'react', 'es2015' ], - plugins: [ 'transform-class-properties' ] - }, - }, - ], - }, - plugins: [ - new webpack.BannerPlugin('\n// @generated\n', { raw: true }), - ], - output: { - path: './', - filename: 'bundle.js', - }, -}; diff --git a/local-cli/server/middleware/heapCaptureMiddleware.js b/local-cli/server/middleware/heapCaptureMiddleware.js deleted file mode 100644 index 7f73e5420f6ea8..00000000000000 --- a/local-cli/server/middleware/heapCaptureMiddleware.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright (c) 2016-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. - */ -'use strict'; -/*eslint no-console-disallow: "off"*/ - -const spawn = require('child_process').spawn; -const fs = require('fs'); -const http = require('http'); -const path = require('path'); -const urlLib = require('url'); -const SourceMapConsumer = require('source-map').SourceMapConsumer; - - -// url: string -// onSuccess: function (SourceMapConsumer) -// onFailure: function (string) -function getSourceMapForUrl(url, onFailure, onSuccess) { - if (!url) { - onFailure('must provide a URL'); - return; - } - - if (url === 'assets://default_bundle') { - onFailure('Don\'t know how to symbolicate in-app bundle, please load from server'); - return; - } - - const parsedUrl = urlLib.parse(url); - const mapPath = parsedUrl.pathname.replace(/\.bundle$/, '.map'); - const options = { - host: 'localhost', - port: parsedUrl.port, - path: mapPath + parsedUrl.search + '&babelSourcemap=true', - }; - - http.get(options, (res) => { - res.setEncoding('utf8'); - let sawEnd = false; - let resBody = ''; - res.on('data', (chunk) => { - resBody += chunk; - }).on('end', () => { - sawEnd = true; - onSuccess(new SourceMapConsumer(resBody)); - }).on('close', (err) => { - if (!sawEnd) { - onFailure('Connection terminated prematurely because of: ' - + err.code + ' for url: ' + url); - } - }); - }).on('error', (err) => { - onFailure('Could not get response from: ' + url + ', error: ' + err.message); - }); -} - -// capture: capture object -// onSuccess: function (Map of url -> SourceMapConsumer) -// onFailure: function (string) -function getSourceMapsForCapture(capture, onFailure, onSuccess) { - const urls = new Set(); - const sourcemaps = new Map(); - for (const id in capture.refs) { - const ref = capture.refs[id]; - if ((ref.type === 'ScriptExecutable' || - ref.type === 'EvalExecutable' || - ref.type === 'ProgramExecutable' || - ref.type === 'FunctionExecutable') && ref.value.url) { - urls.add(ref.value.url); - } - } - urls.forEach((url) => { - getSourceMapForUrl(url, onFailure, (sourcemap) => { - sourcemaps.set(url, sourcemap); - urls.delete(url); - if (urls.size === 0) { - onSuccess(sourcemaps); - } - }); - }); - if (urls.size === 0) { - console.warn('No source information found in capture'); - onSuccess(sourcemaps); - } -} - -// capture: capture object -// onSuccess: function (capture object) -// onFailure: function (string) -function symbolicateHeapCaptureFunctions(capture, onFailure, onSuccess) { - getSourceMapsForCapture(capture, onFailure, (sourcemaps) => { - for (const id in capture.refs) { - const ref = capture.refs[id]; - if (ref.type === 'ScriptExecutable' || - ref.type === 'EvalExecutable' || - ref.type === 'ProgramExecutable' || - ref.type === 'FunctionExecutable') { - const sourcemap = sourcemaps.get(ref.value.url); - if (sourcemap) { - const original = sourcemap.originalPositionFor({ - line: ref.value.line, - column: ref.value.col, - }); - if (original.name) { - ref.value.name = original.name; - } else if (!ref.value.name) { - ref.value.name = path.posix.basename(original.source || '') + ':' + original.line; - } - ref.value.url = original.source; - ref.value.line = original.line; - ref.value.col = original.column; - } - } - } - onSuccess(capture); - }); -} - -module.exports = function(req, res, next) { - if (req.url !== '/jscheapcaptureupload') { - next(); - return; - } - - console.log('symbolicating Heap Capture'); - symbolicateHeapCaptureFunctions(JSON.parse(req.rawBody), (err) => { - console.error('Error when symbolicating: ' + err); - }, - (capture) => { - const preload = path.join(__dirname, 'heapCapture/preLoadedCapture.js'); - fs.writeFileSync(preload, 'var preLoadedCapture = '); - fs.appendFileSync(preload, JSON.stringify(capture)); - fs.appendFileSync(preload, ';'); - const captureDir = path.join(__dirname, 'heapCapture/captures'); - if (!fs.existsSync(captureDir)) { - fs.mkdirSync(captureDir); - } - console.log('Packaging Trace'); - var captureHtml = captureDir + '/capture_' + Date.now() + '.html'; - var capture = fs.createWriteStream(captureHtml); - var inliner = spawn( - 'inliner', - ['--nocompress', 'heapCapture.html'], - { cwd: path.join(__dirname, '/heapCapture/'), - stdio: [ process.stdin, 'pipe', process.stderr ], - }); - inliner.stdout.pipe(capture); - inliner.on('error', (err) => { - console.error('Error processing heap capture: ' + err.message); - console.error('make sure you have installed inliner with \'npm install inliner -g\''); - }); - inliner.on('exit', (code, signal) => { - if (code === 0) { - var response = captureHtml; - console.log('Heap capture written to: ' + response); - res.end(response); - } else { - var response = 'Error processing heap capture, inliner returned code: ' + code; - console.error(response); - res.statusCode = 500; - res.end(response); - } - }); - } - ); -}; diff --git a/local-cli/server/runServer.js b/local-cli/server/runServer.js index 09501018b65ae6..5cc924a3bd4946 100644 --- a/local-cli/server/runServer.js +++ b/local-cli/server/runServer.js @@ -20,7 +20,6 @@ const defaultAssetExts = require('../../packager/defaults').assetExts; const defaultPlatforms = require('../../packager/defaults').platforms; const defaultProvidesModuleNodeModules = require('../../packager/defaults').providesModuleNodeModules; const getDevToolsMiddleware = require('./middleware/getDevToolsMiddleware'); -const heapCaptureMiddleware = require('./middleware/heapCaptureMiddleware.js'); const http = require('http'); const indexPageMiddleware = require('./middleware/indexPage'); const loadRawBodyMiddleware = require('./middleware/loadRawBodyMiddleware'); @@ -46,7 +45,6 @@ function runServer(args, config, readyCallback) { .use(copyToClipBoardMiddleware) .use(statusPageMiddleware) .use(systraceProfileMiddleware) - .use(heapCaptureMiddleware) .use(cpuProfilerMiddleware) .use(indexPageMiddleware) .use(unless('/inspector', inspectorProxy.processRequest.bind(inspectorProxy))) From 373eb61f1f411458d4c1c24bb08033bc6d16a316 Mon Sep 17 00:00:00 2001 From: Charles Dick Date: Wed, 15 Mar 2017 06:31:47 -0700 Subject: [PATCH 040/366] Download files through RN packager connection Differential Revision: D4650999 fbshipit-source-id: 298e3e65bfc13a3610a588c9bffb4808fb2135e3 --- .../react/devsupport/DevServerHelper.java | 2 + .../devsupport/DevSupportManagerImpl.java | 29 ++- .../devsupport/DisabledDevSupportManager.java | 5 - .../react/devsupport/JSCHeapUpload.java | 77 ------- .../interfaces/DevSupportManager.java | 1 - .../packagerconnection/FileIoHandler.java | 192 ++++++++++++++++++ .../packagerconnection/JSPackagerClient.java | 1 + 7 files changed, 208 insertions(+), 99 deletions(-) delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/devsupport/JSCHeapUpload.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index 5cebb1bc52fdba..5c3750dfe1ea87 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -32,6 +32,7 @@ import com.facebook.react.common.network.OkHttpCallUtil; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; import com.facebook.react.modules.systeminfo.AndroidInfoHelpers; +import com.facebook.react.packagerconnection.FileIoHandler; import com.facebook.react.packagerconnection.JSPackagerClient; import org.json.JSONException; @@ -149,6 +150,7 @@ public void onRequest(@Nullable Object params, JSPackagerClient.Responder respon commandListener.onPokeSamplingProfilerCommand(responder); } }); + handlers.putAll(new FileIoHandler().handlers()); mPackagerClient = new JSPackagerClient(getPackagerConnectionURL(), handlers); mPackagerClient.init(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java index 28ab652142d904..d141dc9325b585 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java @@ -422,14 +422,6 @@ public void onOptionSelected() { mDevSettings.setFpsDebugEnabled(!mDevSettings.isFpsDebugEnabled()); } }); - options.put( - mApplicationContext.getString(R.string.catalyst_heap_capture), - new DevOptionHandler() { - @Override - public void onOptionSelected() { - handleCaptureHeap(null); - } - }); options.put( mApplicationContext.getString(R.string.catalyst_poke_sampling_profiler), new DevOptionHandler() { @@ -540,11 +532,6 @@ public String getDownloadedJSBundleFile() { return mJSBundleTempFile.getAbsolutePath(); } - @Override - public String getHeapCaptureUploadUrl() { - return mDevServerHelper.getHeapCaptureUploadUrl(); - } - /** * @return {@code true} if {@link com.facebook.react.ReactInstanceManager} should use downloaded JS bundle file * instead of using JS file from assets. This may happen when app has not been updated since @@ -687,7 +674,7 @@ public void run() { } @Override - public void onCaptureHeapCommand(@Nullable final JSPackagerClient.Responder responder) { + public void onCaptureHeapCommand(final JSPackagerClient.Responder responder) { UiThreadUtil.runOnUiThread(new Runnable() { @Override public void run() { @@ -706,14 +693,24 @@ public void run() { }); } - private void handleCaptureHeap(@Nullable final JSPackagerClient.Responder responder) { + private void handleCaptureHeap(final JSPackagerClient.Responder responder) { if (mCurrentContext == null) { return; } JSCHeapCapture heapCapture = mCurrentContext.getNativeModule(JSCHeapCapture.class); heapCapture.captureHeap( mApplicationContext.getCacheDir().getPath(), - JSCHeapUpload.captureCallback(mDevServerHelper.getHeapCaptureUploadUrl(), responder)); + new JSCHeapCapture.CaptureCallback() { + @Override + public void onSuccess(File capture) { + responder.respond(capture.toString()); + } + + @Override + public void onFailure(JSCHeapCapture.CaptureException error) { + responder.error(error.toString()); + } + }); } private void handlePokeSamplingProfiler(@Nullable final JSPackagerClient.Responder responder) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java index 23455e02759875..63f803ca60370a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java @@ -109,11 +109,6 @@ public String getDownloadedJSBundleFile() { return null; } - @Override - public String getHeapCaptureUploadUrl() { - return null; - } - @Override public boolean hasUpToDateJSBundleInCache() { return false; diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/JSCHeapUpload.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/JSCHeapUpload.java deleted file mode 100644 index f678a6d66dd58a..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/JSCHeapUpload.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2016-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. - */ - -package com.facebook.react.devsupport; - -import javax.annotation.Nullable; - -import android.util.Log; -import java.io.File; -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import com.facebook.react.packagerconnection.JSPackagerClient; - -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -/** - * Created by cwdick on 7/22/16. - */ -public class JSCHeapUpload { - public static JSCHeapCapture.CaptureCallback captureCallback( - final String uploadUrl, - @Nullable final JSPackagerClient.Responder responder) { - return new JSCHeapCapture.CaptureCallback() { - @Override - public void onSuccess(File capture) { - OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); - httpClientBuilder.connectTimeout(1, TimeUnit.MINUTES) - .writeTimeout(5, TimeUnit.MINUTES) - .readTimeout(5, TimeUnit.MINUTES); - OkHttpClient httpClient = httpClientBuilder.build(); - RequestBody body = RequestBody.create(MediaType.parse("application/json"), capture); - Request request = new Request.Builder() - .url(uploadUrl) - .method("POST", body) - .build(); - Call call = httpClient.newCall(request); - call.enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - String message = "Upload of heap capture failed: " + e.toString(); - Log.e("JSCHeapCapture", message); - responder.error(message); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - if (!response.isSuccessful()) { - String message = "Upload of heap capture failed with code " + Integer.toString(response.code()) + ": " + response.body().string(); - Log.e("JSCHeapCapture", message); - responder.error(message); - } - responder.respond(response.body().string()); - } - }); - } - - @Override - public void onFailure(JSCHeapCapture.CaptureException e) { - String message = "Heap capture failed: " + e.toString(); - Log.e("JSCHeapCapture", message); - responder.error(message); - } - }; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java index a8fedc356e2517..b6bcdec4eb2797 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java @@ -40,7 +40,6 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler { String getSourceUrl(); String getJSBundleURLForRemoteDebugging(); String getDownloadedJSBundleFile(); - String getHeapCaptureUploadUrl(); boolean hasUpToDateJSBundleInCache(); void reloadSettings(); void handleReloadJS(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.java new file mode 100644 index 00000000000000..5a0adb1dd6b6aa --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2016-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. + */ + +package com.facebook.react.packagerconnection; + +import javax.annotation.Nullable; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import android.os.Handler; +import android.os.Looper; +import android.util.Base64; + +import com.facebook.common.logging.FLog; + +import org.json.JSONObject; + +public class FileIoHandler implements Runnable { + private static final String TAG = JSPackagerClient.class.getSimpleName(); + private static final long FILE_TTL = 30 * 1000; + + private static class TtlFileInputStream { + private final FileInputStream mStream; + private long mTtl; + + public TtlFileInputStream(String path) throws FileNotFoundException { + mStream = new FileInputStream(path); + mTtl = System.currentTimeMillis() + FILE_TTL; + } + + private void extendTtl() { + mTtl = System.currentTimeMillis() + FILE_TTL; + } + + public boolean expiredTtl() { + return System.currentTimeMillis() >= mTtl; + } + + public String read(int size) throws IOException { + extendTtl(); + byte[] buffer = new byte[size]; + int bytesRead = mStream.read(buffer); + return Base64.encodeToString(buffer, 0, bytesRead, Base64.DEFAULT); + } + + public void close() throws IOException { + mStream.close(); + } + }; + + private int mNextHandle; + private final Handler mHandler; + private final Map mOpenFiles; + private final Map mRequestHandlers; + + public FileIoHandler() { + mNextHandle = 1; + mHandler = new Handler(Looper.getMainLooper()); + mOpenFiles = new HashMap<>(); + mRequestHandlers = new HashMap<>(); + mRequestHandlers.put("fopen", new JSPackagerClient.RequestOnlyHandler() { + @Override + public void onRequest( + @Nullable Object params, JSPackagerClient.Responder responder) { + synchronized (mOpenFiles) { + try { + JSONObject paramsObj = (JSONObject)params; + if (paramsObj == null) { + throw new Exception("params must be an object { mode: string, filename: string }"); + } + String mode = paramsObj.optString("mode"); + if (mode == null) { + throw new Exception("missing params.mode"); + } + String filename = paramsObj.optString("filename"); + if (filename == null) { + throw new Exception("missing params.filename"); + } + if (!mode.equals("r")) { + throw new IllegalArgumentException("unsupported mode: " + mode); + } + + responder.respond(addOpenFile(filename)); + } catch (Exception e) { + responder.error(e.toString()); + } + } + } + }); + mRequestHandlers.put("fclose", new JSPackagerClient.RequestOnlyHandler() { + @Override + public void onRequest( + @Nullable Object params, JSPackagerClient.Responder responder) { + synchronized (mOpenFiles) { + try { + if (!(params instanceof Number)) { + throw new Exception("params must be a file handle"); + } + TtlFileInputStream stream = mOpenFiles.get((int)params); + if (stream == null) { + throw new Exception("invalid file handle, it might have timed out"); + } + + mOpenFiles.remove((int)params); + stream.close(); + responder.respond(""); + } catch (Exception e) { + responder.error(e.toString()); + } + } + } + }); + mRequestHandlers.put("fread", new JSPackagerClient.RequestOnlyHandler() { + @Override + public void onRequest( + @Nullable Object params, JSPackagerClient.Responder responder) { + synchronized (mOpenFiles) { + try { + JSONObject paramsObj = (JSONObject)params; + if (paramsObj == null) { + throw new Exception("params must be an object { file: handle, size: number }"); + } + int file = paramsObj.optInt("file"); + if (file == 0) { + throw new Exception("invalid or missing file handle"); + } + int size = paramsObj.optInt("size"); + if (size == 0) { + throw new Exception("invalid or missing read size"); + } + TtlFileInputStream stream = mOpenFiles.get(file); + if (stream == null) { + throw new Exception("invalid file handle, it might have timed out"); + } + + responder.respond(stream.read(size)); + } catch (Exception e) { + responder.error(e.toString()); + } + } + } + }); + } + + public Map handlers() { + return mRequestHandlers; + } + + private int addOpenFile(String filename) throws FileNotFoundException { + int handle = mNextHandle++; + mOpenFiles.put(handle, new TtlFileInputStream(filename)); + if (mOpenFiles.size() == 1) { + mHandler.postDelayed(FileIoHandler.this, FILE_TTL); + } + return handle; + } + + @Override + public void run() { + // clean up files that are past their expiry date + synchronized (mOpenFiles) { + Iterator i = mOpenFiles.values().iterator(); + while (i.hasNext()) { + TtlFileInputStream stream = i.next(); + if (stream.expiredTtl()) { + i.remove(); + try { + stream.close(); + } catch (IOException e) { + FLog.e( + TAG, + "closing expired file failed: " + e.toString()); + } + } + } + if (!mOpenFiles.isEmpty()) { + mHandler.postDelayed(this, FILE_TTL); + } + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java index 5170d46d0cfd47..d8cdc6544d706b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java @@ -10,6 +10,7 @@ import javax.annotation.Nullable; +import java.util.HashMap; import java.util.Map; import com.facebook.common.logging.FLog; From ca6043292adaaa25c44d7a050b122f8980c6f5fe Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Wed, 15 Mar 2017 07:23:23 -0700 Subject: [PATCH 041/366] @build-break codemod ios_library to fb_apple_library Reviewed By: swolchok fbshipit-source-id: cdbb5195b3c279121c79e237cc2c7275447aa625 --- ReactCommon/cxxreact/BUCK | 2 +- ReactCommon/jschelpers/BUCK | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index c82d6a8769d2eb..5b58dbd2dc2c63 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -63,7 +63,7 @@ if THIS_IS_FBANDROID: if THIS_IS_FBOBJC: def react_library(**kwargs): - ios_library( + fb_apple_library( name = 'bridge', header_path_prefix = "cxxreact", inherited_buck_flags = STATIC_LIBRARY_IOS_FLAGS, diff --git a/ReactCommon/jschelpers/BUCK b/ReactCommon/jschelpers/BUCK index 82577f4f475648..ef759f4ca02bb6 100644 --- a/ReactCommon/jschelpers/BUCK +++ b/ReactCommon/jschelpers/BUCK @@ -54,7 +54,7 @@ if THIS_IS_FBANDROID: if THIS_IS_FBOBJC: - ios_library( + fb_apple_library( name = "jscinternalhelpers", inherited_buck_flags = STATIC_LIBRARY_IOS_FLAGS, compiler_flags = [ @@ -75,7 +75,7 @@ if THIS_IS_FBOBJC: ], ) - ios_library( + fb_apple_library( name = "jschelpers", inherited_buck_flags = STATIC_LIBRARY_IOS_FLAGS, compiler_flags = [ From 24183a363c455611b31d964d5a88a9a8d108b66e Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Wed, 15 Mar 2017 07:42:22 -0700 Subject: [PATCH 042/366] packager: Resolver/polyfills/require: fix nit error message Reviewed By: davidaurelio Differential Revision: D4713066 fbshipit-source-id: 35f09ded85472191a3f1c36b5879acee55c60b8b --- packager/src/Resolver/polyfills/require.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packager/src/Resolver/polyfills/require.js b/packager/src/Resolver/polyfills/require.js index f928c6fd80e442..d532f4a90b1ae8 100644 --- a/packager/src/Resolver/polyfills/require.js +++ b/packager/src/Resolver/polyfills/require.js @@ -195,7 +195,8 @@ let message = 'Requiring unknown module "' + id + '".'; if (__DEV__) { message += - 'If you are sure the module is there, try restarting the packager or running "npm install".'; + 'If you are sure the module is there, try restarting the packager. ' + + 'You may also want to run `npm install`, or `yarn` (depending on your environment).'; } return Error(message); } From 6a9c2445ed03de88b3fc46dff3e8913c1aed9dfb Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Wed, 15 Mar 2017 07:43:20 -0700 Subject: [PATCH 043/366] Don't merge before Circle CI tests pass: Re-enable Android e2e test Summary: Disabled earlier today, a fix for HMR landed so let's test it works. Closes https://github.com/facebook/react-native/pull/12943 Differential Revision: D4712940 Pulled By: jeanlauliac fbshipit-source-id: 38cbfb04a0a16a0a59e8eb4c9c26adb60ab5d635 --- circle.yml | 4 +--- scripts/run-ci-e2e-tests.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/circle.yml b/circle.yml index 945e6b7412ff4c..00cad8d46afdfb 100644 --- a/circle.yml +++ b/circle.yml @@ -76,9 +76,7 @@ test: - node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests # Android e2e test - # Temp disabled to debug an ongoing issue (so far we think it's packager related). - # Re-enable this ASAP once the issue is fixed. - #- source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2 + - source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2 # testing docs generation - cd website && npm test diff --git a/scripts/run-ci-e2e-tests.js b/scripts/run-ci-e2e-tests.js index fea5078d93bc2d..b9b5a9abdb8472 100644 --- a/scripts/run-ci-e2e-tests.js +++ b/scripts/run-ci-e2e-tests.js @@ -12,10 +12,10 @@ /** * This script tests that React Native end to end installation/bootstrap works for different platforms * Available arguments: - * --ios - to test only ios application end to end - * --tvos - to test only tvOS application end to end - * --android - to test only android application end to end - * --js - to test that JS in the application is compilable + * --ios - 'react-native init' and check iOS app doesn't redbox + * --tvos - 'react-native init' and check tvOS app doesn't redbox + * --android - 'react-native init' and check Android app doesn't redbox + * --js - 'react-native init' and only check the packager returns a bundle * --skip-cli-install - to skip react-native-cli global installation (for local debugging) * --retries [num] - how many times to retry possible flaky commands: npm install and running tests, default 1 */ @@ -113,16 +113,18 @@ try { cd('..'); exec('keytool -genkey -v -keystore android/keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"'); - echo(`Starting packager server, ${SERVER_PID}`); + echo(`Starting appium server, ${APPIUM_PID}`); const appiumProcess = spawn('node', ['./node_modules/.bin/appium']); APPIUM_PID = appiumProcess.pid; - echo(`Starting appium server, ${APPIUM_PID}`); - echo('Building app'); + + echo('Building the app'); if (exec('buck build android/app').code) { echo('could not execute Buck build, is it installed and in PATH?'); exitCode = 1; throw Error(exitCode); } + + echo(`Starting packager server, ${SERVER_PID}`); const packagerEnv = Object.create(process.env); packagerEnv.REACT_NATIVE_MAX_WORKERS = 1; // shelljs exec('', {async: true}) does not emit stdout events, so we rely on good old spawn From 5873a228f41bca763c35c470375a9f3cb36f80ab Mon Sep 17 00:00:00 2001 From: Andy Street Date: Wed, 15 Mar 2017 07:49:53 -0700 Subject: [PATCH 044/366] Fix crash if native code tries to update the size of a modal view after JS has removed it Summary: See https://github.com/facebook/react-native/issues/10845 onSizeChanged is enqueueing a runnable from the ui thread that references a shadow view by id on the native modules thread. Since the shadow thread 'runs ahead' of the UI thread (e.g. it has a newer state of the world than the UI thread), this isn't safe. By the time that code block runs, it's possible that an update from JS has already removed that view from the shadow hierarchy (a change which would then propagate to the native hierarchy on the UI thread). Reviewed By: AaaChiuuu Differential Revision: D4706521 fbshipit-source-id: 0915f081068709b895f70b2edce12163b39e5456 --- .../java/com/facebook/react/uimanager/UIImplementation.java | 6 ++++++ .../com/facebook/react/views/modal/ReactModalHostView.java | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index e6499c43deff6e..ab6e6f2aa998e3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -153,6 +153,12 @@ public void updateNodeSize( int newWidth, int newHeight) { ReactShadowNode cssNode = mShadowNodeRegistry.getNode(nodeViewTag); + if (cssNode == null) { + FLog.w( + ReactConstants.TAG, + "Tried to update size of non-existent tag: " + nodeViewTag); + return; + } cssNode.setStyleWidth(newWidth); cssNode.setStyleHeight(newHeight); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index 50a00923b211d8..674c6ef80675b6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -304,13 +304,14 @@ public DialogRootViewGroup(Context context) { protected void onSizeChanged(final int w, final int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (getChildCount() > 0) { + final int viewTag = getChildAt(0).getId(); ReactContext reactContext = (ReactContext) getContext(); reactContext.runOnNativeModulesQueueThread( new GuardedRunnable(reactContext) { @Override public void runGuarded() { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class) - .updateNodeSize(getChildAt(0).getId(), w, h); + .updateNodeSize(viewTag, w, h); } }); } From 6eb9ee26525793d73bcf2988449eea7535504fb3 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Wed, 15 Mar 2017 13:35:36 -0700 Subject: [PATCH 045/366] Missing @DoNotStrip Reviewed By: javache Differential Revision: D4715719 fbshipit-source-id: c689ea68efafeb353382c8c3b72f5616932ad761 --- .../main/java/com/facebook/react/cxxbridge/ModuleHolder.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ModuleHolder.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ModuleHolder.java index 52ac2059e11be7..6226bf5be463cd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ModuleHolder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ModuleHolder.java @@ -5,6 +5,7 @@ import javax.annotation.Nullable; import javax.inject.Provider; +import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; @@ -75,6 +76,7 @@ public synchronized void destroy() { } } + @DoNotStrip public String getName() { return mName; } @@ -87,6 +89,7 @@ public boolean getSupportsWebWorkers() { return mSupportsWebWorkers; } + @DoNotStrip public synchronized NativeModule getModule() { if (mModule == null) { mModule = create(); From 6336e4d41eccbe83212db88d00a73450f8353d3b Mon Sep 17 00:00:00 2001 From: Maxime Aoustin Date: Wed, 15 Mar 2017 14:07:21 -0700 Subject: [PATCH 046/366] Update Podfile documentation for RN >= 0.42.0 Summary: I've updated the documentation that gives an example of Podfile adding a note that from React Native 0.42.0, users have to explicitly include Yoga in their Podfile. I didn't want to modify existing `Podfile` from this documentation because they refer to `Podfile` from existing projects that do not use RN 0.42.0. So I just added a note and an example to explain how to include Yoga. This PR is to fix the following issue: https://github.com/facebook/react-native/issues/12670 Closes https://github.com/facebook/react-native/pull/12728 Differential Revision: D4716172 Pulled By: hramos fbshipit-source-id: 12a6e61b0d426c776a7cc2dea3ac22a50ffe56ef --- docs/IntegrationWithExistingApps.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/IntegrationWithExistingApps.md b/docs/IntegrationWithExistingApps.md index 3afa8669bb3f58..e4fa55e7f69c0b 100644 --- a/docs/IntegrationWithExistingApps.md +++ b/docs/IntegrationWithExistingApps.md @@ -218,6 +218,8 @@ target 'NumberTileGame' do 'RCTWebSocket', # needed for debugging # Add any other subspecs you want to use in your project ] + # Explicitly include Yoga if you are using RN >= 0.42.0 + pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga" end ``` @@ -243,6 +245,8 @@ target 'swift-2048' do 'RCTWebSocket', # needed for debugging # Add any other subspecs you want to use in your project ] + # Explicitly include Yoga if you are using RN >= 0.42.0 + pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga" end ``` From 7f4054df76b6fdf44ef84a89487ef1381e078b95 Mon Sep 17 00:00:00 2001 From: Mike Adams Date: Wed, 15 Mar 2017 16:53:17 -0700 Subject: [PATCH 047/366] Update FlatList.js Summary: Only a small amendment, but took me a little bit to figure out why this wasn't working. Closes https://github.com/facebook/react-native/pull/12706 Differential Revision: D4656700 fbshipit-source-id: d6038581aa70e96a2be775a7a9786e8c7e64f762 --- Libraries/CustomComponents/Lists/FlatList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Lists/FlatList.js b/Libraries/CustomComponents/Lists/FlatList.js index 10a47cc46974c5..5fedff5d612446 100644 --- a/Libraries/CustomComponents/Lists/FlatList.js +++ b/Libraries/CustomComponents/Lists/FlatList.js @@ -51,7 +51,7 @@ type RequiredProps = { * _renderItem = ({item}) => ( * this._onPress(item)}> * {item.title}} - * + * * ); * ... * From 28bb1960a40fea21494f9cc707b19901962a1561 Mon Sep 17 00:00:00 2001 From: Hector Ramos Date: Wed, 15 Mar 2017 17:24:39 -0700 Subject: [PATCH 048/366] Shorten template instructions. Summary: I've noticed many pull requests are opened with a very short description that fails to explain the motivation behind the PR, and many more forego providing a test plan. With this PR, I am shortening the template in order to provide concise instructions that provide the essential steps above the fold in the "Open a pull request" composer window. - We need people to open a PR against master, not stable. The exact reason for this is not important and providing an explanation takes away from other more important points. - Test plans are essential, but their requirement appears below the fold in the current template. - More PRs could use tests. - Make a point of asking PR authors to follow up on CI test failures. - The composer does not parse Markdown into HTML. Markdown is pretty readable as is, but using reference links instead of inline links should help with readability. I observed that it will only display the first 8 lines of the PR template above the fold. Seeing t Closes https://github.com/facebook/react-native/pull/12958 Differential Revision: D4718294 Pulled By: mkonicek fbshipit-source-id: b19a29e5ed73fb78d09c7de17625b1883590075c --- .github/PULL_REQUEST_TEMPLATE.md | 33 ++++++++++++++++-------- CONTRIBUTING.md | 43 ++++++++++++++++---------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7da24f09442644..de18092adff89b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,19 +1,32 @@ -Thanks for submitting a pull request! Please provide enough information so that others can review your pull request: +Thanks for submitting a PR! Please read these instructions carefully: -> **Unless you are a React Native release maintainer and cherry-picking an *existing* commit into a current release, ensure your pull request is targeting the `master` React Native branch.** +- [ ] Explain the **motivation** for making this change. +- [ ] Provide a **test plan** demonstrating that the code is solid. +- [ ] Match the **code formatting** of the rest of the codebase. +- [ ] Target the `master` branch, NOT a "stable" branch. -Explain the **motivation** for making this change. What existing problem does the pull request solve? +## Motivation (required) -Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. +What existing problem does the pull request solve? -**Test plan (required)** +## Test Plan (required) -Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. +A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI or updates the website. See [What is a Test Plan?][1] to learn more. -Make sure tests pass on both Travis and Circle CI. +If you have added code that should be tested, add tests. -**Code formatting** +## Next Steps -Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide). +Sign the [CLA][2], if you haven't already. -For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests). +Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. + +Make sure all **tests pass** on both [Travis][3] and [Circle CI][4]. PRs that break tests are unlikely to be merged. + +For more info, see the ["Pull Requests"][5] section of our "Contributing" guidelines. + +[1]: https://medium.com/@martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9 +[2]: https://code.facebook.com/cla +[3]: https://travis-ci.org/facebook/react-native +[4]: http://circleci.com/gh/facebook/react-native +[5]: https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d460766a81a844..59731909c8a3cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,29 +14,30 @@ We will do our best to keep `master` in good shape, with tests passing at all ti The core team will be monitoring for pull requests. When we get one, we'll run some Facebook-specific integration tests on it first. From here, we'll need to get another person to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process. -**Please submit your pull request on the `master` branch**. If the fix is critical and should be included in a stable branch please mention it and it will be cherry picked into it. +**Please submit your pull request on the `master` branch**. If the fix is critical and should be included in a stable branch please mention it and it will be cherry picked into it by a project maintainer. *Before* submitting a pull request, please make sure the following is done… 1. Fork the repo and create your branch from `master`. -2. **Describe your test plan in your commit.** If you've added code that should be tested, add tests! -3. If you've changed APIs, update the documentation. -4. If you've updated the docs, verify the website locally and submit screenshots if applicable. - - ``` - $ cd website - $ npm install && npm start - go to: http://localhost:8079/react-native/index.html - ``` - -5. Add the copyright notice to the top of any new files you've added. -6. Ensure tests pass on Travis and Circle CI. -7. Make sure your code lints (`node linter.js `). -8. If you haven't already, sign the [CLA](https://code.facebook.com/cla). -9. Squash your commits (`git rebase -i`). - one intent alongs with one commit makes it clearer for people to review and easier to understand your intention - -Note: It is not necessary to keep clicking `Merge master to your branch` on PR page. You would want to merge master if there are conflicts or tests are failing. The facebook-bot ultimately squashes all commits to a single one before merging your PR. +2. **Describe your test plan in your commit.** + - If you've added code that should be tested, add tests! + - If you've changed APIs, update the documentation. + - If you've updated the docs, verify the website locally and submit screenshots if applicable. + + ``` + $ cd website + $ npm install && npm start + Open the following in your browser: http://localhost:8079/react-native/index.html + ``` + +3. Add the copyright notice to the top of any new files you've added. +4. Ensure tests pass on Travis and Circle CI. +5. Make sure your code lints (`node linter.js `). +6. If you haven't already, sign the [CLA](https://code.facebook.com/cla). +7. Squash your commits (`git rebase -i`). + One intent alongside one commit makes it clearer for people to review and easier to understand your intention. + +> **Note:** It is not necessary to keep clicking `Merge master to your branch` on the PR page. You would want to merge master if there are conflicts or tests are failing. The Facebook-GitHub-Bot ultimately squashes all commits to a single one before merging your PR. #### Copyright Notice for files @@ -77,8 +78,8 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe ## How to Get in Touch -* [Facebook group](https://www.facebook.com/groups/react.native.community/) -* Reactiflux — [#react-native](http://join.reactiflux.com/) +* [Facebook](https://www.facebook.com/groups/react.native.community/) +* [Twitter](https://www.twitter.com/reactnative) ## Style Guide From 35046984db2c41ef740cb50e1e60a3dfdcd23056 Mon Sep 17 00:00:00 2001 From: Andrew Jack Date: Wed, 15 Mar 2017 17:32:01 -0700 Subject: [PATCH 049/366] Add TextLayoutBuilder proguard rule Summary: To enable users to use run proguard in their Android applications. Fixes: #11891 [TextLayoutBuilder](https://github.com/facebookincubator/TextLayoutBuilder) is built as a Jar rather than an AAR so the proguard rules cannot be provided with the binary. This PR adds the rules that are included in TextLayoutBuilder https://github.com/facebookincubator/TextLayoutBuilder/blob/master/proguard-android.txt - Created an `react-native init ProguardTest` project with proguard turned on for v0.41.0 - Make sure tests pass on both Travis and Circle CI. Closes https://github.com/facebook/react-native/pull/12511 Differential Revision: D4718029 Pulled By: mkonicek fbshipit-source-id: 9e3f11c8b9b54eb8e52aa6f44cacd00857a882a5 --- local-cli/templates/HelloWorld/android/app/proguard-rules.pro | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/local-cli/templates/HelloWorld/android/app/proguard-rules.pro b/local-cli/templates/HelloWorld/android/app/proguard-rules.pro index 48361a9015dc50..6e8516c8d6dd4b 100644 --- a/local-cli/templates/HelloWorld/android/app/proguard-rules.pro +++ b/local-cli/templates/HelloWorld/android/app/proguard-rules.pro @@ -50,6 +50,10 @@ -dontwarn com.facebook.react.** +# TextLayoutBuilder uses a non-public Android constructor within StaticLayout. +# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. +-dontwarn android.text.StaticLayout + # okhttp -keepattributes Signature From c53404a6889b8f8718d90cda161523c06ebcb43f Mon Sep 17 00:00:00 2001 From: Bin Yue Date: Wed, 15 Mar 2017 17:46:14 -0700 Subject: [PATCH 050/366] Fix TextInput 'defaultValue' propTypes to 'string' Summary: Because `TextInput` only render the `defaultValue` of `string`, when using other types of values, it will render empty('') without any Warnings. Closes https://github.com/facebook/react-native/pull/11478 Differential Revision: D4340132 Pulled By: lacker fbshipit-source-id: aedb96d49277836000c0adc53007c97db6363253 --- Libraries/Components/TextInput/TextInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index e9b2de7d5474e3..d1c3e6fd27f723 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -452,7 +452,7 @@ const TextInput = React.createClass({ * Useful for simple use-cases where you do not want to deal with listening * to events and updating the value prop to keep the controlled state in sync. */ - defaultValue: PropTypes.node, + defaultValue: PropTypes.string, /** * When the clear button should appear on the right side of the text view. * @platform ios From d5dda1b1365321c0bb64eca0d1d88909f0f6c7fd Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Wed, 15 Mar 2017 18:25:35 -0700 Subject: [PATCH 051/366] Adding in missing wrapper invocation. Reviewed By: javache Differential Revision: D4714260 fbshipit-source-id: 641dfd07e8161bedf31cfd4cf94b1dc64beec0f9 --- React/CxxModule/RCTCxxUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/CxxModule/RCTCxxUtils.h b/React/CxxModule/RCTCxxUtils.h index 0f361233836d2e..2278cf42de8371 100644 --- a/React/CxxModule/RCTCxxUtils.h +++ b/React/CxxModule/RCTCxxUtils.h @@ -33,7 +33,7 @@ template <> struct ValueEncoder { static Value toValue(JSGlobalContextRef ctx, NSArray *const __strong array) { - JSValue *value = [JSValue valueWithObject:array inContext:contextForGlobalContextRef(ctx)]; + JSValue *value = [JSC_JSValue(ctx) valueWithObject:array inContext:contextForGlobalContextRef(ctx)]; return {ctx, [value JSValueRef]}; } }; From c8d922b4670127c417d744a0a1412cc6ad961fef Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 16 Mar 2017 07:40:54 -0700 Subject: [PATCH 052/366] BREAKING: Only call batchDidComplete when there were actually native calls dispatched Summary: This is breaking because it affects the contract for onBatchComplete, but modules really shouldn't (and probably aren't) depending on it being called without any actual native module method calls having happened. Reviewed By: javache Differential Revision: D4715656 fbshipit-source-id: 53ddd4a26c9949de86f5111d214b3e5002ca2e94 --- ReactCommon/cxxreact/NativeToJsBridge.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 5c4399766c0704..8c6babfbf4e842 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -52,6 +52,8 @@ class JsToNativeBridge : public react::ExecutorDelegate { "native module calls cannot be completed with no native modules"; ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor); m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable { + m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty(); + // An exception anywhere in here stops processing of the batch. This // was the behavior of the Android bridge, and since exception handling // terminates the whole bridge, there's not much point in continuing. @@ -60,7 +62,10 @@ class JsToNativeBridge : public react::ExecutorDelegate { token, call.moduleId, call.methodId, std::move(call.arguments), call.callId); } if (isEndOfBatch) { - m_callback->onBatchComplete(); + if (m_batchHadNativeModuleCalls) { + m_callback->onBatchComplete(); + m_batchHadNativeModuleCalls = false; + } m_callback->decrementPendingJSCalls(); } }); @@ -88,6 +93,7 @@ class JsToNativeBridge : public react::ExecutorDelegate { std::shared_ptr m_registry; std::unique_ptr m_nativeQueue; std::shared_ptr m_callback; + bool m_batchHadNativeModuleCalls = false; }; NativeToJsBridge::NativeToJsBridge( From bb266715f14f47916adbfb804279cfe35df71121 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Thu, 16 Mar 2017 09:20:55 -0700 Subject: [PATCH 053/366] packager: GlobalTransformCache: finish conversion to async/await Reviewed By: davidaurelio Differential Revision: D4713456 fbshipit-source-id: f65c45439d1657872e28a296f13a73ee2032ea93 --- packager/src/lib/GlobalTransformCache.js | 108 ++++++++++------------- packager/src/node-haste/Module.js | 21 +++-- 2 files changed, 55 insertions(+), 74 deletions(-) diff --git a/packager/src/lib/GlobalTransformCache.js b/packager/src/lib/GlobalTransformCache.js index 70b5979d927ea1..002960df315be5 100644 --- a/packager/src/lib/GlobalTransformCache.js +++ b/packager/src/lib/GlobalTransformCache.js @@ -14,10 +14,10 @@ const BatchProcessor = require('./BatchProcessor'); const crypto = require('crypto'); +const fetch = require('node-fetch'); const imurmurhash = require('imurmurhash'); const jsonStableStringify = require('json-stable-stringify'); const path = require('path'); -const request = require('request'); import type {Options as TransformOptions} from '../JSTransformer/worker/worker'; import type {CachedResult, GetTransformCacheKey} from './TransformCache'; @@ -33,9 +33,6 @@ type FetchProps = { transformOptions: TransformOptions, }; -type FetchCallback = (error?: Error, result?: ?CachedResult) => mixed; -type FetchURICallback = (error?: Error, resultURI?: ?string) => mixed; - type URI = string; /** @@ -64,11 +61,8 @@ class KeyURIFetcher { return keys.map(key => URIsByKey.get(key)); } - fetch(key: string, callback: FetchURICallback) { - this._batchProcessor.queue(key).then( - res => process.nextTick(callback.bind(undefined, undefined, res)), - err => process.nextTick(callback.bind(undefined, err)), - ); + async fetch(key: string): Promise { + return await this._batchProcessor.queue(key); } constructor(fetchResultURIs: FetchResultURIs, processError: (error: Error) => mixed) { @@ -181,20 +175,6 @@ class GlobalTransformCache { _retries: number; _store: ?KeyResultStore; - /** - * If too many errors already happened, we just drop the additional errors. - */ - _processError(error: Error) { - if (this._retries <= 0) { - return; - } - this._reporter.update({type: 'global_cache_error', error}); - --this._retries; - if (this._retries <= 0) { - this._reporter.update({type: 'global_cache_disabled', reason: 'too_many_errors'}); - } - } - /** * For using the global cache one needs to have some kind of central key-value * store that gets prefilled using keyOf() and the transformed results. The @@ -231,30 +211,36 @@ class GlobalTransformCache { return `${digest}-${path.basename(props.filePath)}`; } + /** + * If too many errors already happened, we just drop the additional errors. + */ + _processError(error: Error) { + if (this._retries <= 0) { + return; + } + this._reporter.update({type: 'global_cache_error', error}); + --this._retries; + if (this._retries <= 0) { + this._reporter.update({type: 'global_cache_disabled', reason: 'too_many_errors'}); + } + } + /** * We may want to improve that logic to return a stream instead of the whole * blob of transformed results. However the results are generally only a few * megabytes each. */ - _fetchFromURI(uri: string, callback: FetchCallback) { - request.get({uri, json: true, timeout: 8000}, (error, response, unvalidatedResult) => { - if (error != null) { - callback(error); - return; - } - if (response.statusCode !== 200) { - callback(new Error( - `Unexpected HTTP status code: ${response.statusCode}`, - )); - return; - } - const result = validateCachedResult(unvalidatedResult); - if (result == null) { - callback(new Error('Invalid result returned by server.')); - return; - } - callback(undefined, result); - }); + async _fetchFromURI(uri: string): Promise { + const response = await fetch(uri, {method: 'GET', timeout: 8000}); + if (response.status !== 200) { + throw new Error(`Unexpected HTTP status: ${response.status} ${response.statusText} `); + } + const unvalidatedResult = await response.json(); + const result = validateCachedResult(unvalidatedResult); + if (result == null) { + throw new Error('Server returned invalid result.'); + } + return result; } /** @@ -262,31 +248,27 @@ class GlobalTransformCache { * of errors. This is because the global cache is not critical to the normal * packager operation. */ - _tryFetchingFromURI(uri: string, callback: FetchCallback) { - this._fetchFromURI(uri, (error, result) => { - if (error != null) { - this._processError(error); - } - callback(undefined, result); - }); + async _tryFetchingFromURI(uri: string): Promise { + try { + return await this._fetchFromURI(uri); + } catch (error) { + this._processError(error); + } } - fetch(props: FetchProps, callback: FetchCallback) { + /** + * This may return `null` if either the cache doesn't have a value for that + * key yet, or an error happened, processed separately. + */ + async fetch(props: FetchProps): Promise { if (this._retries <= 0 || !this._profileSet.has(props.transformOptions)) { - process.nextTick(callback); - return; + return null; + } + const uri = await this._fetcher.fetch(GlobalTransformCache.keyOf(props)); + if (uri == null) { + return null; } - this._fetcher.fetch(GlobalTransformCache.keyOf(props), (error, uri) => { - if (error != null) { - callback(error); - } else { - if (uri == null) { - callback(); - return; - } - this._tryFetchingFromURI(uri, callback); - } - }); + return await this._tryFetchingFromURI(uri); } store(props: FetchProps, result: CachedResult) { diff --git a/packager/src/node-haste/Module.js b/packager/src/node-haste/Module.js index 6f05271016d714..0f1892ca56a231 100644 --- a/packager/src/node-haste/Module.js +++ b/packager/src/node-haste/Module.js @@ -307,17 +307,16 @@ class Module { this._transformCodeForCallback(cacheProps, callback); return; } - _globalCache.fetch(cacheProps, (globalCacheError, globalCachedResult) => { - if (globalCacheError) { - callback(globalCacheError); - return; - } - if (globalCachedResult == null) { - this._transformAndStoreCodeGlobally(cacheProps, _globalCache, callback); - return; - } - callback(undefined, globalCachedResult); - }); + _globalCache.fetch(cacheProps).then( + globalCachedResult => process.nextTick(() => { + if (globalCachedResult == null) { + this._transformAndStoreCodeGlobally(cacheProps, _globalCache, callback); + return; + } + callback(undefined, globalCachedResult); + }), + globalCacheError => process.nextTick(() => callback(globalCacheError)), + ); } _getAndCacheTransformedCode( From b8cc75c613b4e7e5919128707e97aa0d19bb957d Mon Sep 17 00:00:00 2001 From: Jakub Woyke Date: Thu, 16 Mar 2017 12:24:11 -0700 Subject: [PATCH 054/366] Don't call libc malloc and free directly from assembly Reviewed By: strager, javache Differential Revision: D4484300 fbshipit-source-id: 97b9c2e9525f38c9158cfb499ba93d1af7d84b69 --- React/Profiler/RCTProfile.m | 14 +++++++++++++ React/Profiler/RCTProfileTrampoline-arm.S | 21 ++------------------ React/Profiler/RCTProfileTrampoline-arm64.S | 4 ++-- React/Profiler/RCTProfileTrampoline-i386.S | 4 ++-- React/Profiler/RCTProfileTrampoline-x86_64.S | 4 ++-- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/React/Profiler/RCTProfile.m b/React/Profiler/RCTProfile.m index b51a9bcc2bbd65..d2b0f479d136a7 100644 --- a/React/Profiler/RCTProfile.m +++ b/React/Profiler/RCTProfile.m @@ -153,6 +153,20 @@ static dispatch_group_t RCTProfileGetUnhookGroup(void) return unhookGroup; } +// Used by RCTProfileTrampoline assembly file to call libc`malloc +RCT_EXTERN void *RCTProfileMalloc(size_t size); +void *RCTProfileMalloc(size_t size) +{ + return malloc(size); +} + +// Used by RCTProfileTrampoline assembly file to call libc`free +RCT_EXTERN void RCTProfileFree(void *buf); +void RCTProfileFree(void *buf) +{ + free(buf); +} + RCT_EXTERN IMP RCTProfileGetImplementation(id obj, SEL cmd); IMP RCTProfileGetImplementation(id obj, SEL cmd) { diff --git a/React/Profiler/RCTProfileTrampoline-arm.S b/React/Profiler/RCTProfileTrampoline-arm.S index 99ac6498b2748a..aa622bbd0d7c6b 100644 --- a/React/Profiler/RCTProfileTrampoline-arm.S +++ b/React/Profiler/RCTProfileTrampoline-arm.S @@ -35,12 +35,7 @@ SYMBOL_NAME(RCTProfileTrampoline): * profile */ mov r0, #0xc - movw ip, :lower16:(L_malloc-(LPC1_0+4)) - movt ip, :upper16:(L_malloc-(LPC1_0+4)) -LPC1_0: - add ip, pc - ldr ip, [ip] - blx ip + bl SYMBOL_NAME(RCTProfileMalloc) /** * r4 is the callee saved register we'll use to refer to the allocated memory, * store its initial value, so we can restore it later @@ -92,12 +87,7 @@ LPC1_0: ldr r1, [r4, #0x8] ldr r4, [r4] push {r1} // save the caller on the stack - movw ip, :lower16:(L_free-(LPC1_1+4)) - movt ip, :upper16:(L_free-(LPC1_1+4)) -LPC1_1: - add ip, pc - ldr ip, [ip] - blx ip + bl SYMBOL_NAME(RCTProfileFree) pop {lr} // pop the caller pop {r0} // pop the return value @@ -105,11 +95,4 @@ LPC1_1: trap - .data - .p2align 2 -L_malloc: - .long SYMBOL_NAME(malloc) -L_free: - .long SYMBOL_NAME(free) - #endif diff --git a/React/Profiler/RCTProfileTrampoline-arm64.S b/React/Profiler/RCTProfileTrampoline-arm64.S index 30ce4a04828662..e513696870551e 100644 --- a/React/Profiler/RCTProfileTrampoline-arm64.S +++ b/React/Profiler/RCTProfileTrampoline-arm64.S @@ -48,7 +48,7 @@ SYMBOL_NAME(RCTProfileTrampoline): * the implementation and the caller address. */ mov x0, #0x10 - bl SYMBOL_NAME(malloc) + bl SYMBOL_NAME(RCTProfileMalloc) // store the initial value of r19, the callee saved register we'll use str x19, [x0] mov x19, x0 @@ -111,7 +111,7 @@ SYMBOL_NAME(RCTProfileTrampoline): ldr x10, [x19, #0x8] // load the caller address ldr x19, [x19] // restore x19 str x10, [sp, #0x18] // store x10 on the stack space allocated above - bl SYMBOL_NAME(free) + bl SYMBOL_NAME(RCTProfileFree) // Load both return values and link register from the stack ldr q0, [sp, #0x0] diff --git a/React/Profiler/RCTProfileTrampoline-i386.S b/React/Profiler/RCTProfileTrampoline-i386.S index 7faba7742656d1..d1adf1a0898ecd 100644 --- a/React/Profiler/RCTProfileTrampoline-i386.S +++ b/React/Profiler/RCTProfileTrampoline-i386.S @@ -30,7 +30,7 @@ SYMBOL_NAME(RCTProfileTrampoline): */ subl $0x8, %esp // stack padding (16-byte alignment for function calls) pushl $0xc // allocate 12-bytes - calll SYMBOL_NAME(malloc) + calll SYMBOL_NAME(RCTProfileMalloc) addl $0xc, %esp // restore stack (8-byte padding + 4-byte argument) /** @@ -85,7 +85,7 @@ SYMBOL_NAME(RCTProfileTrampoline): * the stack has already been padded and the first and only argument, the * memory address, is already in the bottom of the stack. */ - calll SYMBOL_NAME(free) + calll SYMBOL_NAME(RCTProfileFree) addl $0x8, %esp /** diff --git a/React/Profiler/RCTProfileTrampoline-x86_64.S b/React/Profiler/RCTProfileTrampoline-x86_64.S index 0325fb0f5496fe..21072d84241906 100644 --- a/React/Profiler/RCTProfileTrampoline-x86_64.S +++ b/React/Profiler/RCTProfileTrampoline-x86_64.S @@ -90,7 +90,7 @@ SYMBOL_NAME(RCTProfileTrampoline): // allocate 16 bytes movq $0x10, %rdi - callq SYMBOL_NAME_PIC(malloc) + callq SYMBOL_NAME_PIC(RCTProfileMalloc) // store the initial value of calle saved registers %r13 and %r14 movq %r13, 0x0(%rax) @@ -169,7 +169,7 @@ SYMBOL_NAME(RCTProfileTrampoline): andq $-0x10, %rsp // Free the memory allocated to stash callee saved registers - callq SYMBOL_NAME_PIC(free) + callq SYMBOL_NAME_PIC(RCTProfileFree) // unalign stack and restore %r12 movq %r12, %rsp From 3bb95124845873d593903579e1e6e01e7e6525f2 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Thu, 16 Mar 2017 13:47:25 -0700 Subject: [PATCH 055/366] packager: FBGlobalTransformCache: move the retry/error logic Reviewed By: davidaurelio Differential Revision: D4713585 fbshipit-source-id: 6a83f858692b8a1f6326051f3a3f4a3a549e4027 --- packager/src/lib/GlobalTransformCache.js | 97 ++++++++++-------------- 1 file changed, 39 insertions(+), 58 deletions(-) diff --git a/packager/src/lib/GlobalTransformCache.js b/packager/src/lib/GlobalTransformCache.js index 002960df315be5..b74a1981cc2330 100644 --- a/packager/src/lib/GlobalTransformCache.js +++ b/packager/src/lib/GlobalTransformCache.js @@ -12,6 +12,7 @@ 'use strict'; const BatchProcessor = require('./BatchProcessor'); +const FetchError = require('node-fetch/lib/fetch-error'); const crypto = require('crypto'); const fetch = require('node-fetch'); @@ -21,9 +22,9 @@ const path = require('path'); import type {Options as TransformOptions} from '../JSTransformer/worker/worker'; import type {CachedResult, GetTransformCacheKey} from './TransformCache'; -import type {Reporter} from './reporting'; type FetchResultURIs = (keys: Array) => Promise>; +type FetchResultFromURI = (uri: string) => Promise; type StoreResults = (resultsByKey: Map) => Promise; type FetchProps = { @@ -43,7 +44,6 @@ class KeyURIFetcher { _batchProcessor: BatchProcessor; _fetchResultURIs: FetchResultURIs; - _processError: (error: Error) => mixed; /** * When a batch request fails for some reason, we process the error locally @@ -51,13 +51,7 @@ class KeyURIFetcher { * a build will not fail just because of the cache. */ async _processKeys(keys: Array): Promise> { - let URIsByKey; - try { - URIsByKey = await this._fetchResultURIs(keys); - } catch (error) { - this._processError(error); - return new Array(keys.length); - } + const URIsByKey = await this._fetchResultURIs(keys); return keys.map(key => URIsByKey.get(key)); } @@ -65,14 +59,13 @@ class KeyURIFetcher { return await this._batchProcessor.queue(key); } - constructor(fetchResultURIs: FetchResultURIs, processError: (error: Error) => mixed) { + constructor(fetchResultURIs: FetchResultURIs) { this._fetchResultURIs = fetchResultURIs; this._batchProcessor = new BatchProcessor({ maximumDelayMs: 10, maximumItems: 500, concurrency: 25, }, this._processKeys.bind(this)); - this._processError = processError; } } @@ -105,21 +98,6 @@ class KeyResultStore { } -function validateCachedResult(cachedResult: mixed): ?CachedResult { - if ( - cachedResult != null && - typeof cachedResult === 'object' && - typeof cachedResult.code === 'string' && - Array.isArray(cachedResult.dependencies) && - cachedResult.dependencies.every(dep => typeof dep === 'string') && - Array.isArray(cachedResult.dependencyOffsets) && - cachedResult.dependencyOffsets.every(offset => typeof offset === 'number') - ) { - return (cachedResult: any); - } - return undefined; -} - /** * The transform options contain absolute paths. This can contain, for * example, the username if someone works their home directory (very likely). @@ -167,12 +145,30 @@ class TransformProfileSet { } } +/** + * For some reason the result stored by the server for a key might mismatch what + * we expect a result to be. So we need to verify carefully the data. + */ +function validateCachedResult(cachedResult: mixed): ?CachedResult { + if ( + cachedResult != null && + typeof cachedResult === 'object' && + typeof cachedResult.code === 'string' && + Array.isArray(cachedResult.dependencies) && + cachedResult.dependencies.every(dep => typeof dep === 'string') && + Array.isArray(cachedResult.dependencyOffsets) && + cachedResult.dependencyOffsets.every(offset => typeof offset === 'number') + ) { + return (cachedResult: any); + } + return null; +} + class GlobalTransformCache { _fetcher: KeyURIFetcher; + _fetchResultFromURI: FetchResultFromURI; _profileSet: TransformProfileSet; - _reporter: Reporter; - _retries: number; _store: ?KeyResultStore; /** @@ -185,14 +181,13 @@ class GlobalTransformCache { */ constructor( fetchResultURIs: FetchResultURIs, + fetchResultFromURI: FetchResultFromURI, storeResults: ?StoreResults, profiles: Iterable, - reporter: Reporter, ) { - this._fetcher = new KeyURIFetcher(fetchResultURIs, this._processError.bind(this)); + this._fetcher = new KeyURIFetcher(fetchResultURIs); this._profileSet = new TransformProfileSet(profiles); - this._reporter = reporter; - this._retries = 4; + this._fetchResultFromURI = fetchResultFromURI; if (storeResults != null) { this._store = new KeyResultStore(storeResults); } @@ -211,26 +206,12 @@ class GlobalTransformCache { return `${digest}-${path.basename(props.filePath)}`; } - /** - * If too many errors already happened, we just drop the additional errors. - */ - _processError(error: Error) { - if (this._retries <= 0) { - return; - } - this._reporter.update({type: 'global_cache_error', error}); - --this._retries; - if (this._retries <= 0) { - this._reporter.update({type: 'global_cache_disabled', reason: 'too_many_errors'}); - } - } - /** * We may want to improve that logic to return a stream instead of the whole * blob of transformed results. However the results are generally only a few * megabytes each. */ - async _fetchFromURI(uri: string): Promise { + static async _fetchResultFromURI(uri: string): Promise { const response = await fetch(uri, {method: 'GET', timeout: 8000}); if (response.status !== 200) { throw new Error(`Unexpected HTTP status: ${response.status} ${response.statusText} `); @@ -244,16 +225,16 @@ class GlobalTransformCache { } /** - * Wrap `_fetchFromURI` with error logging, and return an empty result instead - * of errors. This is because the global cache is not critical to the normal - * packager operation. + * It happens from time to time that a fetch timeouts, we want to try these + * again a second time. */ - async _tryFetchingFromURI(uri: string): Promise { - try { - return await this._fetchFromURI(uri); - } catch (error) { - this._processError(error); - } + static fetchResultFromURI(uri: string): Promise { + return GlobalTransformCache._fetchResultFromURI(uri).catch(error => { + if (!(error instanceof FetchError && error.type === 'request-timeout')) { + throw error; + } + return this._fetchResultFromURI(uri); + }); } /** @@ -261,14 +242,14 @@ class GlobalTransformCache { * key yet, or an error happened, processed separately. */ async fetch(props: FetchProps): Promise { - if (this._retries <= 0 || !this._profileSet.has(props.transformOptions)) { + if (!this._profileSet.has(props.transformOptions)) { return null; } const uri = await this._fetcher.fetch(GlobalTransformCache.keyOf(props)); if (uri == null) { return null; } - return await this._tryFetchingFromURI(uri); + return await this._fetchResultFromURI(uri); } store(props: FetchProps, result: CachedResult) { From a1694ba86b03a06979c6c5623332341fc354aceb Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Thu, 16 Mar 2017 13:49:51 -0700 Subject: [PATCH 056/366] packager: DependencyGraph-test: add broken use case Summary: This changeset adds a test that verifies that the duplicate modules use case is broken. The goal is to acknowledge the problem for now, and when we update `jest-haste`, we'll simply fix what needs to be fixed in that test. See also, https://github.com/facebook/jest/pull/3107 Reviewed By: davidaurelio Differential Revision: D4673431 fbshipit-source-id: 05e09bdf61a2eddf2e9d1e32a33d32065c9051f1 --- .../__tests__/DependencyGraph-test.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packager/src/node-haste/__tests__/DependencyGraph-test.js b/packager/src/node-haste/__tests__/DependencyGraph-test.js index 4a60d0b55c450c..f55ee0a9137123 100644 --- a/packager/src/node-haste/__tests__/DependencyGraph-test.js +++ b/packager/src/node-haste/__tests__/DependencyGraph-test.js @@ -5181,6 +5181,69 @@ describe('DependencyGraph', function() { expect(deps).toBeDefined(); }); }); + + it('should recover from multiple modules with the same name (but this is broken right now)', async () => { + const root = '/root'; + console.warn = jest.fn(); + const filesystem = setMockFileSystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require(\'a\')', + 'require(\'b\')', + ].join('\n'), + 'a.js': [ + '/**', + ' * @providesModule a', + ' */', + ].join('\n'), + 'b.js': [ + '/**', + ' * @providesModule b', + ' */', + ].join('\n'), + }, + }); + + const dgraph = DependencyGraph.load({...defaults, roots: [root]}); + await getOrderedDependenciesAsJSON(dgraph, root + '/index.js'); + filesystem.root['b.js'] = [ + '/**', + ' * @providesModule a', + ' */', + ].join('\n'); + await triggerAndProcessWatchEvent(dgraph, 'change', root + '/b.js'); + try { + await getOrderedDependenciesAsJSON(dgraph, root + '/index.js'); + throw new Error('expected `getOrderedDependenciesAsJSON` to fail'); + } catch (error) { + if (error.type !== 'UnableToResolveError') { + throw error; + } + expect(console.warn).toBeCalled(); + filesystem.root['b.js'] = [ + '/**', + ' * @providesModule b', + ' */', + ].join('\n'); + await triggerAndProcessWatchEvent(dgraph, 'change', root + '/b.js'); + } + + // This verifies that it is broken right now. Instead of throwing it should + // return correct results. Once this is fixed in `jest-haste`, remove + // the whole try catch and verify results are matching a snapshot. + try { + await getOrderedDependenciesAsJSON(dgraph, root + '/index.js'); + throw new Error('expected `getOrderedDependenciesAsJSON` to fail'); + } catch (error) { + if (error.type !== 'UnableToResolveError') { + throw error; + } + } + }); + }); describe('Extensions', () => { From 9d3292069d105aa097c265166c0256c6b6bea159 Mon Sep 17 00:00:00 2001 From: mlanter Date: Thu, 16 Mar 2017 14:44:23 -0700 Subject: [PATCH 057/366] Add support for animating nested styles Summary: This adds the ability to nest animated styles and is a follow up to #11030 . **Test plan (required)** Verify a component with a shadowOffset animation animates. Example: ``` ``` ![example](https://cloud.githubusercontent.com/assets/19673711/23878825/e29f6ae4-0806-11e7-8650-9cff1f591204.gif) Closes https://github.com/facebook/react-native/pull/12909 Differential Revision: D4723933 fbshipit-source-id: 751d7ceb4f9bb22283fb14a5e597730ffd1d9ff6 --- .../Animated/src/AnimatedImplementation.js | 42 +++++++++++++------ .../Animated/src/__tests__/Animated-test.js | 14 ++++++- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index 1c06b9a6e4ab13..769b3b0164a64e 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -1508,32 +1508,48 @@ class AnimatedStyle extends AnimatedWithChildren { this._style = style; } - __getValue(): Object { - var style = {}; - for (var key in this._style) { - var value = this._style[key]; + // Recursively get values for nested styles (like iOS's shadowOffset) + __walkStyleAndGetValues(style) { + let updatedStyle = {}; + for (let key in style) { + let value = style[key]; if (value instanceof Animated) { if (!value.__isNative) { // We cannot use value of natively driven nodes this way as the value we have access from // JS may not be up to date. - style[key] = value.__getValue(); + updatedStyle[key] = value.__getValue(); } + } else if (value && !Array.isArray(value) && typeof value === 'object') { + // Support animating nested values (for example: shadowOffset.height) + updatedStyle[key] = this.__walkStyleAndGetValues(value); } else { - style[key] = value; + updatedStyle[key] = value; } } - return style; + return updatedStyle; } - __getAnimatedValue(): Object { - var style = {}; - for (var key in this._style) { - var value = this._style[key]; + __getValue(): Object { + return this.__walkStyleAndGetValues(this._style); + } + + // Recursively get animated values for nested styles (like iOS's shadowOffset) + __walkStyleAndGetAnimatedValues(style) { + let updatedStyle = {}; + for (let key in style) { + let value = style[key]; if (value instanceof Animated) { - style[key] = value.__getAnimatedValue(); + updatedStyle[key] = value.__getAnimatedValue(); + } else if (value && !Array.isArray(value) && typeof value === 'object') { + // Support animating nested values (for example: shadowOffset.height) + updatedStyle[key] = this.__walkStyleAndGetAnimatedValues(value); } } - return style; + return updatedStyle; + } + + __getAnimatedValue(): Object { + return this.__walkStyleAndGetAnimatedValues(this._style); } __attach(): void { diff --git a/Libraries/Animated/src/__tests__/Animated-test.js b/Libraries/Animated/src/__tests__/Animated-test.js index c89d2979995a21..c081f1e9b7a67a 100644 --- a/Libraries/Animated/src/__tests__/Animated-test.js +++ b/Libraries/Animated/src/__tests__/Animated-test.js @@ -33,7 +33,11 @@ describe('Animated tests', () => { outputRange: [100, 200], })}, {scale: anim}, - ] + ], + shadowOffset: { + width: anim, + height: anim, + }, } }, callback); @@ -47,6 +51,10 @@ describe('Animated tests', () => { {translateX: 100}, {scale: 0}, ], + shadowOffset: { + width: 0, + height: 0, + }, }, }); @@ -62,6 +70,10 @@ describe('Animated tests', () => { {translateX: 150}, {scale: 0.5}, ], + shadowOffset: { + width: 0.5, + height: 0.5, + }, }, }); From 3637bce4790fd6c05ad81fe330dad6dc51c1d531 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 16 Mar 2017 15:13:53 -0700 Subject: [PATCH 058/366] Warn when timers longer than 1 min are set pending AlarmManager support Summary: These are bad since the app might not be foregrounded anymore and it also keeps the timing module awake. We could fix the latter but the former would require someone adding AlarmManager support. Open to better/more helpful ideas for the warning message but there's not a ton we can tell them to do right now. Reviewed By: achen1 Differential Revision: D4716273 fbshipit-source-id: c5d3a3ce8af53253b240477f2bde38094a138a02 --- Libraries/Core/Timers/JSTimers.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Libraries/Core/Timers/JSTimers.js b/Libraries/Core/Timers/JSTimers.js index 842b47ba5455fc..967552c29ec833 100644 --- a/Libraries/Core/Timers/JSTimers.js +++ b/Libraries/Core/Timers/JSTimers.js @@ -15,6 +15,7 @@ // in dependencies. NativeModules > BatchedBridge > MessageQueue > JSTimersExecution const RCTTiming = require('NativeModules').Timing; const JSTimersExecution = require('JSTimersExecution'); +const Platform = require('Platform'); const parseErrorStack = require('parseErrorStack'); @@ -64,6 +65,14 @@ function _freeCallback(timerID: number) { } } +const MAX_TIMER_DURATION_MS = 60 * 1000; +const IS_ANDROID = Platform.OS === 'android'; +const ANDROID_LONG_TIMER_MESSAGE = + 'Setting a timer for a long period of time, i.e. multiple minutes, is a ' + + 'performance and correctness issue on Android as it keeps the timer ' + + 'module awake, and timers can only be called when the app is in the foreground. ' + + 'See https://github.com/facebook/react-native/issues/12981 for more info.'; + /** * JS implementation of timer functions. Must be completely driven by an * external clock signal, all that's stored here is timerID, timer type, and @@ -75,6 +84,11 @@ const JSTimers = { * @param {number} duration Number of milliseconds. */ setTimeout: function(func: Function, duration: number, ...args?: any): number { + if (IS_ANDROID && duration > MAX_TIMER_DURATION_MS) { + console.warn( + ANDROID_LONG_TIMER_MESSAGE + '\n' + '(Saw setTimeout with duration ' + + duration + 'ms)'); + } const id = _allocateCallback(() => func.apply(undefined, args), 'setTimeout'); RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ false); return id; @@ -85,6 +99,11 @@ const JSTimers = { * @param {number} duration Number of milliseconds. */ setInterval: function(func: Function, duration: number, ...args?: any): number { + if (IS_ANDROID && duration > MAX_TIMER_DURATION_MS) { + console.warn( + ANDROID_LONG_TIMER_MESSAGE + '\n' + '(Saw setInterval with duration ' + + duration + 'ms)'); + } const id = _allocateCallback(() => func.apply(undefined, args), 'setInterval'); RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ true); return id; From e1f493ebd2c6a8d44673648eeefe19c382789280 Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Thu, 16 Mar 2017 15:18:20 -0700 Subject: [PATCH 059/366] Add a flag to support a simplified iOS test - just compile, don't run packager and tests Reviewed By: gfosco Differential Revision: D4690887 fbshipit-source-id: e6299c9146e1ac97a0606f538e9d10539f147a67 --- .travis.yml | 2 +- docs/Testing.md | 2 +- scripts/objc-test-ios.sh | 17 +++++++++++++++++ scripts/objc-test-tvos.sh | 5 ++--- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5f0caaabc938ee..0cbf424aacd8f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ install: - $yarn install script: - - if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait ./scripts/objc-test-ios.sh; fi + - if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait ./scripts/objc-test-ios.sh test; fi - if [[ "$TEST_TYPE" = objc-tvos ]]; then travis_retry travis_wait ./scripts/objc-test-tvos.sh; fi - if [[ "$TEST_TYPE" = e2e-objc ]]; then node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3; fi - if [[ ( "$TEST_TYPE" = podspecs ) && ( "$TRAVIS_PULL_REQUEST" = "false" ) ]]; then gem install cocoapods && ./scripts/process-podspecs.sh; fi diff --git a/docs/Testing.md b/docs/Testing.md index ace4db3efc4958..eb7121788fdc80 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -97,7 +97,7 @@ See the following for example usage and integration points: You can run integration tests locally with cmd+U in the IntegrationTest and UIExplorer apps in Xcode, or by running the following in the command line on macOS: $ cd react-native - $ ./scripts/objc-test-ios.sh + $ ./scripts/objc-test-ios.sh test > Your Xcode install will come with a variety of Simulators running the latest OS. You may need to manually create a new Simulator to match what the `XCODE_DESTINATION` param in the test script. diff --git a/scripts/objc-test-ios.sh b/scripts/objc-test-ios.sh index a85caa073af04f..3f909f7a23a0c4 100755 --- a/scripts/objc-test-ios.sh +++ b/scripts/objc-test-ios.sh @@ -26,6 +26,12 @@ function cleanup { } trap cleanup INT TERM EXIT +# If first argument is "test", actually start the packager and run tests. +# Otherwise, just build UIExplorer for tvOS and exit + +if [ "$1" = "test" ]; +then + # Start the packager (exec "./packager/launchPackager.command" || echo "Can't start packager automatically") & (exec "./IntegrationTests/launchWebSocketServer.command" || echo "Can't start web socket server automatically") & @@ -60,6 +66,7 @@ rm temp.bundle curl 'http://localhost:8081/IntegrationTests/RCTRootViewIntegrationTestApp.bundle?platform=ios&dev=true' -o temp.bundle rm temp.bundle +# Build and test for iOS # TODO: We use xcodebuild because xctool would stall when collecting info about # the tests before running them. Switch back when this issue with xctool has # been resolved. @@ -70,3 +77,13 @@ xcodebuild \ -destination "platform=iOS Simulator,name=iPhone 5s,OS=10.1" \ build test +else + +# Only build for iOS (check there are no missing files in the Xcode project) +xcodebuild \ + -project "Examples/UIExplorer/UIExplorer.xcodeproj" \ + -scheme "UIExplorer" \ + -sdk "iphonesimulator" \ + build + +fi diff --git a/scripts/objc-test-tvos.sh b/scripts/objc-test-tvos.sh index 2266a5ba6fb40f..943d510914eece 100755 --- a/scripts/objc-test-tvos.sh +++ b/scripts/objc-test-tvos.sh @@ -23,7 +23,7 @@ function cleanup { } trap cleanup EXIT -# If first argument is "test", actually start the packager and run tests as in the iOS script +# If first argument is "test", actually start the packager and run tests. # Otherwise, just build UIExplorer for tvOS and exit if [ "$1" = "test" ]; @@ -57,12 +57,11 @@ xcodebuild \ else -# Build only (no test) for tvOS, to make sure there are no missing files +# Only build for tvOS (check there are no missing files in the Xcode project) xcodebuild \ -project "Examples/UIExplorer/UIExplorer.xcodeproj" \ -scheme "UIExplorer-tvOS" \ -sdk "appletvsimulator" \ - -destination "platform=tvOS Simulator,name=Apple TV 1080p,OS=10.1" \ build fi From 7e4c93d65ade762b401f64fa4017680a24a3415b Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Thu, 16 Mar 2017 15:49:54 -0700 Subject: [PATCH 060/366] Fix doubled ItemSeparators Summary: Forgot to update these after rename... Reviewed By: yungsters Differential Revision: D4723788 fbshipit-source-id: 8cf7e8de57a23d9ee0a424aa9c0d62ab1cfbbb12 --- Libraries/CustomComponents/Lists/VirtualizedSectionList.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Libraries/CustomComponents/Lists/VirtualizedSectionList.js b/Libraries/CustomComponents/Lists/VirtualizedSectionList.js index 317891c4d3ebc2..cd4312e96f4a11 100644 --- a/Libraries/CustomComponents/Lists/VirtualizedSectionList.js +++ b/Libraries/CustomComponents/Lists/VirtualizedSectionList.js @@ -267,10 +267,8 @@ class VirtualizedSectionList return { childProps: { ...props, - FooterComponent: this.props.ListFooterComponent, - HeaderComponent: this.props.ListHeaderComponent, renderItem: this._renderItem, - SeparatorComponent: undefined, // Rendered with renderItem + ItemSeparatorComponent: undefined, // Rendered with renderItem data: props.sections, getItemCount: () => itemCount, getItem, From 55f48eb9f6916a866df73d15f6c225c3b35d9273 Mon Sep 17 00:00:00 2001 From: Mehdi Mulani Date: Thu, 16 Mar 2017 15:59:10 -0700 Subject: [PATCH 061/366] Move RCTLinkingManager to the main thread Reviewed By: shergin Differential Revision: D4709352 fbshipit-source-id: 3fc86504b12fe64dd94dd7330073d1a4a4f272e2 --- Libraries/LinkingIOS/RCTLinkingManager.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m index dd1ef0381072dc..d5eb10be1fc441 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.m +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -19,6 +19,11 @@ @implementation RCTLinkingManager RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self From 63fa621df100df05fbdaf04e93304a7d1dc35f92 Mon Sep 17 00:00:00 2001 From: "Andrew Y. Chen" Date: Thu, 16 Mar 2017 16:26:02 -0700 Subject: [PATCH 062/366] Fix FrescoModule not initialized in Nodes Reviewed By: AaaChiuuu Differential Revision: D4687127 fbshipit-source-id: d387ff665374bd4ef40fc2e9c19543f2bacf4a66 --- ReactAndroid/src/main/java/com/facebook/react/flat/BUCK | 1 + .../main/java/com/facebook/react/flat/FlatUIImplementation.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK b/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK index 0354f255e38d04..ed641a76eabe53 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK @@ -19,6 +19,7 @@ android_library( react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/modules/fresco:fresco"), react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"), react_native_target("java/com/facebook/react/touch:touch"), react_native_target("java/com/facebook/react/uimanager:uimanager"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java index 755b43e645b9bb..add945cbb1f34b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java @@ -18,6 +18,7 @@ import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.modules.fresco.FrescoModule; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.ReactShadowNode; import com.facebook.react.uimanager.ReactStylesDiffMap; @@ -98,6 +99,7 @@ protected ReactShadowNode createRootShadowNode() { // initialization is undefined, and this is pretty much the earliest when we are guarantied // that Fresco is initalized and DraweeControllerBuilder can be queried. This also happens // relatively rarely to have any performance considerations. + mReactContext.getNativeModule(FrescoModule.class); // initialize Fresco DraweeRequestHelper.setDraweeControllerBuilder( mRCTImageViewManager.getDraweeControllerBuilder()); mRCTImageViewManager = null; From b1a63f00886ff4fbd557b391b83d38e81284f28f Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Thu, 16 Mar 2017 16:36:57 -0700 Subject: [PATCH 063/366] Fix minimumViewTime Reviewed By: blairvanderhoof Differential Revision: D4723575 fbshipit-source-id: a120eef4079a808bd3dead08df93989e1db3d96a --- Libraries/CustomComponents/Lists/ViewabilityHelper.js | 8 ++++---- .../Lists/__tests__/ViewabilityHelper-test.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Libraries/CustomComponents/Lists/ViewabilityHelper.js b/Libraries/CustomComponents/Lists/ViewabilityHelper.js index f3cc0e584b34d4..31ce384ba972aa 100644 --- a/Libraries/CustomComponents/Lists/ViewabilityHelper.js +++ b/Libraries/CustomComponents/Lists/ViewabilityHelper.js @@ -49,7 +49,7 @@ export type ViewabilityConfig = {| * layout. * * An item is said to be in a "viewable" state when any of the following -* is true for longer than `minViewTime` milliseconds (after an interaction if `waitForInteraction` +* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction` * is true): * * - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item @@ -145,7 +145,7 @@ class ViewabilityHelper { renderRange?: {first: number, last: number}, // Optional optimization to reduce the scan size ): void { const updateTime = Date.now(); - if (this._lastUpdateTime === 0 && getFrameMetrics(0)) { + if (this._lastUpdateTime === 0 && itemCount > 0 && getFrameMetrics(0)) { // Only count updates after the first item is rendered and has a frame. this._lastUpdateTime = updateTime; } @@ -171,13 +171,13 @@ class ViewabilityHelper { } this._viewableIndices = viewableIndices; this._lastUpdateTime = updateTime; - if (this._config.minViewTime && updateElapsed < this._config.minViewTime) { + if (this._config.minimumViewTime && updateElapsed < this._config.minimumViewTime) { const handle = setTimeout( () => { this._timers.delete(handle); this._onUpdateSync(viewableIndices, onViewableItemsChanged, createViewToken); }, - this._config.minViewTime, + this._config.minimumViewTime, ); this._timers.add(handle); } else { diff --git a/Libraries/CustomComponents/Lists/__tests__/ViewabilityHelper-test.js b/Libraries/CustomComponents/Lists/__tests__/ViewabilityHelper-test.js index 199132ae8156bd..1d97105a7ad411 100644 --- a/Libraries/CustomComponents/Lists/__tests__/ViewabilityHelper-test.js +++ b/Libraries/CustomComponents/Lists/__tests__/ViewabilityHelper-test.js @@ -249,9 +249,9 @@ describe('onUpdate', function() { ); it( - 'minViewTime delays callback', + 'minimumViewTime delays callback', function() { - const helper = new ViewabilityHelper({minViewTime: 350, viewAreaCoveragePercentThreshold: 0}); + const helper = new ViewabilityHelper({minimumViewTime: 350, viewAreaCoveragePercentThreshold: 0}); rowFrames = { a: {y: 0, height: 200}, b: {y: 200, height: 200}, @@ -279,9 +279,9 @@ describe('onUpdate', function() { ); it( - 'minViewTime skips briefly visible items', + 'minimumViewTime skips briefly visible items', function() { - const helper = new ViewabilityHelper({minViewTime: 350, viewAreaCoveragePercentThreshold: 0}); + const helper = new ViewabilityHelper({minimumViewTime: 350, viewAreaCoveragePercentThreshold: 0}); rowFrames = { a: {y: 0, height: 250}, b: {y: 250, height: 200}, From 17cb70efdd1ed6378677e720a022c9d83ad87dd6 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 16 Mar 2017 18:09:21 -0700 Subject: [PATCH 064/366] Apply numeric text event bubbling fix for Android Reviewed By: bvaughn Differential Revision: D4726029 fbshipit-source-id: 94c8025bb59cc0970b6cc8ce7f3caf6e867bf72b --- .../shared/shared/event/EventPluginHub.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js b/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js index bba563c8c6d39d..bdd958571531f3 100644 --- a/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js +++ b/Libraries/Renderer/src/renderers/shared/shared/event/EventPluginHub.js @@ -138,7 +138,10 @@ var EventPluginHub = { return null; } } else { - if (typeof inst._currentElement === 'string') { + const currentElement = inst._currentElement; + if ( + typeof currentElement === 'string' || typeof currentElement === 'number' + ) { // Text node, let it bubble through. return null; } @@ -146,12 +149,11 @@ var EventPluginHub = { // If the instance is already unmounted, we have no listeners. return null; } - const props = inst._currentElement.props; - if (!props) { - return null; - } + const props = currentElement.props; listener = props[registrationName]; - if (shouldPreventMouseEvent(registrationName, inst._currentElement.type, props)) { + if ( + shouldPreventMouseEvent(registrationName, currentElement.type, props) + ) { return null; } } From 6620b1ff7db9e9cf906a2bcee3184a35c350f95b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 16 Mar 2017 19:23:24 -0700 Subject: [PATCH 065/366] Added ReactNative feature-flag switch between Stack and Fiber builds Reviewed By: sebmarkbage Differential Revision: D4606164 fbshipit-source-id: 5f25dbc52298d359e11e52341ff3570c797a6ea9 --- Libraries/Renderer/src/renderers/native/ReactNative.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Libraries/Renderer/src/renderers/native/ReactNative.js b/Libraries/Renderer/src/renderers/native/ReactNative.js index 27c56d54ba9d56..527a53920b741a 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNative.js +++ b/Libraries/Renderer/src/renderers/native/ReactNative.js @@ -11,5 +11,8 @@ */ 'use strict'; -// TODO (bvaughn) Enable Fiber experiement via ReactNativeFeatureFlags -module.exports = require('ReactNativeStack'); +const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); + +module.exports = ReactNativeFeatureFlags.useFiber + ? require('ReactNativeFiber') + : require('ReactNativeStack') From fbf6d1aaeb69111f9188c0800dabdf1ee4292506 Mon Sep 17 00:00:00 2001 From: Adam Perry Date: Thu, 16 Mar 2017 19:56:32 -0700 Subject: [PATCH 066/366] Update CRNA blog post with youtube embed. Summary: Adds the lightning talk's YouTube video ID to the existing CRNA blog post. cc hramos Closes https://github.com/facebook/react-native/pull/12987 Differential Revision: D4727390 Pulled By: ericvicenti fbshipit-source-id: 86412679f1c476b416d4bfe41e21c255100188df --- blog/2017-03-13-introducing-create-react-native-app.md | 1 + 1 file changed, 1 insertion(+) diff --git a/blog/2017-03-13-introducing-create-react-native-app.md b/blog/2017-03-13-introducing-create-react-native-app.md index 4ff6714e8d05a6..6aa49895ee3c13 100644 --- a/blog/2017-03-13-introducing-create-react-native-app.md +++ b/blog/2017-03-13-introducing-create-react-native-app.md @@ -6,6 +6,7 @@ authorURL: https://github.com/dikaiosune authorImage: https://avatars2.githubusercontent.com/u/6812281 authorTwitter: dika10sune category: engineering +youtubeVideoId: 9baaVjGdBqs --- Today we’re announcing [Create React Native App](https://github.com/react-community/create-react-native-app): a new tool that makes it significantly easier to get started with a React Native project! It’s heavily inspired by the design of [Create React App](https://github.com/facebookincubator/create-react-app) and is the product of a collaboration between [Facebook](https://code.facebook.com) and [Expo](https://expo.io) (formerly Exponent). From d7b37c405013926c2bb4fc98f1575bebf690aaff Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 17 Mar 2017 06:55:37 -0700 Subject: [PATCH 067/366] Remove JsToNativeBridge's nativeQueue Reviewed By: mhorowitz Differential Revision: D4589737 fbshipit-source-id: 3b2730417d99c4f98cfaad386bc50328f2551592 --- React/CxxBridge/RCTCxxBridge.mm | 81 +++++-------------- .../jni/xreact/jni/CatalystInstanceImpl.cpp | 42 ++++++---- .../jni/xreact/jni/CatalystInstanceImpl.h | 2 + .../main/jni/xreact/jni/JavaModuleWrapper.cpp | 42 ++++++---- .../main/jni/xreact/jni/JavaModuleWrapper.h | 13 ++- .../jni/xreact/jni/ModuleRegistryBuilder.cpp | 10 ++- .../jni/xreact/jni/ModuleRegistryBuilder.h | 5 +- ReactCommon/cxxreact/CxxNativeModule.cpp | 52 +++++------- ReactCommon/cxxreact/CxxNativeModule.h | 11 ++- ReactCommon/cxxreact/Instance.cpp | 14 +--- ReactCommon/cxxreact/Instance.h | 1 - ReactCommon/cxxreact/NativeToJsBridge.cpp | 50 +++++------- ReactCommon/cxxreact/NativeToJsBridge.h | 1 - 13 files changed, 143 insertions(+), 181 deletions(-) diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index 16b8782ec07338..3e776b61232bb4 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -101,70 +101,25 @@ static bool isRAMBundle(NSData *script) { // is not the JS thread. C++ modules don't use RCTNativeModule, so this little // adapter does the work. -class QueueNativeModule : public NativeModule { +class DispatchMessageQueueThread : public MessageQueueThread { public: - QueueNativeModule(RCTCxxBridge *bridge, - std::unique_ptr module) - : bridge_(bridge) - , module_(std::move(module)) - // Create new queue (store queueName, as it isn't retained by dispatch_queue) - , queueName_("com.facebook.react." + module_->getName() + "Queue") - , queue_(dispatch_queue_create(queueName_.c_str(), DISPATCH_QUEUE_SERIAL)) {} - - std::string getName() override { - return module_->getName(); - } - - std::vector getMethods() override { - return module_->getMethods(); - } - - folly::dynamic getConstants() override { - return module_->getConstants(); - } - - bool supportsWebWorkers() override { - return module_->supportsWebWorkers(); - } + DispatchMessageQueueThread(RCTModuleData *moduleData) + : moduleData_(moduleData) {} - void invoke(ExecutorToken token, unsigned int reactMethodId, - folly::dynamic &¶ms) override { - __weak RCTCxxBridge *bridge = bridge_; - auto module = module_; - auto cparams = std::make_shared(std::move(params)); - dispatch_async(queue_, ^{ - if (!bridge || !bridge.valid) { - return; - } - - module->invoke(token, reactMethodId, std::move(*cparams)); + void runOnQueue(std::function&& func) override { + dispatch_async(moduleData_.methodQueue, [func=std::move(func)] { + func(); }); } - - MethodCallResult callSerializableNativeHook( - ExecutorToken token, unsigned int reactMethodId, - folly::dynamic &&args) override { - return module_->callSerializableNativeHook(token, reactMethodId, std::move(args)); + void runOnQueueSync(std::function&& func) override { + LOG(FATAL) << "Unsupported operation"; + } + void quitSynchronous() override { + LOG(FATAL) << "Unsupported operation"; } private: - RCTCxxBridge *bridge_; - std::shared_ptr module_; - std::string queueName_; - dispatch_queue_t queue_; -}; - - -// This is a temporary hack. The cxx bridge assumes a single native -// queue handles all native method calls, but that's false on ios. So -// this is a no-thread passthrough, and queues are handled in -// RCTNativeModule. A similar refactoring should be done on the Java -// bridge. -class InlineMessageQueueThread : public MessageQueueThread { -public: - void runOnQueue(std::function&& func) override { func(); } - void runOnQueueSync(std::function&& func) override { func(); } - void quitSynchronous() override {} + RCTModuleData *moduleData_; }; } @@ -551,12 +506,13 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass if (![moduleData hasInstance]) { continue; } - modules.emplace_back( - new QueueNativeModule(self, std::make_unique( - _reactInstance, [moduleData.name UTF8String], - [moduleData] { return [(RCTCxxModule *)(moduleData.instance) move]; }))); + modules.emplace_back(std::make_unique( + _reactInstance, + [moduleData.name UTF8String], + [moduleData] { return [(RCTCxxModule *)(moduleData.instance) move]; }, + std::make_shared(moduleData))); } else { - modules.emplace_back(new RCTNativeModule(self, moduleData)); + modules.emplace_back(std::make_unique(self, moduleData)); } } @@ -586,7 +542,6 @@ - (void)_initializeBridge:(std::shared_ptr)executorFactory std::unique_ptr(new RCTInstanceCallback(self)), executorFactory, _jsMessageThread, - std::unique_ptr(new InlineMessageQueueThread), std::move([self _createModuleRegistry])); #if RCT_PROFILE diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index a38ed7713f2e2e..23b1e4d2d21d56 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -41,13 +41,17 @@ class Exception : public jni::JavaClass { class JInstanceCallback : public InstanceCallback { public: - explicit JInstanceCallback(alias_ref jobj) - : jobj_(make_global(jobj)) {} + explicit JInstanceCallback( + alias_ref jobj, + std::shared_ptr messageQueueThread) + : jobj_(make_global(jobj)), messageQueueThread_(std::move(messageQueueThread)) {} void onBatchComplete() override { - static auto method = - ReactCallback::javaClassStatic()->getMethod("onBatchComplete"); - method(jobj_); + messageQueueThread_->runOnQueue([this] { + static auto method = + ReactCallback::javaClassStatic()->getMethod("onBatchComplete"); + method(jobj_); + }); } void incrementPendingJSCalls() override { @@ -61,6 +65,7 @@ class JInstanceCallback : public InstanceCallback { } void decrementPendingJSCalls() override { + jni::ThreadScope guard; static auto method = ReactCallback::javaClassStatic()->getMethod("decrementPendingJSCalls"); method(jobj_); @@ -81,12 +86,11 @@ class JInstanceCallback : public InstanceCallback { return jobj->cthis()->getExecutorToken(jobj); } - void onExecutorStopped(ExecutorToken) override { - // TODO(cjhopman): implement this. - } + void onExecutorStopped(ExecutorToken) override {} private: global_ref jobj_; + std::shared_ptr messageQueueThread_; }; } @@ -97,7 +101,12 @@ jni::local_ref CatalystInstanceImpl::initHybr } CatalystInstanceImpl::CatalystInstanceImpl() - : instance_(folly::make_unique()) {} + : instance_(folly::make_unique()) {} + +CatalystInstanceImpl::~CatalystInstanceImpl() { + // TODO: 16669252: this prevents onCatalystInstanceDestroy from being called + moduleMessageQueue_->quitSynchronous(); +} void CatalystInstanceImpl::registerNatives() { registerHybrid({ @@ -127,11 +136,12 @@ void CatalystInstanceImpl::initializeBridge( // This executor is actually a factory holder. JavaScriptExecutorHolder* jseh, jni::alias_ref jsQueue, - jni::alias_ref moduleQueue, + jni::alias_ref nativeModulesQueue, jni::alias_ref::javaobject> javaModules, jni::alias_ref::javaobject> cxxModules) { // TODO mhorowitz: how to assert here? // Assertions.assertCondition(mBridge == null, "initializeBridge should be called once"); + moduleMessageQueue_ = std::make_shared(nativeModulesQueue); // This used to be: // @@ -149,12 +159,12 @@ void CatalystInstanceImpl::initializeBridge( // don't need jsModuleDescriptions any more, all the way up and down the // stack. - instance_->initializeBridge(folly::make_unique(callback), - jseh->getExecutorFactory(), - folly::make_unique(jsQueue), - folly::make_unique(moduleQueue), - buildModuleRegistry(std::weak_ptr(instance_), - javaModules, cxxModules)); + instance_->initializeBridge( + folly::make_unique(callback, moduleMessageQueue_), + jseh->getExecutorFactory(), + folly::make_unique(jsQueue), + buildModuleRegistry( + std::weak_ptr(instance_), javaModules, cxxModules, moduleMessageQueue_)); } void CatalystInstanceImpl::jniSetSourceURL(const std::string& sourceURL) { diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index 804fc927cbc1aa..0a811c1e65d52b 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -28,6 +28,7 @@ class CatalystInstanceImpl : public jni::HybridClass { static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/CatalystInstanceImpl;"; static jni::local_ref initHybrid(jni::alias_ref); + ~CatalystInstanceImpl() override; static void registerNatives(); @@ -74,6 +75,7 @@ class CatalystInstanceImpl : public jni::HybridClass { // This should be the only long-lived strong reference, but every C++ class // will have a weak reference. std::shared_ptr instance_; + std::shared_ptr moduleMessageQueue_; }; }} diff --git a/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.cpp b/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.cpp index e3444346bcec6e..c545f1233a1576 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.cpp @@ -87,30 +87,36 @@ bool JavaNativeModule::supportsWebWorkers() { } void JavaNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) { - static auto invokeMethod = - wrapper_->getClass()->getMethod("invoke"); - invokeMethod(wrapper_, JExecutorToken::extractJavaPartFromToken(token).get(), static_cast(reactMethodId), - ReadableNativeArray::newObjectCxxArgs(std::move(params)).get()); + messageQueueThread_->runOnQueue([this, token, reactMethodId, params=std::move(params)] { + static auto invokeMethod = wrapper_->getClass()-> + getMethod("invoke"); + invokeMethod(wrapper_, + JExecutorToken::extractJavaPartFromToken(token).get(), + static_cast(reactMethodId), + ReadableNativeArray::newObjectCxxArgs(std::move(params)).get()); + }); } MethodCallResult JavaNativeModule::callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) { - // TODO: evaluate whether calling through invoke is potentially faster - if (reactMethodId >= syncMethods_.size()) { - throw std::invalid_argument( - folly::to("methodId ", reactMethodId, " out of range [0..", syncMethods_.size(), "]")); - } + // TODO: evaluate whether calling through invoke is potentially faster + if (reactMethodId >= syncMethods_.size()) { + throw std::invalid_argument( + folly::to("methodId ", reactMethodId, " out of range [0..", syncMethods_.size(), "]")); + } - auto& method = syncMethods_[reactMethodId]; - CHECK(method.hasValue() && method->isSyncHook()) << "Trying to invoke a asynchronous method as synchronous hook"; - return method->invoke(instance_, wrapper_->getModule(), token, params); + auto& method = syncMethods_[reactMethodId]; + CHECK(method.hasValue() && method->isSyncHook()) << "Trying to invoke a asynchronous method as synchronous hook"; + return method->invoke(instance_, wrapper_->getModule(), token, params); } NewJavaNativeModule::NewJavaNativeModule( std::weak_ptr instance, - jni::alias_ref wrapper) - : instance_(std::move(instance)), - wrapper_(make_global(wrapper)), - module_(make_global(wrapper->getModule())) { + jni::alias_ref wrapper, + std::shared_ptr messageQueueThread) +: instance_(std::move(instance)) +, wrapper_(make_global(wrapper)) +, module_(make_global(wrapper->getModule())) +, messageQueueThread_(std::move(messageQueueThread)) { auto descs = wrapper_->getMethodDescriptors(); std::string moduleName = getName(); methods_.reserve(descs->size()); @@ -161,7 +167,9 @@ void NewJavaNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId folly::to("methodId ", reactMethodId, " out of range [0..", methods_.size(), "]")); } CHECK(!methods_[reactMethodId].isSyncHook()) << "Trying to invoke a synchronous hook asynchronously"; - invokeInner(token, reactMethodId, std::move(params)); + messageQueueThread_->runOnQueue([this, token, reactMethodId, params=std::move(params)] () mutable { + invokeInner(token, reactMethodId, std::move(params)); + }); } MethodCallResult NewJavaNativeModule::callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) { diff --git a/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h b/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h index 544b6eb167c169..222765de63d26b 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h +++ b/ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h @@ -12,6 +12,7 @@ namespace facebook { namespace react { class Instance; +class MessageQueueThread; struct JMethodDescriptor : public jni::JavaClass { static constexpr auto kJavaDescriptor = @@ -44,8 +45,11 @@ class JavaNativeModule : public NativeModule { public: JavaNativeModule( std::weak_ptr instance, - jni::alias_ref wrapper) - : instance_(std::move(instance)), wrapper_(make_global(wrapper)) {} + jni::alias_ref wrapper, + std::shared_ptr messageQueueThread) + : instance_(std::move(instance)) + , wrapper_(make_global(wrapper)) + , messageQueueThread_(std::move(messageQueueThread)) {} std::string getName() override; folly::dynamic getConstants() override; @@ -57,6 +61,7 @@ class JavaNativeModule : public NativeModule { private: std::weak_ptr instance_; jni::global_ref wrapper_; + std::shared_ptr messageQueueThread_; std::vector> syncMethods_; }; @@ -65,7 +70,8 @@ class NewJavaNativeModule : public NativeModule { public: NewJavaNativeModule( std::weak_ptr instance, - jni::alias_ref wrapper); + jni::alias_ref wrapper, + std::shared_ptr messageQueueThread); std::string getName() override; std::vector getMethods() override; @@ -78,6 +84,7 @@ class NewJavaNativeModule : public NativeModule { std::weak_ptr instance_; jni::global_ref wrapper_; jni::global_ref module_; + std::shared_ptr messageQueueThread_; std::vector methods_; std::vector methodDescriptors_; diff --git a/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp index 108091a8dfd934..0dd0c4836260ba 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.cpp @@ -32,14 +32,16 @@ xplat::module::CxxModule::Provider ModuleHolder::getProvider() const { std::unique_ptr buildModuleRegistry( std::weak_ptr winstance, jni::alias_ref::javaobject> javaModules, - jni::alias_ref::javaobject> cxxModules) { + jni::alias_ref::javaobject> cxxModules, + std::shared_ptr moduleMessageQueue) { std::vector> modules; for (const auto& jm : *javaModules) { - modules.emplace_back(folly::make_unique(winstance, jm)); + modules.emplace_back(folly::make_unique( + winstance, jm, moduleMessageQueue)); } for (const auto& cm : *cxxModules) { - modules.emplace_back( - folly::make_unique(winstance, cm->getName(), cm->getProvider())); + modules.emplace_back(folly::make_unique( + winstance, cm->getName(), cm->getProvider(), moduleMessageQueue)); } if (modules.empty()) { return nullptr; diff --git a/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h index 554473601173f8..0aeb7d5398e3df 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h +++ b/ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryBuilder.h @@ -12,6 +12,8 @@ namespace facebook { namespace react { +class MessageQueueThread; + class ModuleHolder : public jni::JavaClass { public: static auto constexpr kJavaDescriptor = @@ -24,7 +26,8 @@ class ModuleHolder : public jni::JavaClass { std::unique_ptr buildModuleRegistry( std::weak_ptr winstance, jni::alias_ref::javaobject> javaModules, - jni::alias_ref::javaobject> cxxModules); + jni::alias_ref::javaobject> cxxModules, + std::shared_ptr moduleMessageQueue); } } diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index c0cacd0b85b927..b487588bb76cbd 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -46,13 +46,6 @@ CxxModule::Callback convertCallback( } -CxxNativeModule::CxxNativeModule(std::weak_ptr instance, - std::string name, - CxxModule::Provider provider) - : instance_(instance) - , name_(std::move(name)) - , provider_(provider) {} - std::string CxxNativeModule::getName() { return name_; } @@ -85,8 +78,8 @@ bool CxxNativeModule::supportsWebWorkers() { void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) { if (reactMethodId >= methods_.size()) { - throw std::invalid_argument( - folly::to("methodId ", reactMethodId, " out of range [0..", methods_.size(), "]")); + throw std::invalid_argument(folly::to("methodId ", reactMethodId, + " out of range [0..", methods_.size(), "]")); } if (!params.isArray()) { throw std::invalid_argument( @@ -99,25 +92,20 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo const auto& method = methods_[reactMethodId]; if (!method.func) { - throw std::runtime_error( - folly::to("Method ", method.name, - " is synchronous but invoked asynchronously")); + throw std::runtime_error(folly::to("Method ", method.name, + " is synchronous but invoked asynchronously")); } if (params.size() < method.callbacks) { - throw std::invalid_argument( - folly::to("Expected ", method.callbacks, " callbacks, but only ", - params.size(), " parameters provided")); + throw std::invalid_argument(folly::to("Expected ", method.callbacks, + " callbacks, but only ", params.size(), " parameters provided")); } if (method.callbacks == 1) { - first = convertCallback( - makeCallback(instance_, token, params[params.size() - 1])); + first = convertCallback(makeCallback(instance_, token, params[params.size() - 1])); } else if (method.callbacks == 2) { - first = convertCallback( - makeCallback(instance_, token, params[params.size() - 2])); - second = convertCallback( - makeCallback(instance_, token, params[params.size() - 1])); + first = convertCallback(makeCallback(instance_, token, params[params.size() - 2])); + second = convertCallback(makeCallback(instance_, token, params[params.size() - 1])); } params.resize(params.size() - method.callbacks); @@ -141,16 +129,18 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo // stack. I'm told that will be possible in the future. TODO // mhorowitz #7128529: convert C++ exceptions to Java - try { - method.func(std::move(params), first, second); - } catch (const facebook::xplat::JsArgumentException& ex) { - // This ends up passed to the onNativeException callback. - throw; - } catch (...) { - // This means some C++ code is buggy. As above, we fail hard so the C++ - // developer can debug and fix it. - std::terminate(); - } + messageQueueThread_->runOnQueue([method, params=std::move(params), first, second] () { + try { + method.func(std::move(params), first, second); + } catch (const facebook::xplat::JsArgumentException& ex) { + // This ends up passed to the onNativeException callback. + throw; + } catch (...) { + // This means some C++ code is buggy. As above, we fail hard so the C++ + // developer can debug and fix it. + std::terminate(); + } + }); } MethodCallResult CxxNativeModule::callSerializableNativeHook( diff --git a/ReactCommon/cxxreact/CxxNativeModule.h b/ReactCommon/cxxreact/CxxNativeModule.h index ab6f3df87ffd4c..376ad7a19875a4 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.h +++ b/ReactCommon/cxxreact/CxxNativeModule.h @@ -15,8 +15,14 @@ std::function makeCallback( class CxxNativeModule : public NativeModule { public: - CxxNativeModule(std::weak_ptr instance, std::string name, - xplat::module::CxxModule::Provider provider); + CxxNativeModule(std::weak_ptr instance, + std::string name, + xplat::module::CxxModule::Provider provider, + std::shared_ptr messageQueueThread) + : instance_(instance) + , name_(std::move(name)) + , provider_(provider) + , messageQueueThread_(messageQueueThread) {} std::string getName() override; std::vector getMethods() override; @@ -32,6 +38,7 @@ class CxxNativeModule : public NativeModule { std::weak_ptr instance_; std::string name_; xplat::module::CxxModule::Provider provider_; + std::shared_ptr messageQueueThread_; std::unique_ptr module_; std::vector methods_; }; diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index f7ad78ed4e0266..300c809dbf18ca 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -33,24 +33,14 @@ void Instance::initializeBridge( std::unique_ptr callback, std::shared_ptr jsef, std::shared_ptr jsQueue, - std::unique_ptr nativeQueue, std::shared_ptr moduleRegistry) { callback_ = std::move(callback); - if (!nativeQueue) { - // TODO pass down a thread/queue from java, instead of creating our own. - - auto queue = folly::make_unique(); - std::thread t(queue->getUnregisteredRunLoop()); - t.detach(); - nativeQueue = std::move(queue); - } jsQueue->runOnQueueSync( - [this, &jsef, moduleRegistry, jsQueue, - nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable { + [this, &jsef, moduleRegistry, jsQueue] () mutable { nativeToJsBridge_ = folly::make_unique( - jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_); + jsef.get(), moduleRegistry, jsQueue, callback_); std::lock_guard lock(m_syncMutex); m_syncReady = true; diff --git a/ReactCommon/cxxreact/Instance.h b/ReactCommon/cxxreact/Instance.h index 4b8da4f26084e6..cf1cd2459a5478 100644 --- a/ReactCommon/cxxreact/Instance.h +++ b/ReactCommon/cxxreact/Instance.h @@ -31,7 +31,6 @@ class Instance { std::unique_ptr callback, std::shared_ptr jsef, std::shared_ptr jsQueue, - std::unique_ptr nativeQueue, std::shared_ptr moduleRegistry); void setSourceURL(std::string sourceURL); diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 8c6babfbf4e842..e863791bc78e49 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -24,11 +24,9 @@ class JsToNativeBridge : public react::ExecutorDelegate { public: JsToNativeBridge(NativeToJsBridge* nativeToJs, std::shared_ptr registry, - std::unique_ptr nativeQueue, std::shared_ptr callback) : m_nativeToJs(nativeToJs) , m_registry(registry) - , m_nativeQueue(std::move(nativeQueue)) , m_callback(callback) {} void registerExecutor(std::unique_ptr executor, @@ -51,24 +49,25 @@ class JsToNativeBridge : public react::ExecutorDelegate { CHECK(m_registry || calls.empty()) << "native module calls cannot be completed with no native modules"; ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor); - m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable { - m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty(); - - // An exception anywhere in here stops processing of the batch. This - // was the behavior of the Android bridge, and since exception handling - // terminates the whole bridge, there's not much point in continuing. - for (auto& call : react::parseMethodCalls(std::move(calls))) { - m_registry->callNativeMethod( - token, call.moduleId, call.methodId, std::move(call.arguments), call.callId); - } - if (isEndOfBatch) { - if (m_batchHadNativeModuleCalls) { - m_callback->onBatchComplete(); - m_batchHadNativeModuleCalls = false; - } - m_callback->decrementPendingJSCalls(); + m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty(); + + // An exception anywhere in here stops processing of the batch. This + // was the behavior of the Android bridge, and since exception handling + // terminates the whole bridge, there's not much point in continuing. + for (auto& call : parseMethodCalls(std::move(calls))) { + m_registry->callNativeMethod( + token, call.moduleId, call.methodId, std::move(call.arguments), call.callId); + } + if (isEndOfBatch) { + // onBatchComplete will be called on the native (module) queue, but + // decrementPendingJSCalls will be called sync. Be aware that the bridge may still + // be processing native calls when the birdge idle signaler fires. + if (m_batchHadNativeModuleCalls) { + m_callback->onBatchComplete(); + m_batchHadNativeModuleCalls = false; } - }); + m_callback->decrementPendingJSCalls(); + } } MethodCallResult callSerializableNativeHook( @@ -78,10 +77,6 @@ class JsToNativeBridge : public react::ExecutorDelegate { return m_registry->callSerializableNativeHook(token, moduleId, methodId, std::move(args)); } - void quitQueueSynchronous() { - m_nativeQueue->quitSynchronous(); - } - private: // These methods are always invoked from an Executor. The NativeToJsBridge @@ -91,7 +86,6 @@ class JsToNativeBridge : public react::ExecutorDelegate { // valid object during a call to a delegate method from an exectuto. NativeToJsBridge* m_nativeToJs; std::shared_ptr m_registry; - std::unique_ptr m_nativeQueue; std::shared_ptr m_callback; bool m_batchHadNativeModuleCalls = false; }; @@ -100,12 +94,10 @@ NativeToJsBridge::NativeToJsBridge( JSExecutorFactory* jsExecutorFactory, std::shared_ptr registry, std::shared_ptr jsQueue, - std::unique_ptr nativeQueue, std::shared_ptr callback) : m_destroyed(std::make_shared(false)) , m_mainExecutorToken(callback->createExecutorToken()) - , m_delegate(std::make_shared( - this, registry, std::move(nativeQueue), callback)) { + , m_delegate(std::make_shared(this, registry, callback)) { std::unique_ptr mainExecutor = jsExecutorFactory->createJSExecutor(m_delegate, jsQueue); // cached to avoid locked map lookup in the common case @@ -129,7 +121,6 @@ void NativeToJsBridge::loadApplication( startupScript=folly::makeMoveWrapper(std::move(startupScript)), startupScriptSourceURL=std::move(startupScriptSourceURL)] (JSExecutor* executor) mutable { - auto unbundle = unbundleWrap.move(); if (unbundle) { executor->setJSModulesUnbundle(std::move(unbundle)); @@ -322,7 +313,6 @@ ExecutorToken NativeToJsBridge::getTokenForExecutor(JSExecutor& executor) { } void NativeToJsBridge::destroy() { - m_delegate->quitQueueSynchronous(); auto* executorMessageQueueThread = getMessageQueueThread(m_mainExecutorToken); // All calls made through runOnExecutorQueue have an early exit if // m_destroyed is true. Setting this before the runOnQueueSync will cause @@ -331,7 +321,7 @@ void NativeToJsBridge::destroy() { executorMessageQueueThread->runOnQueueSync([this, executorMessageQueueThread] { m_mainExecutor->destroy(); executorMessageQueueThread->quitSynchronous(); - unregisterExecutor(*m_mainExecutor); + m_delegate->unregisterExecutor(*m_mainExecutor); m_mainExecutor = nullptr; }); } diff --git a/ReactCommon/cxxreact/NativeToJsBridge.h b/ReactCommon/cxxreact/NativeToJsBridge.h index 70ef620b887710..1c2505f834cb95 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.h +++ b/ReactCommon/cxxreact/NativeToJsBridge.h @@ -61,7 +61,6 @@ class NativeToJsBridge { JSExecutorFactory* jsExecutorFactory, std::shared_ptr registry, std::shared_ptr jsQueue, - std::unique_ptr nativeQueue, std::shared_ptr callback); virtual ~NativeToJsBridge(); From 73e81b87d3df80c406bec4ad0ad40e36531b7594 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 17 Mar 2017 06:55:39 -0700 Subject: [PATCH 068/366] Remove unused onNativeException delegate method Reviewed By: mhorowitz Differential Revision: D4597914 fbshipit-source-id: 82a109d1e6d8b0c93380840c22ec74dc75ab1a45 --- React/CxxBridge/RCTCxxBridge.mm | 4 ---- .../facebook/react/cxxbridge/CatalystInstanceImpl.java | 8 -------- .../com/facebook/react/cxxbridge/ReactCallback.java | 3 --- .../src/main/jni/xreact/jni/CatalystInstanceImpl.cpp | 10 ---------- ReactCommon/cxxreact/CxxNativeModule.cpp | 1 - ReactCommon/cxxreact/Instance.h | 1 - 6 files changed, 27 deletions(-) diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index 3e776b61232bb4..9eeceb387e6611 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -132,7 +132,6 @@ @interface RCTCxxBridge () - (instancetype)initWithParentBridge:(RCTBridge *)bridge; - (void)partialBatchDidFlush; - (void)batchDidComplete; -- (void)handleError:(NSError *)error; @end @@ -146,9 +145,6 @@ void onBatchComplete() override { } void incrementPendingJSCalls() override {} void decrementPendingJSCalls() override {} - void onNativeException(const std::string &what) override { - [bridge_ handleError:RCTErrorWithMessage(@(what.c_str()))]; - } ExecutorToken createExecutorToken() override { return ExecutorToken(std::make_shared()); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java index 38a9d548546131..80be1fbe51a559 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -168,14 +168,6 @@ public void decrementPendingJSCalls() { impl.decrementPendingJSCalls(); } } - - @Override - public void onNativeException(Exception e) { - CatalystInstanceImpl impl = mOuter.get(); - if (impl != null) { - impl.onNativeException(e); - } - } } private native void initializeBridge( diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ReactCallback.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ReactCallback.java index 6eb743dbe3035d..d4299a43994417 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ReactCallback.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/ReactCallback.java @@ -21,7 +21,4 @@ @DoNotStrip void decrementPendingJSCalls(); - - @DoNotStrip - void onNativeException(Exception e); } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index 23b1e4d2d21d56..3c6a45166c9231 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -71,16 +71,6 @@ class JInstanceCallback : public InstanceCallback { method(jobj_); } - void onNativeException(const std::string& what) override { - static auto exCtor = - Exception::javaClassStatic()->getConstructor(); - static auto method = - ReactCallback::javaClassStatic()->getMethod("onNativeException"); - - method(jobj_, Exception::javaClassStatic()->newObject( - exCtor, jni::make_jstring(what).get()).get()); - } - ExecutorToken createExecutorToken() override { auto jobj = JExecutorToken::newObjectCxxArgs(); return jobj->cthis()->getExecutorToken(jobj); diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index b487588bb76cbd..40d37f1a0ea7fc 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -133,7 +133,6 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo try { method.func(std::move(params), first, second); } catch (const facebook::xplat::JsArgumentException& ex) { - // This ends up passed to the onNativeException callback. throw; } catch (...) { // This means some C++ code is buggy. As above, we fail hard so the C++ diff --git a/ReactCommon/cxxreact/Instance.h b/ReactCommon/cxxreact/Instance.h index cf1cd2459a5478..13a66cfb38d2b0 100644 --- a/ReactCommon/cxxreact/Instance.h +++ b/ReactCommon/cxxreact/Instance.h @@ -19,7 +19,6 @@ struct InstanceCallback { virtual void onBatchComplete() = 0; virtual void incrementPendingJSCalls() = 0; virtual void decrementPendingJSCalls() = 0; - virtual void onNativeException(const std::string& what) = 0; virtual ExecutorToken createExecutorToken() = 0; virtual void onExecutorStopped(ExecutorToken) = 0; }; From 99d73c8927e67ddf5da7ebb7f35ac7804384b8a4 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 17 Mar 2017 06:55:41 -0700 Subject: [PATCH 069/366] Move FBReactJSExecutor to Instance Reviewed By: mhorowitz Differential Revision: D4589940 fbshipit-source-id: e3f0c374e78f77e54cede5c617414e41844f0f4d --- ReactCommon/cxxreact/Instance.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index 300c809dbf18ca..4188fe5720ea02 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -36,7 +36,6 @@ void Instance::initializeBridge( std::shared_ptr moduleRegistry) { callback_ = std::move(callback); - jsQueue->runOnQueueSync( [this, &jsef, moduleRegistry, jsQueue] () mutable { nativeToJsBridge_ = folly::make_unique( From ea069b69de427832226c4fe6e32803b4c7c31d2c Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 17 Mar 2017 06:55:43 -0700 Subject: [PATCH 070/366] Make Cxx modules lazy on iOS Reviewed By: mhorowitz Differential Revision: D4716040 fbshipit-source-id: 08cc1317e15f0b3bd1df6d76c483a560f5c43d53 --- React/CxxBridge/RCTCxxBridge.mm | 2 +- React/CxxModule/RCTCxxModule.h | 15 ++++++----- React/CxxModule/RCTCxxModule.mm | 46 +++++++++++++++++---------------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index 9eeceb387e6611..a98b0e2a9beee2 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -505,7 +505,7 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass modules.emplace_back(std::make_unique( _reactInstance, [moduleData.name UTF8String], - [moduleData] { return [(RCTCxxModule *)(moduleData.instance) move]; }, + [moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; }, std::make_shared(moduleData))); } else { modules.emplace_back(std::make_unique(self, moduleData)); diff --git a/React/CxxModule/RCTCxxModule.h b/React/CxxModule/RCTCxxModule.h index b95889df7e64b8..0f08f3113f816a 100644 --- a/React/CxxModule/RCTCxxModule.h +++ b/React/CxxModule/RCTCxxModule.h @@ -14,14 +14,15 @@ #import #import +/** + * Subclass RCTCxxModule to use cross-platform CxxModule on iOS. + * + * Subclasses must implement the createModule method to lazily produce the module. When running under the Cxx bridge + * modules will be accessed directly, under the Objective-C bridge method access is wrapped through RCTCxxMethod. + */ @interface RCTCxxModule : NSObject -- (instancetype)initWithCxxModule:(std::unique_ptr)module; - -- (NSArray *)methodsToExport; -- (NSDictionary *)constantsToExport; - -// Extracts the module from its objc wrapper -- (std::unique_ptr)move; +// To be implemented by subclasses +- (std::unique_ptr)createModule; @end diff --git a/React/CxxModule/RCTCxxModule.mm b/React/CxxModule/RCTCxxModule.mm index a2be24170983f4..3838cb7bcc32e9 100644 --- a/React/CxxModule/RCTCxxModule.mm +++ b/React/CxxModule/RCTCxxModule.mm @@ -23,37 +23,36 @@ @implementation RCTCxxModule std::unique_ptr _module; } -- (instancetype)init ++ (NSString *)moduleName { - return nil; + return @""; } -- (instancetype)initWithCxxModule:(std::unique_ptr)module +- (void)lazyInit { - RCTAssert([RCTBridgeModuleNameForClass([self class]) isEqualToString:@(module->getName().c_str())], - @"CxxModule class name %@ does not match runtime name %s", - RCTBridgeModuleNameForClass([self class]), module->getName().c_str()); - - if ((self = [super init])) { - _module = std::move(module); + if (!_module) { + _module = [self createModule]; + + if (_module) { + RCTAssert([RCTBridgeModuleNameForClass([self class]) isEqualToString:@(_module->getName().c_str())], + @"CxxModule class name %@ does not match runtime name %s", + RCTBridgeModuleNameForClass([self class]), _module->getName().c_str()); + } } - - return self; } -- (std::unique_ptr)move +- (std::unique_ptr)createModule { - return std::move(_module); + RCTAssert(NO, @"Subclass %@ must override createModule", [self class]); + return nullptr; } -+ (NSString *)moduleName +- (NSArray> *)methodsToExport; { - return @""; -} - -- (NSArray *)methodsToExport -{ - CHECK(_module) << "Can't call methodsToExport on moved module"; + [self lazyInit]; + if (!_module) { + return nil; + } NSMutableArray *moduleMethods = [NSMutableArray new]; for (const auto &method : _module->getMethods()) { @@ -62,9 +61,12 @@ - (NSArray *)methodsToExport return moduleMethods; } -- (NSDictionary *)constantsToExport +- (NSDictionary *)constantsToExport; { - CHECK(_module) << "Can't call constantsToExport on moved module"; + [self lazyInit]; + if (!_module) { + return nil; + } NSMutableDictionary *moduleConstants = [NSMutableDictionary new]; for (const auto &c : _module->getConstants()) { From ce270220e45abfeabebaaf68643269907bd2605a Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 17 Mar 2017 06:55:44 -0700 Subject: [PATCH 071/366] Extract module registry creation to helper Reviewed By: mhorowitz Differential Revision: D4721817 fbshipit-source-id: 2fa17ca5317a57d429aa75d6c1865932af27e02f --- React/CxxBridge/RCTCxxBridge.mm | 57 ++----------------- React/CxxModule/DispatchMessageQueueThread.h | 40 +++++++++++++ React/CxxModule/RCTCxxUtils.h | 10 ++++ React/CxxModule/RCTCxxUtils.mm | 35 +++++++++++- .../RCTNativeModule.h | 0 .../RCTNativeModule.mm | 0 6 files changed, 86 insertions(+), 56 deletions(-) create mode 100644 React/CxxModule/DispatchMessageQueueThread.h rename React/{CxxBridge => CxxModule}/RCTNativeModule.h (100%) rename React/{CxxBridge => CxxModule}/RCTNativeModule.mm (100%) diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index a98b0e2a9beee2..e48906af72e4b9 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -42,7 +42,6 @@ #import "NSDataBigString.h" #import "RCTMessageThread.h" -#import "RCTNativeModule.h" #import "RCTObjcExecutor.h" #ifdef WITH_FBSYSTRACE @@ -95,35 +94,6 @@ static bool isRAMBundle(NSData *script) { static std::atomic_bool cxxBridgeEnabled(false); -namespace { - -// RCTNativeModule arranges for native methods to be invoked on a queue which -// is not the JS thread. C++ modules don't use RCTNativeModule, so this little -// adapter does the work. - -class DispatchMessageQueueThread : public MessageQueueThread { -public: - DispatchMessageQueueThread(RCTModuleData *moduleData) - : moduleData_(moduleData) {} - - void runOnQueue(std::function&& func) override { - dispatch_async(moduleData_.methodQueue, [func=std::move(func)] { - func(); - }); - } - void runOnQueueSync(std::function&& func) override { - LOG(FATAL) << "Unsupported operation"; - } - void quitSynchronous() override { - LOG(FATAL) << "Unsupported operation"; - } - -private: - RCTModuleData *moduleData_; -}; - -} - @interface RCTCxxBridge () @property (nonatomic, weak, readonly) RCTBridge *parentBridge; @@ -484,35 +454,16 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass return _moduleDataByName[RCTBridgeModuleNameForClass(moduleClass)].hasInstance; } -- (std::shared_ptr)_createModuleRegistry +- (std::shared_ptr)_buildModuleRegistry { if (!self.valid) { return {}; } [_performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig]; - RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge createModuleRegistry]", nil); - - std::vector> modules; - for (RCTModuleData *moduleData in _moduleDataByID) { - if ([moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) { - // If a module does not support automatic instantiation, and - // wasn't provided as an extra module, it may not have an - // instance. If so, skip it. - if (![moduleData hasInstance]) { - continue; - } - modules.emplace_back(std::make_unique( - _reactInstance, - [moduleData.name UTF8String], - [moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; }, - std::make_shared(moduleData))); - } else { - modules.emplace_back(std::make_unique(self, moduleData)); - } - } + RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge buildModuleRegistry]", nil); - auto registry = std::make_shared(std::move(modules)); + auto registry = buildModuleRegistry(_moduleDataByID, self, _reactInstance); [_performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); @@ -538,7 +489,7 @@ - (void)_initializeBridge:(std::shared_ptr)executorFactory std::unique_ptr(new RCTInstanceCallback(self)), executorFactory, _jsMessageThread, - std::move([self _createModuleRegistry])); + std::move([self _buildModuleRegistry])); #if RCT_PROFILE if (RCTProfileIsProfiling()) { diff --git a/React/CxxModule/DispatchMessageQueueThread.h b/React/CxxModule/DispatchMessageQueueThread.h new file mode 100644 index 00000000000000..c1fb9fb0bdc696 --- /dev/null +++ b/React/CxxModule/DispatchMessageQueueThread.h @@ -0,0 +1,40 @@ +/** + * 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. + */ + +#include + +namespace facebook { +namespace react { + +// RCTNativeModule arranges for native methods to be invoked on a queue which +// is not the JS thread. C++ modules don't use RCTNativeModule, so this little +// adapter does the work. + +class DispatchMessageQueueThread : public MessageQueueThread { +public: + DispatchMessageQueueThread(RCTModuleData *moduleData) + : moduleData_(moduleData) {} + + void runOnQueue(std::function&& func) override { + dispatch_async(moduleData_.methodQueue, [func=std::move(func)] { + func(); + }); + } + void runOnQueueSync(std::function&& func) override { + LOG(FATAL) << "Unsupported operation"; + } + void quitSynchronous() override { + LOG(FATAL) << "Unsupported operation"; + } + +private: + RCTModuleData *moduleData_; +}; + +} } diff --git a/React/CxxModule/RCTCxxUtils.h b/React/CxxModule/RCTCxxUtils.h index 2278cf42de8371..e84032682d154d 100644 --- a/React/CxxModule/RCTCxxUtils.h +++ b/React/CxxModule/RCTCxxUtils.h @@ -7,12 +7,18 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include + #import #include #include +#include #include #include +@class RCTBridge; +@class RCTModuleData; + @interface RCTConvert (folly) + (folly::dynamic)folly_dynamic:(id)json; @@ -22,6 +28,10 @@ namespace facebook { namespace react { +class Instance; + +std::shared_ptr buildModuleRegistry(NSArray *modules, RCTBridge *bridge, const std::shared_ptr &instance); + JSContext *contextForGlobalContextRef(JSGlobalContextRef contextRef); /* diff --git a/React/CxxModule/RCTCxxUtils.mm b/React/CxxModule/RCTCxxUtils.mm index 3283f82032db63..740f5960aa3e96 100644 --- a/React/CxxModule/RCTCxxUtils.mm +++ b/React/CxxModule/RCTCxxUtils.mm @@ -10,10 +10,15 @@ #import "RCTCxxUtils.h" #import +#import #import - +#include #include +#import "DispatchMessageQueueThread.h" +#import "RCTCxxModule.h" +#import "RCTNativeModule.h" + using namespace facebook::react; @implementation RCTConvert (folly) @@ -36,6 +41,30 @@ @implementation RCTConvert (folly) namespace facebook { namespace react { +std::shared_ptr buildModuleRegistry(NSArray *modules, RCTBridge *bridge, const std::shared_ptr &instance) +{ + std::vector> nativeModules; + for (RCTModuleData *moduleData in modules) { + if ([moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) { + // If a module does not support automatic instantiation, and + // wasn't provided as an extra module, it may not have an + // instance. If so, skip it. + if (![moduleData hasInstance]) { + continue; + } + nativeModules.emplace_back(std::make_unique( + instance, + [moduleData.name UTF8String], + [moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; }, + std::make_shared(moduleData))); + } else { + nativeModules.emplace_back(std::make_unique(bridge, moduleData)); + } + } + + return std::make_shared(std::move(nativeModules)); +} + JSContext *contextForGlobalContextRef(JSGlobalContextRef contextRef) { static std::mutex s_mutex; @@ -61,7 +90,7 @@ @implementation RCTConvert (folly) return ctx; } -static NSError *errorWithException(const std::exception& e) +static NSError *errorWithException(const std::exception &e) { NSString *msg = @(e.what()); NSMutableDictionary *errorInfo = [NSMutableDictionary dictionary]; @@ -75,7 +104,7 @@ @implementation RCTConvert (folly) NSError *nestedError; try { std::rethrow_if_nested(e); - } catch(const std::exception& e) { + } catch(const std::exception &e) { nestedError = errorWithException(e); } catch(...) {} diff --git a/React/CxxBridge/RCTNativeModule.h b/React/CxxModule/RCTNativeModule.h similarity index 100% rename from React/CxxBridge/RCTNativeModule.h rename to React/CxxModule/RCTNativeModule.h diff --git a/React/CxxBridge/RCTNativeModule.mm b/React/CxxModule/RCTNativeModule.mm similarity index 100% rename from React/CxxBridge/RCTNativeModule.mm rename to React/CxxModule/RCTNativeModule.mm From e85477552784f599ce7ec9274715affef183a2fa Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 17 Mar 2017 09:07:23 -0700 Subject: [PATCH 072/366] Invalidate layout when node is removed from tree Reviewed By: astreet Differential Revision: D4716022 fbshipit-source-id: 399cc64a4b3f5fd3fc469ea37bdd31abe474dc6c --- ReactCommon/yoga/yoga/Yoga.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactCommon/yoga/yoga/Yoga.c b/ReactCommon/yoga/yoga/Yoga.c index 323bb881545626..65a3ba46f98707 100644 --- a/ReactCommon/yoga/yoga/Yoga.c +++ b/ReactCommon/yoga/yoga/Yoga.c @@ -423,6 +423,7 @@ void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32 void YGNodeRemoveChild(const YGNodeRef node, const YGNodeRef child) { if (YGNodeListDelete(node->children, child) != NULL) { + child->layout = gYGNodeDefaults.layout; // layout is no longer valid child->parent = NULL; YGNodeMarkDirtyInternal(node); } From f0240e004a021c2bb5889f507726949d849e8ea6 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 17 Mar 2017 09:07:24 -0700 Subject: [PATCH 073/366] Avoid transfering cached layout information to java Reviewed By: astreet Differential Revision: D4716024 fbshipit-source-id: c30763a6fc7426d653c7a6ca129615cddb4140e9 --- .../src/main/jni/first-party/yogajni/jni/YGJNI.cpp | 6 ++++++ ReactCommon/yoga/yoga/Yoga.c | 13 +++++++++++++ ReactCommon/yoga/yoga/Yoga.h | 2 ++ 3 files changed, 21 insertions(+) diff --git a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp index 72e704c8c82e81..c0b0dd49505ec6 100644 --- a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp +++ b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp @@ -24,6 +24,12 @@ static void YGTransferLayoutDirection(YGNodeRef node, alias_ref javaNod } static void YGTransferLayoutOutputsRecursive(YGNodeRef root) { + // If the node is using a cached layout it means we have already + // transfered it to java. + if (YGNodeIsUsingCachedLayout(root)) { + return; + } + if (auto obj = YGNodeJobject(root)->lockLocal()) { static auto widthField = obj->getClass()->getField("mWidth"); static auto heightField = obj->getClass()->getField("mHeight"); diff --git a/ReactCommon/yoga/yoga/Yoga.c b/ReactCommon/yoga/yoga/Yoga.c index 65a3ba46f98707..1b3961c9c16804 100644 --- a/ReactCommon/yoga/yoga/Yoga.c +++ b/ReactCommon/yoga/yoga/Yoga.c @@ -459,6 +459,18 @@ void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) { } } +static int YGNodeRootGenerationCount(const YGNodeRef node) { + if (node->parent) { + return YGNodeRootGenerationCount(node->parent); + } else { + return node->layout.generationCount; + } +} + +bool YGNodeIsUsingCachedLayout(const YGNodeRef node) { + return node->layout.generationCount != YGNodeRootGenerationCount(node); +} + static inline float YGResolveFlexGrow(const YGNodeRef node) { if (!YGFloatIsUndefined(node->style.flexGrow)) { return node->style.flexGrow; @@ -1811,6 +1823,7 @@ static void YGZeroOutLayoutRecursivly(const YGNodeRef node) { node->layout.cachedLayout.widthMeasureMode = YGMeasureModeExactly; node->layout.cachedLayout.computedWidth = 0; node->layout.cachedLayout.computedHeight = 0; + node->layout.generationCount = gCurrentGenerationCount; const uint32_t childCount = YGNodeGetChildCount(node); for (uint32_t i = 0; i < childCount; i++) { const YGNodeRef child = YGNodeListGet(node->children, i); diff --git a/ReactCommon/yoga/yoga/Yoga.h b/ReactCommon/yoga/yoga/Yoga.h index d5c5258a7dca15..054b5dc5079a0e 100644 --- a/ReactCommon/yoga/yoga/Yoga.h +++ b/ReactCommon/yoga/yoga/Yoga.h @@ -218,6 +218,8 @@ YG_NODE_LAYOUT_EDGE_PROPERTY(float, Margin); YG_NODE_LAYOUT_EDGE_PROPERTY(float, Border); YG_NODE_LAYOUT_EDGE_PROPERTY(float, Padding); +bool YGNodeIsUsingCachedLayout(const YGNodeRef node); + WIN_EXPORT void YGSetLogger(YGLogger logger); WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...); From 50ff7167cbf6254cf21babd04ba1ac3369858c28 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 17 Mar 2017 09:28:55 -0700 Subject: [PATCH 074/366] Optimize ReactShadowNode by more manually converting between string and enums Reviewed By: astreet Differential Revision: D4713847 fbshipit-source-id: 5ef5b59f85bb26f0af9af0698c9fc1f36eb5f7bf --- .../react/uimanager/LayoutShadowNode.java | 301 ++++++++++++++++-- 1 file changed, 275 insertions(+), 26 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java index 3f5020e3ad4157..e57c9f6158ecb4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java @@ -4,9 +4,9 @@ import javax.annotation.Nullable; -import java.util.Locale; import com.facebook.react.bridge.Dynamic; +import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableType; import com.facebook.yoga.YogaAlign; @@ -246,9 +246,34 @@ public void setFlexDirection(@Nullable String flexDirection) { if (isVirtual()) { return; } - setFlexDirection( - flexDirection == null ? YogaFlexDirection.COLUMN : YogaFlexDirection.valueOf( - flexDirection.toUpperCase(Locale.US).replace("-", "_"))); + + if (flexDirection == null) { + setFlexDirection(YogaFlexDirection.COLUMN); + return; + } + + switch (flexDirection) { + case "column": { + setFlexDirection(YogaFlexDirection.COLUMN); + break; + } + case "column-reverse": { + setFlexDirection(YogaFlexDirection.COLUMN_REVERSE); + break; + } + case "row": { + setFlexDirection(YogaFlexDirection.ROW); + break; + } + case "row-reverse": { + setFlexDirection(YogaFlexDirection.ROW_REVERSE); + break; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for flexDirection: " + flexDirection); + } + } } @ReactProp(name = ViewProps.FLEX_WRAP) @@ -256,12 +281,25 @@ public void setFlexWrap(@Nullable String flexWrap) { if (isVirtual()) { return; } - if (flexWrap == null || flexWrap.equals("nowrap")) { + + if (flexWrap == null) { setFlexWrap(YogaWrap.NO_WRAP); - } else if (flexWrap.equals("wrap")) { - setFlexWrap(YogaWrap.WRAP); - } else { - throw new IllegalArgumentException("Unknown flexWrap value: " + flexWrap); + return; + } + + switch (flexWrap) { + case "nowrap": { + setFlexWrap(YogaWrap.NO_WRAP); + break; + } + case "wrap": { + setFlexWrap(YogaWrap.WRAP); + break; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for flexWrap: " + flexWrap); + } } } @@ -270,8 +308,50 @@ public void setAlignSelf(@Nullable String alignSelf) { if (isVirtual()) { return; } - setAlignSelf(alignSelf == null ? YogaAlign.AUTO : YogaAlign.valueOf( - alignSelf.toUpperCase(Locale.US).replace("-", "_"))); + + if (alignSelf == null) { + setAlignSelf(YogaAlign.AUTO); + return; + } + + switch (alignSelf) { + case "auto": { + setAlignSelf(YogaAlign.FLEX_START); + return; + } + case "flex-start": { + setAlignSelf(YogaAlign.CENTER); + return; + } + case "center": { + setAlignSelf(YogaAlign.CENTER); + return; + } + case "flex-end": { + setAlignSelf(YogaAlign.FLEX_END); + return; + } + case "stretch": { + setAlignSelf(YogaAlign.STRETCH); + return; + } + case "baseline": { + setAlignSelf(YogaAlign.BASELINE); + return; + } + case "space-between": { + setAlignSelf(YogaAlign.SPACE_BETWEEN); + return; + } + case "space-around": { + setAlignSelf(YogaAlign.SPACE_AROUND); + return; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for alignSelf: " + alignSelf); + } + } } @ReactProp(name = ViewProps.ALIGN_ITEMS) @@ -279,9 +359,50 @@ public void setAlignItems(@Nullable String alignItems) { if (isVirtual()) { return; } - setAlignItems( - alignItems == null ? YogaAlign.STRETCH : YogaAlign.valueOf( - alignItems.toUpperCase(Locale.US).replace("-", "_"))); + + if (alignItems == null) { + setAlignItems(YogaAlign.STRETCH); + return; + } + + switch (alignItems) { + case "auto": { + setAlignItems(YogaAlign.FLEX_START); + return; + } + case "flex-start": { + setAlignItems(YogaAlign.CENTER); + return; + } + case "center": { + setAlignItems(YogaAlign.CENTER); + return; + } + case "flex-end": { + setAlignItems(YogaAlign.FLEX_END); + return; + } + case "stretch": { + setAlignItems(YogaAlign.STRETCH); + return; + } + case "baseline": { + setAlignItems(YogaAlign.BASELINE); + return; + } + case "space-between": { + setAlignItems(YogaAlign.SPACE_BETWEEN); + return; + } + case "space-around": { + setAlignItems(YogaAlign.SPACE_AROUND); + return; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for alignItems: " + alignItems); + } + } } @ReactProp(name = ViewProps.ALIGN_CONTENT) @@ -289,9 +410,50 @@ public void setAlignContent(@Nullable String alignContent) { if (isVirtual()) { return; } - setAlignContent( - alignContent == null ? YogaAlign.FLEX_START : YogaAlign.valueOf( - alignContent.toUpperCase(Locale.US).replace("-", "_"))); + + if (alignContent == null) { + setAlignContent(YogaAlign.FLEX_START); + return; + } + + switch (alignContent) { + case "auto": { + setAlignContent(YogaAlign.FLEX_START); + return; + } + case "flex-start": { + setAlignContent(YogaAlign.CENTER); + return; + } + case "center": { + setAlignContent(YogaAlign.CENTER); + return; + } + case "flex-end": { + setAlignContent(YogaAlign.FLEX_END); + return; + } + case "stretch": { + setAlignContent(YogaAlign.STRETCH); + return; + } + case "baseline": { + setAlignContent(YogaAlign.BASELINE); + return; + } + case "space-between": { + setAlignContent(YogaAlign.SPACE_BETWEEN); + return; + } + case "space-around": { + setAlignContent(YogaAlign.SPACE_AROUND); + return; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for alignContent: " + alignContent); + } + } } @ReactProp(name = ViewProps.JUSTIFY_CONTENT) @@ -299,8 +461,38 @@ public void setJustifyContent(@Nullable String justifyContent) { if (isVirtual()) { return; } - setJustifyContent(justifyContent == null ? YogaJustify.FLEX_START : YogaJustify.valueOf( - justifyContent.toUpperCase(Locale.US).replace("-", "_"))); + + if (justifyContent == null) { + setJustifyContent(YogaJustify.FLEX_START); + return; + } + + switch (justifyContent) { + case "flex-start": { + setJustifyContent(YogaJustify.FLEX_START); + break; + } + case "center": { + setJustifyContent(YogaJustify.CENTER); + break; + } + case "flex-end": { + setJustifyContent(YogaJustify.FLEX_END); + break; + } + case "space-between": { + setJustifyContent(YogaJustify.SPACE_BETWEEN); + break; + } + case "space-around": { + setJustifyContent(YogaJustify.SPACE_AROUND); + break; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for justifyContent: " + justifyContent); + } + } } @ReactProp(name = ViewProps.OVERFLOW) @@ -308,8 +500,30 @@ public void setOverflow(@Nullable String overflow) { if (isVirtual()) { return; } - setOverflow(overflow == null ? YogaOverflow.VISIBLE : YogaOverflow.valueOf( - overflow.toUpperCase(Locale.US).replace("-", "_"))); + + if (overflow == null) { + setOverflow(YogaOverflow.VISIBLE); + return; + } + + switch (overflow) { + case "visible": { + setOverflow(YogaOverflow.VISIBLE); + break; + } + case "hidden": { + setOverflow(YogaOverflow.HIDDEN); + break; + } + case "scroll": { + setOverflow(YogaOverflow.SCROLL); + break; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for overflow: " + overflow); + } + } } @ReactProp(name = ViewProps.DISPLAY) @@ -317,8 +531,26 @@ public void setDisplay(@Nullable String display) { if (isVirtual()) { return; } - setDisplay(display == null ? YogaDisplay.FLEX : YogaDisplay.valueOf( - display.toUpperCase(Locale.US).replace("-", "_"))); + + if (display == null) { + setDisplay(YogaDisplay.FLEX); + return; + } + + switch (display) { + case "flex": { + setDisplay(YogaDisplay.FLEX); + break; + } + case "none": { + setDisplay(YogaDisplay.NONE); + break; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for display: " + display); + } + } } @ReactPropGroup(names = { @@ -424,9 +656,26 @@ public void setPosition(@Nullable String position) { if (isVirtual()) { return; } - YogaPositionType positionType = position == null ? - YogaPositionType.RELATIVE : YogaPositionType.valueOf(position.toUpperCase(Locale.US)); - setPositionType(positionType); + + if (position == null) { + setPositionType(YogaPositionType.RELATIVE); + return; + } + + switch (position) { + case "relative": { + setPositionType(YogaPositionType.RELATIVE); + break; + } + case "absolute": { + setPositionType(YogaPositionType.ABSOLUTE); + break; + } + default: { + throw new JSApplicationIllegalArgumentException( + "invalid value for position: " + position); + } + } } @Override From 60142adc72bdf11255c39c22b648a3d8c103aa5d Mon Sep 17 00:00:00 2001 From: Lukas Piatkowski Date: Fri, 17 Mar 2017 09:55:23 -0700 Subject: [PATCH 075/366] Extract PackagerConnectionSettings to ensure easier reusability of PackagerConnection module Reviewed By: cwdick Differential Revision: D4689535 fbshipit-source-id: f698837f407a03bf91521cc5e921c66f5755e6e0 --- .../react/devsupport/DevInternalSettings.java | 12 ++-- .../react/devsupport/DevServerHelper.java | 51 ++++------------ .../facebook/react/modules/systeminfo/BUCK | 22 ++++++- .../facebook/react/packagerconnection/BUCK | 1 + .../packagerconnection/JSPackagerClient.java | 16 ++++- .../PackagerConnectionSettings.java | 59 +++++++++++++++++++ .../JSPackagerClientTest.java | 34 ++++++----- local-cli/server/util/messageSocket.js | 2 +- 8 files changed, 132 insertions(+), 65 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java index ce9ea690ee5490..bfd8d0403f0e05 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java @@ -17,6 +17,7 @@ import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; +import com.facebook.react.packagerconnection.PackagerConnectionSettings; /** * Helper class for accessing developers settings that should not be accessed outside of the package @@ -31,7 +32,6 @@ public class DevInternalSettings implements private static final String PREFS_FPS_DEBUG_KEY = "fps_debug"; private static final String PREFS_JS_DEV_MODE_DEBUG_KEY = "js_dev_mode_debug"; private static final String PREFS_JS_MINIFY_DEBUG_KEY = "js_minify_debug"; - private static final String PREFS_DEBUG_SERVER_HOST_KEY = "debug_http_host"; private static final String PREFS_ANIMATIONS_DEBUG_KEY = "animations_debug"; private static final String PREFS_RELOAD_ON_JS_CHANGE_KEY = "reload_on_js_change"; private static final String PREFS_INSPECTOR_DEBUG_KEY = "inspector_debug"; @@ -40,6 +40,7 @@ public class DevInternalSettings implements private final SharedPreferences mPreferences; private final Listener mListener; + private final PackagerConnectionSettings mPackagerConnectionSettings; public DevInternalSettings( Context applicationContext, @@ -47,6 +48,11 @@ public DevInternalSettings( mListener = listener; mPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext); mPreferences.registerOnSharedPreferenceChangeListener(this); + mPackagerConnectionSettings = new PackagerConnectionSettings(applicationContext); + } + + public PackagerConnectionSettings getPackagerConnectionSettings() { + return mPackagerConnectionSettings; } @Override @@ -73,10 +79,6 @@ public boolean isJSMinifyEnabled() { return mPreferences.getBoolean(PREFS_JS_MINIFY_DEBUG_KEY, false); } - public @Nullable String getDebugServerHost() { - return mPreferences.getString(PREFS_DEBUG_SERVER_HOST_KEY, null); - } - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (mListener != null) { if (PREFS_FPS_DEBUG_KEY.equals(key) || diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index 5c3750dfe1ea87..b102002d8a0eb8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -73,7 +73,6 @@ public class DevServerHelper { private static final String ONCHANGE_ENDPOINT_URL_FORMAT = "http://%s/onchange"; private static final String WEBSOCKET_PROXY_URL_FORMAT = "ws://%s/debugger-proxy?role=client"; - private static final String PACKAGER_CONNECTION_URL_FORMAT = "ws://%s/message?role=android-rn-devserverhelper"; private static final String PACKAGER_STATUS_URL_FORMAT = "http://%s/status"; private static final String HEAP_CAPTURE_UPLOAD_URL_FORMAT = "http://%s/jscheapcaptureupload"; private static final String INSPECTOR_DEVICE_URL_FORMAT = "http://%s/inspector/device?name=%s"; @@ -152,7 +151,7 @@ public void onRequest(@Nullable Object params, JSPackagerClient.Responder respon }); handlers.putAll(new FileIoHandler().handlers()); - mPackagerClient = new JSPackagerClient(getPackagerConnectionURL(), handlers); + mPackagerClient = new JSPackagerClient("devserverhelper", mSettings.getPackagerConnectionSettings(), handlers); mPackagerClient.init(); return null; @@ -213,22 +212,18 @@ public static String getReloadAppAction(Context context) { } public String getWebsocketProxyURL() { - return String.format(Locale.US, WEBSOCKET_PROXY_URL_FORMAT, getDebugServerHost()); - } - - private String getPackagerConnectionURL() { - return String.format(Locale.US, PACKAGER_CONNECTION_URL_FORMAT, getDebugServerHost()); + return String.format(Locale.US, WEBSOCKET_PROXY_URL_FORMAT, mSettings.getPackagerConnectionSettings().getDebugServerHost()); } public String getHeapCaptureUploadUrl() { - return String.format(Locale.US, HEAP_CAPTURE_UPLOAD_URL_FORMAT, getDebugServerHost()); + return String.format(Locale.US, HEAP_CAPTURE_UPLOAD_URL_FORMAT, mSettings.getPackagerConnectionSettings().getDebugServerHost()); } public String getInspectorDeviceUrl() { return String.format( Locale.US, INSPECTOR_DEVICE_URL_FORMAT, - getDebugServerHost(), + mSettings.getPackagerConnectionSettings().getDebugServerHost(), AndroidInfoHelpers.getFriendlyDeviceName()); } @@ -260,30 +255,6 @@ private boolean getHMR() { return mSettings.isHotModuleReplacementEnabled(); } - /** - * @return the host to use when connecting to the bundle server. - */ - private String getDebugServerHost() { - // Check debug server host setting first. If empty try to detect emulator type and use default - // hostname for those - String hostFromSettings = mSettings.getDebugServerHost(); - - if (!TextUtils.isEmpty(hostFromSettings)) { - return Assertions.assertNotNull(hostFromSettings); - } - - String host = AndroidInfoHelpers.getServerHost(); - - if (host.equals(AndroidInfoHelpers.DEVICE_LOCALHOST)) { - FLog.w( - ReactConstants.TAG, - "You seem to be running on device. Run 'adb reverse tcp:8081 tcp:8081' " + - "to forward the debug server's port to the device."); - } - - return host; - } - private static String createBundleURL(String host, String jsModulePath, boolean devMode, boolean hmr, boolean jsMinify) { return String.format(Locale.US, BUNDLE_URL_FORMAT, host, jsModulePath, devMode, hmr, jsMinify); } @@ -294,7 +265,7 @@ private static String createResourceURL(String host, String resourcePath) { public String getDevServerBundleURL(final String jsModulePath) { return createBundleURL( - getDebugServerHost(), + mSettings.getPackagerConnectionSettings().getDebugServerHost(), jsModulePath, getDevMode(), getHMR(), @@ -438,7 +409,7 @@ public void cancelDownloadBundleFromURL() { } public void isPackagerRunning(final PackagerStatusCallback callback) { - String statusURL = createPackagerStatusURL(getDebugServerHost()); + String statusURL = createPackagerStatusURL(mSettings.getPackagerConnectionSettings().getDebugServerHost()); Request request = new Request.Builder() .url(statusURL) .build(); @@ -558,11 +529,11 @@ public void onResponse(Call call, Response response) throws IOException { } private String createOnChangeEndpointUrl() { - return String.format(Locale.US, ONCHANGE_ENDPOINT_URL_FORMAT, getDebugServerHost()); + return String.format(Locale.US, ONCHANGE_ENDPOINT_URL_FORMAT, mSettings.getPackagerConnectionSettings().getDebugServerHost()); } private String createLaunchJSDevtoolsCommandUrl() { - return String.format(Locale.US, LAUNCH_JS_DEVTOOLS_COMMAND_URL_FORMAT, getDebugServerHost()); + return String.format(Locale.US, LAUNCH_JS_DEVTOOLS_COMMAND_URL_FORMAT, mSettings.getPackagerConnectionSettings().getDebugServerHost()); } public void launchJSDevtools() { @@ -584,11 +555,11 @@ public void onResponse(Call call, Response response) throws IOException { } public String getSourceMapUrl(String mainModuleName) { - return String.format(Locale.US, SOURCE_MAP_URL_FORMAT, getDebugServerHost(), mainModuleName, getDevMode(), getHMR(), getJSMinifyMode()); + return String.format(Locale.US, SOURCE_MAP_URL_FORMAT, mSettings.getPackagerConnectionSettings().getDebugServerHost(), mainModuleName, getDevMode(), getHMR(), getJSMinifyMode()); } public String getSourceUrl(String mainModuleName) { - return String.format(Locale.US, BUNDLE_URL_FORMAT, getDebugServerHost(), mainModuleName, getDevMode(), getHMR(), getJSMinifyMode()); + return String.format(Locale.US, BUNDLE_URL_FORMAT, mSettings.getPackagerConnectionSettings().getDebugServerHost(), mainModuleName, getDevMode(), getHMR(), getJSMinifyMode()); } public String getJSBundleURLForRemoteDebugging(String mainModuleName) { @@ -607,7 +578,7 @@ public String getJSBundleURLForRemoteDebugging(String mainModuleName) { public @Nullable File downloadBundleResourceFromUrlSync( final String resourcePath, final File outputFile) { - final String resourceURL = createResourceURL(getDebugServerHost(), resourcePath); + final String resourceURL = createResourceURL(mSettings.getPackagerConnectionSettings().getDebugServerHost(), resourcePath); final Request request = new Request.Builder() .url(resourceURL) .build(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK index cdbc287a7af3ec..9d59eff8107aca 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK @@ -2,15 +2,33 @@ include_defs("//ReactAndroid/DEFS") android_library( name = "systeminfo", - srcs = glob(["**/*.java"]), + srcs = [ + "AndroidInfoModule.java", + ], + exported_deps = [ + ":systeminfo-moduleless", + ], visibility = [ "PUBLIC", ], deps = [ - react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], ) + +android_library( + name = "systeminfo-moduleless", + srcs = [ + "AndroidInfoHelpers.java", + ], + visibility = [ + "PUBLIC", + ], + deps = [ + react_native_dep("third-party/java/infer-annotations:infer-annotations"), + react_native_dep("third-party/java/jsr-305:jsr-305"), + ], +) diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK index 125fa76df098c1..25f3213889469d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK @@ -13,5 +13,6 @@ android_library( react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okhttp:okhttp3-ws"), react_native_dep("third-party/java/okio:okio"), + react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo-moduleless"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java index d8cdc6544d706b..2568ca9eacea0d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java @@ -13,7 +13,10 @@ import java.util.HashMap; import java.util.Map; +import android.net.Uri; + import com.facebook.common.logging.FLog; +import com.facebook.react.modules.systeminfo.AndroidInfoHelpers; import okhttp3.RequestBody; import okhttp3.ResponseBody; @@ -26,6 +29,7 @@ */ final public class JSPackagerClient implements ReconnectingWebSocket.MessageCallback { private static final String TAG = JSPackagerClient.class.getSimpleName(); + private static final String PACKAGER_CONNECTION_URL_FORMAT = "ws://%s/message?device=%s&app=%s&context=%s"; private static final int PROTOCOL_VERSION = 2; public class Responder { @@ -83,8 +87,18 @@ final public void onNotification(@Nullable Object params) { private ReconnectingWebSocket mWebSocket; private Map mRequestHandlers; - public JSPackagerClient(String url, Map requestHandlers) { + public JSPackagerClient(String clientId, PackagerConnectionSettings settings, Map requestHandlers) { super(); + + Uri.Builder builder = new Uri.Builder(); + builder.scheme("ws") + .encodedAuthority(settings.getDebugServerHost()) + .appendPath("message") + .appendQueryParameter("device", AndroidInfoHelpers.getFriendlyDeviceName()) + .appendQueryParameter("app", settings.getPackageName()) + .appendQueryParameter("clientid", clientId); + String url = builder.build().toString(); + mWebSocket = new ReconnectingWebSocket(url, this); mRequestHandlers = requestHandlers; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java new file mode 100644 index 00000000000000..668931dd96bef2 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java @@ -0,0 +1,59 @@ +/** + * 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. + */ + +package com.facebook.react.packagerconnection; + +import javax.annotation.Nullable; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.text.TextUtils; + +import com.facebook.common.logging.FLog; +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.modules.systeminfo.AndroidInfoHelpers; + +public class PackagerConnectionSettings { + private static final String TAG = PackagerConnectionSettings.class.getSimpleName(); + private static final String PREFS_DEBUG_SERVER_HOST_KEY = "debug_http_host"; + + private final SharedPreferences mPreferences; + private final String mPackageName; + + public PackagerConnectionSettings(Context applicationContext) { + mPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext); + mPackageName = applicationContext.getPackageName(); + } + + public String getDebugServerHost() { + // Check host setting first. If empty try to detect emulator type and use default + // hostname for those + String hostFromSettings = mPreferences.getString(PREFS_DEBUG_SERVER_HOST_KEY, null); + + if (!TextUtils.isEmpty(hostFromSettings)) { + return Assertions.assertNotNull(hostFromSettings); + } + + String host = AndroidInfoHelpers.getServerHost(); + + if (host.equals(AndroidInfoHelpers.DEVICE_LOCALHOST)) { + FLog.w( + TAG, + "You seem to be running on device. Run 'adb reverse tcp:8081 tcp:8081' " + + "to forward the debug server's port to the device."); + } + + return host; + } + + public @Nullable String getPackageName() { + return mPackageName; + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java index 6c67780e0cec24..d930384a3bb6e3 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java @@ -9,6 +9,7 @@ package com.facebook.react.packagerconnection; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,11 +33,19 @@ private static Map createRH( return m; } + private PackagerConnectionSettings mSettings; + + @Before + public void setUp() { + mSettings = mock(PackagerConnectionSettings.class); + when(mSettings.getDebugServerHost()).thenReturn("ws://not_needed"); + when(mSettings.getPackageName()).thenReturn("my_test_package"); + } + @Test public void test_onMessage_ShouldTriggerNotification() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -49,8 +58,7 @@ public void test_onMessage_ShouldTriggerNotification() throws IOException { @Test public void test_onMessage_ShouldTriggerRequest() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -63,8 +71,7 @@ public void test_onMessage_ShouldTriggerRequest() throws IOException { @Test public void test_onMessage_WithoutParams_ShouldTriggerNotification() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -77,8 +84,7 @@ public void test_onMessage_WithoutParams_ShouldTriggerNotification() throws IOEx @Test public void test_onMessage_WithInvalidContentType_ShouldNotTriggerCallback() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -91,8 +97,7 @@ public void test_onMessage_WithInvalidContentType_ShouldNotTriggerCallback() thr @Test public void test_onMessage_WithoutMethod_ShouldNotTriggerCallback() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -105,8 +110,7 @@ public void test_onMessage_WithoutMethod_ShouldNotTriggerCallback() throws IOExc @Test public void test_onMessage_With_Null_Action_ShouldNotTriggerCallback() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -119,8 +123,7 @@ public void test_onMessage_With_Null_Action_ShouldNotTriggerCallback() throws IO @Test public void test_onMessage_WithInvalidMethod_ShouldNotTriggerCallback() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( @@ -133,8 +136,7 @@ public void test_onMessage_WithInvalidMethod_ShouldNotTriggerCallback() throws I @Test public void test_onMessage_WrongVersion_ShouldNotTriggerCallback() throws IOException { JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); - final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("methodValue", handler)); - WebSocket webSocket = mock(WebSocket.class); + final JSPackagerClient client = new JSPackagerClient("test_client", mSettings, createRH("methodValue", handler)); client.onMessage( ResponseBody.create( diff --git a/local-cli/server/util/messageSocket.js b/local-cli/server/util/messageSocket.js index 45f9b06609a7ab..94b6eea1bd44e2 100644 --- a/local-cli/server/util/messageSocket.js +++ b/local-cli/server/util/messageSocket.js @@ -130,7 +130,7 @@ function attachToServer(server, path) { result = {}; clients.forEach((otherWs, otherId) => { if (clientId !== otherId) { - result[otherId] = url.parse(otherWs.upgradeReq.url).query; + result[otherId] = url.parse(otherWs.upgradeReq.url, true).query; } }); break; From b956c111d8a32863587854c86de597562e44de3a Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 17 Mar 2017 13:20:48 -0700 Subject: [PATCH 076/366] Revert D4716024: [yoga] Avoid transfering cached layout information to java Differential Revision: D4716024 fbshipit-source-id: 7276b4bbf072aa444c5ae9fd1a3d62ea87a0cec1 --- .../src/main/jni/first-party/yogajni/jni/YGJNI.cpp | 6 ------ ReactCommon/yoga/yoga/Yoga.c | 13 ------------- ReactCommon/yoga/yoga/Yoga.h | 2 -- 3 files changed, 21 deletions(-) diff --git a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp index c0b0dd49505ec6..72e704c8c82e81 100644 --- a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp +++ b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp @@ -24,12 +24,6 @@ static void YGTransferLayoutDirection(YGNodeRef node, alias_ref javaNod } static void YGTransferLayoutOutputsRecursive(YGNodeRef root) { - // If the node is using a cached layout it means we have already - // transfered it to java. - if (YGNodeIsUsingCachedLayout(root)) { - return; - } - if (auto obj = YGNodeJobject(root)->lockLocal()) { static auto widthField = obj->getClass()->getField("mWidth"); static auto heightField = obj->getClass()->getField("mHeight"); diff --git a/ReactCommon/yoga/yoga/Yoga.c b/ReactCommon/yoga/yoga/Yoga.c index 1b3961c9c16804..65a3ba46f98707 100644 --- a/ReactCommon/yoga/yoga/Yoga.c +++ b/ReactCommon/yoga/yoga/Yoga.c @@ -459,18 +459,6 @@ void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) { } } -static int YGNodeRootGenerationCount(const YGNodeRef node) { - if (node->parent) { - return YGNodeRootGenerationCount(node->parent); - } else { - return node->layout.generationCount; - } -} - -bool YGNodeIsUsingCachedLayout(const YGNodeRef node) { - return node->layout.generationCount != YGNodeRootGenerationCount(node); -} - static inline float YGResolveFlexGrow(const YGNodeRef node) { if (!YGFloatIsUndefined(node->style.flexGrow)) { return node->style.flexGrow; @@ -1823,7 +1811,6 @@ static void YGZeroOutLayoutRecursivly(const YGNodeRef node) { node->layout.cachedLayout.widthMeasureMode = YGMeasureModeExactly; node->layout.cachedLayout.computedWidth = 0; node->layout.cachedLayout.computedHeight = 0; - node->layout.generationCount = gCurrentGenerationCount; const uint32_t childCount = YGNodeGetChildCount(node); for (uint32_t i = 0; i < childCount; i++) { const YGNodeRef child = YGNodeListGet(node->children, i); diff --git a/ReactCommon/yoga/yoga/Yoga.h b/ReactCommon/yoga/yoga/Yoga.h index 054b5dc5079a0e..d5c5258a7dca15 100644 --- a/ReactCommon/yoga/yoga/Yoga.h +++ b/ReactCommon/yoga/yoga/Yoga.h @@ -218,8 +218,6 @@ YG_NODE_LAYOUT_EDGE_PROPERTY(float, Margin); YG_NODE_LAYOUT_EDGE_PROPERTY(float, Border); YG_NODE_LAYOUT_EDGE_PROPERTY(float, Padding); -bool YGNodeIsUsingCachedLayout(const YGNodeRef node); - WIN_EXPORT void YGSetLogger(YGLogger logger); WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...); From e69a80f7f9c330bb0dfa3f72181f503b3982a714 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 17 Mar 2017 13:21:10 -0700 Subject: [PATCH 077/366] Revert D4713847: [rn] Optimize ReactShadowNode by more manually converting between string and enums Differential Revision: D4713847 fbshipit-source-id: c1a60ad133ec010556c0b4c6e5fd39cdc0eab337 --- .../react/uimanager/LayoutShadowNode.java | 301 ++---------------- 1 file changed, 26 insertions(+), 275 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java index e57c9f6158ecb4..3f5020e3ad4157 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java @@ -4,9 +4,9 @@ import javax.annotation.Nullable; +import java.util.Locale; import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableType; import com.facebook.yoga.YogaAlign; @@ -246,34 +246,9 @@ public void setFlexDirection(@Nullable String flexDirection) { if (isVirtual()) { return; } - - if (flexDirection == null) { - setFlexDirection(YogaFlexDirection.COLUMN); - return; - } - - switch (flexDirection) { - case "column": { - setFlexDirection(YogaFlexDirection.COLUMN); - break; - } - case "column-reverse": { - setFlexDirection(YogaFlexDirection.COLUMN_REVERSE); - break; - } - case "row": { - setFlexDirection(YogaFlexDirection.ROW); - break; - } - case "row-reverse": { - setFlexDirection(YogaFlexDirection.ROW_REVERSE); - break; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for flexDirection: " + flexDirection); - } - } + setFlexDirection( + flexDirection == null ? YogaFlexDirection.COLUMN : YogaFlexDirection.valueOf( + flexDirection.toUpperCase(Locale.US).replace("-", "_"))); } @ReactProp(name = ViewProps.FLEX_WRAP) @@ -281,25 +256,12 @@ public void setFlexWrap(@Nullable String flexWrap) { if (isVirtual()) { return; } - - if (flexWrap == null) { + if (flexWrap == null || flexWrap.equals("nowrap")) { setFlexWrap(YogaWrap.NO_WRAP); - return; - } - - switch (flexWrap) { - case "nowrap": { - setFlexWrap(YogaWrap.NO_WRAP); - break; - } - case "wrap": { - setFlexWrap(YogaWrap.WRAP); - break; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for flexWrap: " + flexWrap); - } + } else if (flexWrap.equals("wrap")) { + setFlexWrap(YogaWrap.WRAP); + } else { + throw new IllegalArgumentException("Unknown flexWrap value: " + flexWrap); } } @@ -308,50 +270,8 @@ public void setAlignSelf(@Nullable String alignSelf) { if (isVirtual()) { return; } - - if (alignSelf == null) { - setAlignSelf(YogaAlign.AUTO); - return; - } - - switch (alignSelf) { - case "auto": { - setAlignSelf(YogaAlign.FLEX_START); - return; - } - case "flex-start": { - setAlignSelf(YogaAlign.CENTER); - return; - } - case "center": { - setAlignSelf(YogaAlign.CENTER); - return; - } - case "flex-end": { - setAlignSelf(YogaAlign.FLEX_END); - return; - } - case "stretch": { - setAlignSelf(YogaAlign.STRETCH); - return; - } - case "baseline": { - setAlignSelf(YogaAlign.BASELINE); - return; - } - case "space-between": { - setAlignSelf(YogaAlign.SPACE_BETWEEN); - return; - } - case "space-around": { - setAlignSelf(YogaAlign.SPACE_AROUND); - return; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for alignSelf: " + alignSelf); - } - } + setAlignSelf(alignSelf == null ? YogaAlign.AUTO : YogaAlign.valueOf( + alignSelf.toUpperCase(Locale.US).replace("-", "_"))); } @ReactProp(name = ViewProps.ALIGN_ITEMS) @@ -359,50 +279,9 @@ public void setAlignItems(@Nullable String alignItems) { if (isVirtual()) { return; } - - if (alignItems == null) { - setAlignItems(YogaAlign.STRETCH); - return; - } - - switch (alignItems) { - case "auto": { - setAlignItems(YogaAlign.FLEX_START); - return; - } - case "flex-start": { - setAlignItems(YogaAlign.CENTER); - return; - } - case "center": { - setAlignItems(YogaAlign.CENTER); - return; - } - case "flex-end": { - setAlignItems(YogaAlign.FLEX_END); - return; - } - case "stretch": { - setAlignItems(YogaAlign.STRETCH); - return; - } - case "baseline": { - setAlignItems(YogaAlign.BASELINE); - return; - } - case "space-between": { - setAlignItems(YogaAlign.SPACE_BETWEEN); - return; - } - case "space-around": { - setAlignItems(YogaAlign.SPACE_AROUND); - return; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for alignItems: " + alignItems); - } - } + setAlignItems( + alignItems == null ? YogaAlign.STRETCH : YogaAlign.valueOf( + alignItems.toUpperCase(Locale.US).replace("-", "_"))); } @ReactProp(name = ViewProps.ALIGN_CONTENT) @@ -410,50 +289,9 @@ public void setAlignContent(@Nullable String alignContent) { if (isVirtual()) { return; } - - if (alignContent == null) { - setAlignContent(YogaAlign.FLEX_START); - return; - } - - switch (alignContent) { - case "auto": { - setAlignContent(YogaAlign.FLEX_START); - return; - } - case "flex-start": { - setAlignContent(YogaAlign.CENTER); - return; - } - case "center": { - setAlignContent(YogaAlign.CENTER); - return; - } - case "flex-end": { - setAlignContent(YogaAlign.FLEX_END); - return; - } - case "stretch": { - setAlignContent(YogaAlign.STRETCH); - return; - } - case "baseline": { - setAlignContent(YogaAlign.BASELINE); - return; - } - case "space-between": { - setAlignContent(YogaAlign.SPACE_BETWEEN); - return; - } - case "space-around": { - setAlignContent(YogaAlign.SPACE_AROUND); - return; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for alignContent: " + alignContent); - } - } + setAlignContent( + alignContent == null ? YogaAlign.FLEX_START : YogaAlign.valueOf( + alignContent.toUpperCase(Locale.US).replace("-", "_"))); } @ReactProp(name = ViewProps.JUSTIFY_CONTENT) @@ -461,38 +299,8 @@ public void setJustifyContent(@Nullable String justifyContent) { if (isVirtual()) { return; } - - if (justifyContent == null) { - setJustifyContent(YogaJustify.FLEX_START); - return; - } - - switch (justifyContent) { - case "flex-start": { - setJustifyContent(YogaJustify.FLEX_START); - break; - } - case "center": { - setJustifyContent(YogaJustify.CENTER); - break; - } - case "flex-end": { - setJustifyContent(YogaJustify.FLEX_END); - break; - } - case "space-between": { - setJustifyContent(YogaJustify.SPACE_BETWEEN); - break; - } - case "space-around": { - setJustifyContent(YogaJustify.SPACE_AROUND); - break; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for justifyContent: " + justifyContent); - } - } + setJustifyContent(justifyContent == null ? YogaJustify.FLEX_START : YogaJustify.valueOf( + justifyContent.toUpperCase(Locale.US).replace("-", "_"))); } @ReactProp(name = ViewProps.OVERFLOW) @@ -500,30 +308,8 @@ public void setOverflow(@Nullable String overflow) { if (isVirtual()) { return; } - - if (overflow == null) { - setOverflow(YogaOverflow.VISIBLE); - return; - } - - switch (overflow) { - case "visible": { - setOverflow(YogaOverflow.VISIBLE); - break; - } - case "hidden": { - setOverflow(YogaOverflow.HIDDEN); - break; - } - case "scroll": { - setOverflow(YogaOverflow.SCROLL); - break; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for overflow: " + overflow); - } - } + setOverflow(overflow == null ? YogaOverflow.VISIBLE : YogaOverflow.valueOf( + overflow.toUpperCase(Locale.US).replace("-", "_"))); } @ReactProp(name = ViewProps.DISPLAY) @@ -531,26 +317,8 @@ public void setDisplay(@Nullable String display) { if (isVirtual()) { return; } - - if (display == null) { - setDisplay(YogaDisplay.FLEX); - return; - } - - switch (display) { - case "flex": { - setDisplay(YogaDisplay.FLEX); - break; - } - case "none": { - setDisplay(YogaDisplay.NONE); - break; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for display: " + display); - } - } + setDisplay(display == null ? YogaDisplay.FLEX : YogaDisplay.valueOf( + display.toUpperCase(Locale.US).replace("-", "_"))); } @ReactPropGroup(names = { @@ -656,26 +424,9 @@ public void setPosition(@Nullable String position) { if (isVirtual()) { return; } - - if (position == null) { - setPositionType(YogaPositionType.RELATIVE); - return; - } - - switch (position) { - case "relative": { - setPositionType(YogaPositionType.RELATIVE); - break; - } - case "absolute": { - setPositionType(YogaPositionType.ABSOLUTE); - break; - } - default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for position: " + position); - } - } + YogaPositionType positionType = position == null ? + YogaPositionType.RELATIVE : YogaPositionType.valueOf(position.toUpperCase(Locale.US)); + setPositionType(positionType); } @Override From 238fd4ad190e294068cab341d57ef8800fa70e55 Mon Sep 17 00:00:00 2001 From: "Andrew Y. Chen" Date: Fri, 17 Mar 2017 16:30:27 -0700 Subject: [PATCH 078/366] Move idle detection classes to its own directory Reviewed By: AaaChiuuu Differential Revision: D4676282 fbshipit-source-id: 1b07e66ba638d4293eec65cb4e336f21dfb78218 --- .../java/com/facebook/react/testing/BUCK | 6 +- .../ReactAppInstrumentationTestCase.java | 2 +- .../react/testing/ReactAppTestActivity.java | 2 + .../react/testing/ReactIdleDetectionUtil.java | 2 +- .../testing/ReactIntegrationTestCase.java | 2 + .../testing/SingleTouchGestureGenerator.java | 2 + .../facebook/react/testing/idledetection/BUCK | 14 ++ .../{ => idledetection}/IdleWaiter.java | 2 +- .../ReactBridgeIdleSignaler.java | 2 +- .../idledetection/ReactIdleDetectionUtil.java | 125 ++++++++++++++++++ .../java/com/facebook/react/tests/BUCK | 1 + 11 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{ => idledetection}/IdleWaiter.java (90%) rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{ => idledetection}/ReactBridgeIdleSignaler.java (97%) create mode 100644 ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK index 5cf6affe9a6fab..6814e3aa316138 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK @@ -2,7 +2,10 @@ include_defs("//ReactAndroid/DEFS") android_library( name = "testing", - srcs = glob(["**/*.java"]), + srcs = glob( + ["**/*.java"], + excludes = ["idledetection/**/*.java"], + ), visibility = [ "PUBLIC", ], @@ -25,5 +28,6 @@ android_library( react_native_target("java/com/facebook/react/modules/debug:interfaces"), react_native_target("java/com/facebook/react/shell:shell"), react_native_target("java/com/facebook/react/uimanager:uimanager"), + react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), ], ) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java index aaba651def949c..eeb8c02e06715d 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java @@ -20,6 +20,7 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.testing.idledetection.IdleWaiter; /** * Base class for instrumentation tests that runs React based react application in UI mode @@ -123,7 +124,6 @@ public void run() { } }; - getActivity().runOnUiThread(getScreenshotRunnable); try { if (!latch.await(5000, TimeUnit.MILLISECONDS)) { diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java index 022e7826c1fe83..11edda17a429b0 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java @@ -28,6 +28,8 @@ import com.facebook.react.common.LifecycleState; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.shell.MainReactPackage; +import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; +import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.react.uimanager.UIImplementationProvider; public class ReactAppTestActivity extends FragmentActivity implements diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java index 49e2219327b96f..af6ca2ebb82a55 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing; +package com.facebook.react.testing.idledetection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java index 752c89ea618161..77f5492c36cafd 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java @@ -32,6 +32,8 @@ import com.facebook.react.common.futures.SimpleSettableFuture; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.modules.core.Timing; +import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; +import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.soloader.SoLoader; import static org.mockito.Mockito.mock; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java index 278facbcb280b4..f0ae877a953d42 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java @@ -13,6 +13,8 @@ import android.view.View; import android.view.ViewConfiguration; +import com.facebook.react.testing.idledetection.IdleWaiter; + /** * Provides methods for generating touch events and dispatching them directly to a given view. * Events scenarios are based on {@link android.test.TouchUtils} but they get gets dispatched diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK new file mode 100644 index 00000000000000..f6b33535737d3f --- /dev/null +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK @@ -0,0 +1,14 @@ +include_defs("//ReactAndroid/DEFS") + +android_library( + name = "idledetection", + srcs = glob(["**/*.java"]), + visibility = [ + "PUBLIC", + ], + deps = [ + react_native_dep("third-party/java/testing-support-lib:runner"), + react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/modules/core:core"), + ], +) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java similarity index 90% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java index 98884c1033a375..1b94b7c1fc88db 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing; +package com.facebook.react.testing.idledetection; /** * Interface for something that knows how to wait for bridge and UI idle. diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java similarity index 97% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java index ffd941f9a228e6..4aaa451e43ab24 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing; +package com.facebook.react.testing.idledetection; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java new file mode 100644 index 00000000000000..af6ca2ebb82a55 --- /dev/null +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2014-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. + */ + +package com.facebook.react.testing.idledetection; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import android.app.Instrumentation; +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; + +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.modules.core.ChoreographerCompat; + +public class ReactIdleDetectionUtil { + + /** + * Waits for both the UI thread and bridge to be idle. It determines this by waiting for the + * bridge to become idle, then waiting for the UI thread to become idle, then checking if the + * bridge is idle again (if the bridge was idle before and is still idle after running the UI + * thread to idle, then there are no more events to process in either place). + *

+ * Also waits for any Choreographer callbacks to run after the initial sync since things like UI + * events are initiated from Choreographer callbacks. + */ + public static void waitForBridgeAndUIIdle( + ReactBridgeIdleSignaler idleSignaler, + final ReactContext reactContext, + long timeoutMs) { + UiThreadUtil.assertNotOnUiThread(); + + long startTime = SystemClock.uptimeMillis(); + waitInner(idleSignaler, timeoutMs); + + long timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime)); + waitForChoreographer(timeToWait); + waitForJSIdle(reactContext); + + timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime)); + waitInner(idleSignaler, timeToWait); + timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime)); + waitForChoreographer(timeToWait); + } + + private static void waitForChoreographer(long timeToWait) { + final int waitFrameCount = 2; + final CountDownLatch latch = new CountDownLatch(1); + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + ChoreographerCompat.getInstance().postFrameCallback( + new ChoreographerCompat.FrameCallback() { + + private int frameCount = 0; + + @Override + public void doFrame(long frameTimeNanos) { + frameCount++; + if (frameCount == waitFrameCount) { + latch.countDown(); + } else { + ChoreographerCompat.getInstance().postFrameCallback(this); + } + } + }); + } + }); + try { + if (!latch.await(timeToWait, TimeUnit.MILLISECONDS)) { + throw new RuntimeException("Timed out waiting for Choreographer"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void waitForJSIdle(ReactContext reactContext) { + if (!reactContext.hasActiveCatalystInstance()) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + + reactContext.runOnJSQueueThread( + new Runnable() { + @Override + public void run() { + latch.countDown(); + } + }); + + try { + if (!latch.await(5000, TimeUnit.MILLISECONDS)) { + throw new RuntimeException("Timed out waiting for JS thread"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void waitInner(ReactBridgeIdleSignaler idleSignaler, long timeToWait) { + // TODO gets broken in gradle, do we need it? + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + long startTime = SystemClock.uptimeMillis(); + boolean bridgeWasIdle = false; + while (SystemClock.uptimeMillis() - startTime < timeToWait) { + boolean bridgeIsIdle = idleSignaler.isBridgeIdle(); + if (bridgeIsIdle && bridgeWasIdle) { + return; + } + bridgeWasIdle = bridgeIsIdle; + long newTimeToWait = Math.max(1, timeToWait - (SystemClock.uptimeMillis() - startTime)); + idleSignaler.waitForIdle(newTimeToWait); + instrumentation.waitForIdleSync(); + } + throw new RuntimeException("Timed out waiting for bridge and UI idle!"); + } +} diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 8188c87c6175c0..6e07e5fd24ae1f 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -5,6 +5,7 @@ deps = [ react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), react_native_integration_tests_target("java/com/facebook/react/testing:testing"), + react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), From 95c192619372e6b3dfdc55aeafa8f44faf013ecf Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Fri, 17 Mar 2017 16:47:51 -0700 Subject: [PATCH 079/366] Introduce `DeviceInfo` as a new native module Summary: The `UIManager` already has a lot of responsibilities and is deeply tied with React Native's view architecture. This diff separates out a `DeviceInfo` native module to provide information about screen dimensions and font scale, etc. Reviewed By: fkgozali Differential Revision: D4713834 fbshipit-source-id: f2ee93acf876a4221c29a8c731f5abeffbb97974 --- Libraries/Utilities/DeviceInfo.js | 20 +++ Libraries/Utilities/Dimensions.js | 4 +- .../react-native-implementation.js | 1 + React/Modules/RCTDeviceInfo.h | 17 +++ React/Modules/RCTDeviceInfo.m | 116 ++++++++++++++++++ React/Modules/RCTUIManager.m | 57 --------- React/React.xcodeproj/project.pbxproj | 12 ++ React/ReactCxx.xcodeproj/project.pbxproj | 12 ++ .../java/com/facebook/react/tests/BUCK | 1 + ...alystNativeJSToJavaParametersTestCase.java | 2 + ...talystNativeJavaToJSArgumentsTestCase.java | 2 + ...ystNativeJavaToJSReturnValuesTestCase.java | 2 + .../tests/CatalystUIManagerTestCase.java | 2 + .../facebook/react/tests/JSLocaleTest.java | 2 + .../react/tests/ProgressBarTestCase.java | 2 + .../react/tests/ViewRenderingTestCase.java | 2 + .../src/main/java/com/facebook/react/BUCK | 1 + .../facebook/react/CoreModulesPackage.java | 9 ++ .../com/facebook/react/ReactRootView.java | 4 +- .../facebook/react/modules/deviceinfo/BUCK | 17 +++ .../modules/deviceinfo/DeviceInfoModule.java | 105 ++++++++++++++++ .../react/uimanager/UIManagerModule.java | 26 +--- .../uimanager/UIManagerModuleConstants.java | 31 +---- .../UIManagerModuleConstantsHelper.java | 5 +- jest/setup.js | 18 +-- 25 files changed, 345 insertions(+), 125 deletions(-) create mode 100644 Libraries/Utilities/DeviceInfo.js create mode 100644 React/Modules/RCTDeviceInfo.h create mode 100644 React/Modules/RCTDeviceInfo.m create mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK create mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java diff --git a/Libraries/Utilities/DeviceInfo.js b/Libraries/Utilities/DeviceInfo.js new file mode 100644 index 00000000000000..d398f808312459 --- /dev/null +++ b/Libraries/Utilities/DeviceInfo.js @@ -0,0 +1,20 @@ +/** + * 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. + * + * @providesModule DeviceInfo + * @flow + */ +'use strict'; + +const DeviceInfo = require('NativeModules').DeviceInfo; + +const invariant = require('invariant'); + +invariant(DeviceInfo, 'DeviceInfo native module is not installed correctly'); + +module.exports = DeviceInfo; diff --git a/Libraries/Utilities/Dimensions.js b/Libraries/Utilities/Dimensions.js index 2204ba4fcc331a..76b4098b8589cd 100644 --- a/Libraries/Utilities/Dimensions.js +++ b/Libraries/Utilities/Dimensions.js @@ -11,9 +11,9 @@ */ 'use strict'; +var DeviceInfo = require('DeviceInfo'); var EventEmitter = require('EventEmitter'); var Platform = require('Platform'); -var UIManager = require('UIManager'); var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); var invariant = require('fbjs/lib/invariant'); @@ -128,7 +128,7 @@ class Dimensions { } } -Dimensions.set(UIManager.Dimensions); +Dimensions.set(DeviceInfo.Dimensions); RCTDeviceEventEmitter.addListener('didUpdateDimensions', function(update) { Dimensions.set(update); }); diff --git a/Libraries/react-native/react-native-implementation.js b/Libraries/react-native/react-native-implementation.js index d11a202215d013..3eff5dedd60ebe 100644 --- a/Libraries/react-native/react-native-implementation.js +++ b/Libraries/react-native/react-native-implementation.js @@ -85,6 +85,7 @@ const ReactNative = { get CameraRoll() { return require('CameraRoll'); }, get Clipboard() { return require('Clipboard'); }, get DatePickerAndroid() { return require('DatePickerAndroid'); }, + get DeviceInfo() { return require('DeviceInfo'); }, get Dimensions() { return require('Dimensions'); }, get Easing() { return require('Easing'); }, get I18nManager() { return require('I18nManager'); }, diff --git a/React/Modules/RCTDeviceInfo.h b/React/Modules/RCTDeviceInfo.h new file mode 100644 index 00000000000000..2ff2ad965d5a08 --- /dev/null +++ b/React/Modules/RCTDeviceInfo.h @@ -0,0 +1,17 @@ +/** + * 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. + */ + +#import + +#import +#import + +@interface RCTDeviceInfo : NSObject + +@end diff --git a/React/Modules/RCTDeviceInfo.m b/React/Modules/RCTDeviceInfo.m new file mode 100644 index 00000000000000..ef4b71a79ee411 --- /dev/null +++ b/React/Modules/RCTDeviceInfo.m @@ -0,0 +1,116 @@ +/** + * 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. + */ + +#import "RCTDeviceInfo.h" + +#import "RCTAccessibilityManager.h" +#import "RCTAssert.h" +#import "RCTEventDispatcher.h" +#import "RCTUtils.h" + +@implementation RCTDeviceInfo { +#if !TARGET_OS_TV + UIInterfaceOrientation _currentInterfaceOrientation; +#endif +} + +@synthesize bridge = _bridge; + +RCT_EXPORT_MODULE() + +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + +- (void)setBridge:(RCTBridge *)bridge +{ + _bridge = bridge; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didReceiveNewContentSizeMultiplier) + name:RCTAccessibilityManagerDidUpdateMultiplierNotification + object:_bridge.accessibilityManager]; +#if !TARGET_OS_TV + _currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(interfaceOrientationDidChange) + name:UIApplicationDidChangeStatusBarOrientationNotification + object:nil]; +#endif +} + +static NSDictionary *RCTExportedDimensions(RCTBridge *bridge) +{ + RCTAssertMainQueue(); + + // Don't use RCTScreenSize since it the interface orientation doesn't apply to it + CGRect screenSize = [[UIScreen mainScreen] bounds]; + NSDictionary *dims = @{ + @"width": @(screenSize.size.width), + @"height": @(screenSize.size.height), + @"scale": @(RCTScreenScale()), + @"fontScale": @(bridge.accessibilityManager.multiplier) + }; + return @{ + @"window": dims, + @"screen": dims + }; +} + +- (void)invalidate +{ + dispatch_async(dispatch_get_main_queue(), ^{ + self->_bridge = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + }); +} + +- (NSDictionary *)constantsToExport +{ + NSMutableDictionary *constants = [NSMutableDictionary new]; + constants[@"Dimensions"] = RCTExportedDimensions(_bridge); + return constants; +} + +- (void)didReceiveNewContentSizeMultiplier +{ + // Report the event across the bridge. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateDimensions" + body:RCTExportedDimensions(_bridge)]; +#pragma clang diagnostic pop +} + + +- (void)interfaceOrientationDidChange +{ +#if !TARGET_OS_TV + UIInterfaceOrientation nextOrientation = [RCTSharedApplication() statusBarOrientation]; + + // Update when we go from portrait to landscape, or landscape to portrait + if ((UIInterfaceOrientationIsPortrait(_currentInterfaceOrientation) && + !UIInterfaceOrientationIsPortrait(nextOrientation)) || + (UIInterfaceOrientationIsLandscape(_currentInterfaceOrientation) && + !UIInterfaceOrientationIsLandscape(nextOrientation))) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateDimensions" + body:RCTExportedDimensions(_bridge)]; +#pragma clang diagnostic pop + } + + _currentInterfaceOrientation = nextOrientation; +#endif +} + + +@end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index b672cafbe1046b..6a1576236f01d9 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -225,9 +225,6 @@ @implementation RCTUIManager NSDictionary *_componentDataByName; NSMutableSet> *_bridgeTransactionListeners; -#if !TARGET_OS_TV - UIInterfaceOrientation _currentInterfaceOrientation; -#endif } @synthesize bridge = _bridge; @@ -239,8 +236,6 @@ - (void)didReceiveNewContentSizeMultiplier // Report the event across the bridge. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateDimensions" - body:RCTExportedDimensions(_bridge)]; [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateContentSizeMultiplier" body:@([_bridge.accessibilityManager multiplier])]; #pragma clang diagnostic pop @@ -252,28 +247,6 @@ - (void)didReceiveNewContentSizeMultiplier }); } -- (void)interfaceOrientationDidChange -{ -#if !TARGET_OS_TV - UIInterfaceOrientation nextOrientation = - [RCTSharedApplication() statusBarOrientation]; - - // Update when we go from portrait to landscape, or landscape to portrait - if ((UIInterfaceOrientationIsPortrait(_currentInterfaceOrientation) && - !UIInterfaceOrientationIsPortrait(nextOrientation)) || - (UIInterfaceOrientationIsLandscape(_currentInterfaceOrientation) && - !UIInterfaceOrientationIsLandscape(nextOrientation))) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateDimensions" - body:RCTExportedDimensions(_bridge)]; -#pragma clang diagnostic pop - } - - _currentInterfaceOrientation = nextOrientation; -#endif -} - - (void)invalidate { /** @@ -351,13 +324,6 @@ - (void)setBridge:(RCTBridge *)bridge selector:@selector(didReceiveNewContentSizeMultiplier) name:RCTAccessibilityManagerDidUpdateMultiplierNotification object:_bridge.accessibilityManager]; -#if !TARGET_OS_TV - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(interfaceOrientationDidChange) - name:UIApplicationDidChangeStatusBarOrientationNotification - object:nil]; -#endif - [RCTAnimation initializeStatics]; } @@ -1544,35 +1510,12 @@ static void RCTMeasureLayout(RCTShadowView *view, constants[name] = moduleConstants; }]; -#if !TARGET_OS_TV - _currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation]; -#endif - constants[@"customBubblingEventTypes"] = bubblingEvents; constants[@"customDirectEventTypes"] = directEvents; - constants[@"Dimensions"] = RCTExportedDimensions(_bridge); return constants; } -static NSDictionary *RCTExportedDimensions(RCTBridge *bridge) -{ - RCTAssertMainQueue(); - - // Don't use RCTScreenSize since it the interface orientation doesn't apply to it - CGRect screenSize = [[UIScreen mainScreen] bounds]; - NSDictionary *dims = @{ - @"width": @(screenSize.size.width), - @"height": @(screenSize.size.height), - @"scale": @(RCTScreenScale()), - @"fontScale": @(bridge.accessibilityManager.multiplier) - }; - return @{ - @"window": dims, - @"screen": dims - }; -} - RCT_EXPORT_METHOD(configureNextLayoutAnimation:(NSDictionary *)config withCallback:(RCTResponseSenderBlock)callback errorCallback:(__unused RCTResponseSenderBlock)errorCallback) diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 1e2d1be93f3a55..a79946fbfa4b40 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -738,6 +738,10 @@ B50558421E43E14000F71A00 /* RCTDevSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = B505583D1E43DFB900F71A00 /* RCTDevSettings.mm */; }; B50558431E43E64600F71A00 /* RCTDevSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = B505583C1E43DFB900F71A00 /* RCTDevSettings.h */; }; B95154321D1B34B200FE7B80 /* RCTActivityIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */; }; + CF85BC321E79EC6B00F1EF3B /* RCTDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CF85BC301E79EC6B00F1EF3B /* RCTDeviceInfo.h */; }; + CF85BC331E79EC6B00F1EF3B /* RCTDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CF85BC311E79EC6B00F1EF3B /* RCTDeviceInfo.m */; }; + CF85BC341E79EC7A00F1EF3B /* RCTDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CF85BC301E79EC6B00F1EF3B /* RCTDeviceInfo.h */; }; + CF85BC351E79EC7D00F1EF3B /* RCTDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CF85BC311E79EC6B00F1EF3B /* RCTDeviceInfo.m */; }; E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; /* End PBXBuildFile section */ @@ -1381,6 +1385,8 @@ B505583D1E43DFB900F71A00 /* RCTDevSettings.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTDevSettings.mm; sourceTree = ""; }; B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTActivityIndicatorView.h; sourceTree = ""; }; B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActivityIndicatorView.m; sourceTree = ""; }; + CF85BC301E79EC6B00F1EF3B /* RCTDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDeviceInfo.h; sourceTree = ""; }; + CF85BC311E79EC6B00F1EF3B /* RCTDeviceInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDeviceInfo.m; sourceTree = ""; }; E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTextDecorationLineType.h; sourceTree = ""; }; E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAccessibilityManager.h; sourceTree = ""; }; E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAccessibilityManager.m; sourceTree = ""; }; @@ -1456,6 +1462,8 @@ B505583B1E43DFB900F71A00 /* RCTDevMenu.m */, B505583C1E43DFB900F71A00 /* RCTDevSettings.h */, B505583D1E43DFB900F71A00 /* RCTDevSettings.mm */, + CF85BC301E79EC6B00F1EF3B /* RCTDeviceInfo.h */, + CF85BC311E79EC6B00F1EF3B /* RCTDeviceInfo.m */, 13D9FEE91CDCCECF00158BD7 /* RCTEventEmitter.h */, 13D9FEEA1CDCCECF00158BD7 /* RCTEventEmitter.m */, 13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */, @@ -1869,6 +1877,7 @@ 3D302F3E1DF828F800D6DDAE /* RCTKeyCommands.h in Headers */, 3D302F3F1DF828F800D6DDAE /* RCTLog.h in Headers */, 3D302F401DF828F800D6DDAE /* RCTModuleData.h in Headers */, + CF85BC341E79EC7A00F1EF3B /* RCTDeviceInfo.h in Headers */, 3D302F411DF828F800D6DDAE /* RCTModuleMethod.h in Headers */, 3D302F421DF828F800D6DDAE /* RCTMultipartDataTask.h in Headers */, 3D302F431DF828F800D6DDAE /* RCTMultipartStreamReader.h in Headers */, @@ -2055,6 +2064,7 @@ A12E9E2A1E5DEB860029001B /* RCTReconnectingWebSocket.h in Headers */, 3D80DA311DF820620028D040 /* RCTJavaScriptLoader.h in Headers */, 3D80DA321DF820620028D040 /* RCTJSStackFrame.h in Headers */, + CF85BC321E79EC6B00F1EF3B /* RCTDeviceInfo.h in Headers */, 3D80DA331DF820620028D040 /* RCTKeyCommands.h in Headers */, 3D80DA341DF820620028D040 /* RCTLog.h in Headers */, 3D80DA351DF820620028D040 /* RCTModuleData.h in Headers */, @@ -2465,6 +2475,7 @@ 2D3B5EC21D9B093B00451313 /* RCTProfile.m in Sources */, 2D3B5ECB1D9B096200451313 /* RCTConvert+CoreLocation.m in Sources */, A12E9E261E5DEB510029001B /* RCTPackagerClientResponder.m in Sources */, + CF85BC351E79EC7D00F1EF3B /* RCTDeviceInfo.m in Sources */, 2D3B5EEE1D9B09DA00451313 /* RCTView.m in Sources */, 594AD5D01E46D87500B07237 /* RCTScrollContentShadowView.m in Sources */, 2D3B5E981D9B089500451313 /* RCTConvert.m in Sources */, @@ -2675,6 +2686,7 @@ 945929C41DD62ADD00653A7D /* RCTConvert+Transform.m in Sources */, 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */, 13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */, + CF85BC331E79EC6B00F1EF3B /* RCTDeviceInfo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/React/ReactCxx.xcodeproj/project.pbxproj b/React/ReactCxx.xcodeproj/project.pbxproj index 1d9ac80584e181..cef7f0d506bb2b 100644 --- a/React/ReactCxx.xcodeproj/project.pbxproj +++ b/React/ReactCxx.xcodeproj/project.pbxproj @@ -980,6 +980,10 @@ AC70D2E91DE489E4002E6351 /* RCTJavaScriptLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */; }; B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */; }; B95154321D1B34B200FE7B80 /* RCTActivityIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */; }; + CF2731C01E7B8DE40044CA4F /* RCTDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CF2731BE1E7B8DE40044CA4F /* RCTDeviceInfo.h */; }; + CF2731C11E7B8DE40044CA4F /* RCTDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */; }; + CF2731C21E7B8DEF0044CA4F /* RCTDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CF2731BE1E7B8DE40044CA4F /* RCTDeviceInfo.h */; }; + CF2731C31E7B8DF30044CA4F /* RCTDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */; }; E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; /* End PBXBuildFile section */ @@ -1828,6 +1832,8 @@ B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTI18nManager.m; sourceTree = ""; }; B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTActivityIndicatorView.h; sourceTree = ""; }; B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActivityIndicatorView.m; sourceTree = ""; }; + CF2731BE1E7B8DE40044CA4F /* RCTDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDeviceInfo.h; sourceTree = ""; }; + CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDeviceInfo.m; sourceTree = ""; }; E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTextDecorationLineType.h; sourceTree = ""; }; E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAccessibilityManager.h; sourceTree = ""; }; E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAccessibilityManager.m; sourceTree = ""; }; @@ -2034,6 +2040,8 @@ 13B07FE01A69315300A75B9A /* Modules */ = { isa = PBXGroup; children = ( + CF2731BE1E7B8DE40044CA4F /* RCTDeviceInfo.h */, + CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */, 130E3D861E6A082100ACE484 /* RCTDevSettings.h */, 130E3D871E6A082100ACE484 /* RCTDevSettings.mm */, 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */, @@ -2595,6 +2603,7 @@ 3D302F881DF828F800D6DDAE /* RCTRootShadowView.h in Headers */, 3D302F891DF828F800D6DDAE /* RCTScrollableProtocol.h in Headers */, 3D302F8A1DF828F800D6DDAE /* RCTScrollView.h in Headers */, + CF2731C21E7B8DEF0044CA4F /* RCTDeviceInfo.h in Headers */, 3D302F8B1DF828F800D6DDAE /* RCTScrollViewManager.h in Headers */, 3D302F8C1DF828F800D6DDAE /* RCTSegmentedControl.h in Headers */, 3D302F8D1DF828F800D6DDAE /* RCTSegmentedControlManager.h in Headers */, @@ -2837,6 +2846,7 @@ 3D80DA5E1DF820620028D040 /* RCTProfile.h in Headers */, 3D80DA5F1DF820620028D040 /* RCTActivityIndicatorView.h in Headers */, 3D80DA601DF820620028D040 /* RCTActivityIndicatorViewManager.h in Headers */, + CF2731C01E7B8DE40044CA4F /* RCTDeviceInfo.h in Headers */, 3D80DA611DF820620028D040 /* RCTAnimationType.h in Headers */, 3D80DA621DF820620028D040 /* RCTAutoInsetsProtocol.h in Headers */, 3D80DA631DF820620028D040 /* RCTBorderDrawing.h in Headers */, @@ -3314,6 +3324,7 @@ 2D3B5EA51D9B08C700451313 /* RCTRootView.m in Sources */, 2D3B5EAC1D9B08EF00451313 /* RCTJSCExecutor.mm in Sources */, 13134C871E296B2A00B9F3CB /* RCTCxxBridge.mm in Sources */, + CF2731C31E7B8DF30044CA4F /* RCTDeviceInfo.m in Sources */, 2D3B5EB11D9B090100451313 /* RCTAppState.m in Sources */, 2D3B5EC21D9B093B00451313 /* RCTProfile.m in Sources */, 13134C931E296B2A00B9F3CB /* RCTNativeModule.mm in Sources */, @@ -3524,6 +3535,7 @@ 14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */, 13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */, 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */, + CF2731C11E7B8DE40044CA4F /* RCTDeviceInfo.m in Sources */, 3D7AA9C41E548CD5001955CF /* NSDataBigString.mm in Sources */, 13D033631C1837FE0021DC29 /* RCTClipboard.m in Sources */, 14C2CA741B3AC64300E6CBB2 /* RCTModuleData.mm in Sources */, diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 6e07e5fd24ae1f..992351f62026aa 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -15,6 +15,7 @@ deps = [ react_native_target("java/com/facebook/react/modules/datepicker:datepicker"), react_native_target("java/com/facebook/react/modules/share:share"), react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"), + react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"), react_native_target("java/com/facebook/react/modules/timepicker:timepicker"), react_native_target("java/com/facebook/react/touch:touch"), react_native_target("java/com/facebook/react/uimanager:uimanager"), diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJSToJavaParametersTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJSToJavaParametersTestCase.java index ec2dcd5a82d876..7c2da8c8803fe1 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJSToJavaParametersTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJSToJavaParametersTestCase.java @@ -31,6 +31,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.modules.systeminfo.AndroidInfoModule; import com.facebook.react.testing.FakeWebSocketModule; import com.facebook.react.testing.ReactIntegrationTestCase; @@ -101,6 +102,7 @@ public void run() { mCatalystInstance = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(mRecordingTestModule) .addNativeModule(new AndroidInfoModule()) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addNativeModule(mUIManager) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSArgumentsTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSArgumentsTestCase.java index 8d21e7f4291ed0..60b951ca5dcd90 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSArgumentsTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSArgumentsTestCase.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.testing.AssertModule; import com.facebook.react.testing.FakeWebSocketModule; import com.facebook.react.testing.ReactIntegrationTestCase; @@ -78,6 +79,7 @@ public void run() { mInstance = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(mAssertModule) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addJSModule(TestJavaToJSArgumentsModule.class) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSReturnValuesTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSReturnValuesTestCase.java index 236a51a0f3fc37..8dcc2d0d69fb17 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSReturnValuesTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystNativeJavaToJSReturnValuesTestCase.java @@ -19,6 +19,7 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.testing.AssertModule; import com.facebook.react.testing.FakeWebSocketModule; @@ -119,6 +120,7 @@ protected void setUp() throws Exception { mInstance = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(mAssertModule) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addJSModule(TestJavaToJSReturnValuesModule.class) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystUIManagerTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystUIManagerTestCase.java index 6f09e69af41c88..3bcecc6eca748b 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystUIManagerTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/CatalystUIManagerTestCase.java @@ -22,6 +22,7 @@ import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.modules.systeminfo.AndroidInfoModule; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.UIImplementation; @@ -96,6 +97,7 @@ public void run() { jsModule = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(uiManager) .addNativeModule(new AndroidInfoModule()) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addJSModule(UIManagerTestModule.class) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSLocaleTest.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSLocaleTest.java index 67ccb8997d35da..4c524e020de001 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSLocaleTest.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSLocaleTest.java @@ -19,6 +19,7 @@ import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.uimanager.UIImplementationProvider; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewManager; @@ -62,6 +63,7 @@ public void run() { mInstance = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(mStringRecordingModule) .addNativeModule(mUIManager) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addJSModule(TestJSLocaleModule.class) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ProgressBarTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ProgressBarTestCase.java index 9865ce343e0db5..40983432aedfd3 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ProgressBarTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ProgressBarTestCase.java @@ -25,6 +25,7 @@ import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.modules.systeminfo.AndroidInfoModule; import com.facebook.react.uimanager.UIImplementation; import com.facebook.react.uimanager.UIImplementationProvider; @@ -87,6 +88,7 @@ public void run() { mInstance = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(mUIManager) .addNativeModule(new AndroidInfoModule()) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addJSModule(ProgressBarTestModule.class) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ViewRenderingTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ViewRenderingTestCase.java index 4c84c71816cc6e..956269d9683771 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ViewRenderingTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ViewRenderingTestCase.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.modules.appstate.AppStateModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.modules.systeminfo.AndroidInfoModule; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.UIImplementation; @@ -68,6 +69,7 @@ public void run() { mCatalystInstance = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(uiManager) .addNativeModule(new AndroidInfoModule()) + .addNativeModule(new DeviceInfoModule(getContext())) .addNativeModule(new AppStateModule(getContext())) .addNativeModule(new FakeWebSocketModule()) .addJSModule(ViewRenderingTestModule.class) diff --git a/ReactAndroid/src/main/java/com/facebook/react/BUCK b/ReactAndroid/src/main/java/com/facebook/react/BUCK index 7d1d9342225aba..2982e603c15b76 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/BUCK @@ -19,6 +19,7 @@ DEPS = [ react_native_target("java/com/facebook/react/modules/core:core"), react_native_target("java/com/facebook/react/modules/debug:debug"), react_native_target("java/com/facebook/react/modules/debug:interfaces"), + react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"), react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"), react_native_target("java/com/facebook/react/modules/toast:toast"), react_native_target("java/com/facebook/react/uimanager:uimanager"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java b/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java index 2827c47203bb8c..88e53489065cda 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java @@ -36,6 +36,7 @@ import com.facebook.react.modules.core.Timing; import com.facebook.react.modules.debug.AnimationsDebugModule; import com.facebook.react.modules.debug.SourceCodeModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.modules.systeminfo.AndroidInfoModule; import com.facebook.react.modules.appregistry.AppRegistry; import com.facebook.react.uimanager.UIImplementationProvider; @@ -65,6 +66,7 @@ SourceCodeModule.class, Timing.class, UIManagerModule.class, + DeviceInfoModule.class, // Debug only DebugComponentOwnershipModule.class, JSCHeapCapture.class, @@ -151,6 +153,13 @@ public NativeModule get() { return createUIManager(reactContext); } })); + moduleSpecList.add( + new ModuleSpec(DeviceInfoModule.class, new Provider() { + @Override + public NativeModule get() { + return new DeviceInfoModule(reactContext); + } + })); if (ReactBuildConfig.DEBUG) { moduleSpecList.add( diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 46ad7c6c47f658..8babff13d1d269 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -15,7 +15,6 @@ import android.graphics.Rect; import android.os.Bundle; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.Surface; import android.view.View; @@ -32,6 +31,7 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.JSTouchDispatcher; import com.facebook.react.uimanager.PixelUtil; @@ -396,7 +396,7 @@ private void emitOrientationChanged(final int newRotation) { private void emitUpdateDimensionsEvent() { mReactInstanceManager .getCurrentReactContext() - .getNativeModule(UIManagerModule.class) + .getNativeModule(DeviceInfoModule.class) .emitUpdateDimensionsEvent(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK new file mode 100644 index 00000000000000..14f601585c2cb7 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK @@ -0,0 +1,17 @@ +include_defs("//ReactAndroid/DEFS") + +android_library( + name = "deviceinfo", + srcs = glob(["**/*.java"]), + visibility = [ + "PUBLIC", + ], + deps = [ + react_native_dep("third-party/java/jsr-305:jsr-305"), + react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/module/annotations:annotations"), + react_native_target("java/com/facebook/react/modules/core:core"), + react_native_target("java/com/facebook/react/uimanager:uimanager"), + ], +) diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java new file mode 100644 index 00000000000000..c7b5324145225b --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java @@ -0,0 +1,105 @@ +/** + * 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. + */ + +package com.facebook.react.modules.deviceinfo; + +import javax.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import android.util.DisplayMetrics; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.uimanager.DisplayMetricsHolder; + +/** + * Module that exposes Android Constants to JS. + */ +@ReactModule(name = "DeviceInfo") +public class DeviceInfoModule extends ReactContextBaseJavaModule implements + LifecycleEventListener { + + private float mFontScale; + + public DeviceInfoModule( + ReactApplicationContext reactContext) { + super(reactContext); + + mFontScale = getReactApplicationContext().getResources().getConfiguration().fontScale; + } + + @Override + public String getName() { + return "DeviceInfo"; + } + + @Override + public @Nullable Map getConstants() { + HashMap constants = new HashMap<>(); + constants.put( + "Dimensions", + getDimensionsConstants()); + return constants; + } + + @Override + public void onHostResume() { + float fontScale = getReactApplicationContext().getResources().getConfiguration().fontScale; + if (mFontScale != fontScale) { + mFontScale = fontScale; + emitUpdateDimensionsEvent(); + } + } + + @Override + public void onHostPause() { + } + + @Override + public void onHostDestroy() { + } + + public void emitUpdateDimensionsEvent() { + getReactApplicationContext() + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("didUpdateDimensions", getDimensionsConstants()); + } + + private WritableMap getDimensionsConstants() { + DisplayMetrics windowDisplayMetrics = DisplayMetricsHolder.getWindowDisplayMetrics(); + DisplayMetrics screenDisplayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics(); + + WritableMap windowDisplayMetricsMap = Arguments.createMap(); + windowDisplayMetricsMap.putInt("width", windowDisplayMetrics.widthPixels); + windowDisplayMetricsMap.putInt("height", windowDisplayMetrics.heightPixels); + windowDisplayMetricsMap.putDouble("scale", windowDisplayMetrics.density); + windowDisplayMetricsMap.putDouble("fontScale", mFontScale); + windowDisplayMetricsMap.putDouble("densityDpi", windowDisplayMetrics.densityDpi); + + WritableMap screenDisplayMetricsMap = Arguments.createMap(); + screenDisplayMetricsMap.putInt("width", screenDisplayMetrics.widthPixels); + screenDisplayMetricsMap.putInt("height", screenDisplayMetrics.heightPixels); + screenDisplayMetricsMap.putDouble("scale", screenDisplayMetrics.density); + screenDisplayMetricsMap.putDouble("fontScale", mFontScale); + screenDisplayMetricsMap.putDouble("densityDpi", screenDisplayMetrics.densityDpi); + + WritableMap dimensionsMap = Arguments.createMap(); + dimensionsMap.putMap("windowPhysicalPixels", windowDisplayMetricsMap); + dimensionsMap.putMap("screenPhysicalPixels", screenDisplayMetricsMap); + + return dimensionsMap; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 725554596559a1..bde687d5216930 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -87,7 +87,6 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements private final Map mModuleConstants; private final UIImplementation mUIImplementation; private final MemoryTrimCallback mMemoryTrimCallback = new MemoryTrimCallback(); - private float mFontScale; private int mNextRootViewTag = 1; private int mBatchId = 0; @@ -100,8 +99,7 @@ public UIManagerModule( super(reactContext); DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext); mEventDispatcher = new EventDispatcher(reactContext); - mFontScale = getReactApplicationContext().getResources().getConfiguration().fontScale; - mModuleConstants = createConstants(viewManagerList, lazyViewManagersEnabled, mFontScale); + mModuleConstants = createConstants(viewManagerList, lazyViewManagersEnabled); mUIImplementation = uiImplementationProvider .createUIImplementation(reactContext, viewManagerList, mEventDispatcher); @@ -134,12 +132,6 @@ public void initialize() { @Override public void onHostResume() { mUIImplementation.onHostResume(); - - float fontScale = getReactApplicationContext().getResources().getConfiguration().fontScale; - if (mFontScale != fontScale) { - mFontScale = fontScale; - emitUpdateDimensionsEvent(); - } } @Override @@ -163,15 +155,13 @@ public void onCatalystInstanceDestroy() { private static Map createConstants( List viewManagerList, - boolean lazyViewManagersEnabled, - float fontScale) { + boolean lazyViewManagersEnabled) { ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_START); Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CreateUIManagerConstants"); try { return UIManagerModuleConstantsHelper.createConstants( viewManagerList, - lazyViewManagersEnabled, - fontScale); + lazyViewManagersEnabled); } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_END); @@ -550,16 +540,6 @@ public void sendAccessibilityEvent(int tag, int eventType) { mUIImplementation.sendAccessibilityEvent(tag, eventType); } - public void emitUpdateDimensionsEvent() { - sendEvent("didUpdateDimensions", UIManagerModuleConstants.getDimensionsConstants(mFontScale)); - } - - private void sendEvent(String eventName, @Nullable WritableMap params) { - getReactApplicationContext() - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(eventName, params); - } - /** * Schedule a block to be executed on the UI thread. Useful if you need to execute * view logic after all currently queued view updates have completed. diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java index 7dc5602ae4b7c7..9dd93ac82f1e2f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java @@ -83,7 +83,7 @@ .build(); } - public static Map getConstants(float fontScale) { + public static Map getConstants() { HashMap constants = new HashMap(); constants.put( "UIView", @@ -97,10 +97,6 @@ public static Map getConstants(float fontScale) { "ScaleAspectCenter", ImageView.ScaleType.CENTER_INSIDE.ordinal()))); - constants.put( - "Dimensions", - getDimensionsConstants(fontScale)); - constants.put( "StyleConstants", MapBuilder.of( @@ -133,29 +129,4 @@ public static Map getConstants(float fontScale) { return constants; } - - public static WritableMap getDimensionsConstants(float fontScale) { - DisplayMetrics windowDisplayMetrics = DisplayMetricsHolder.getWindowDisplayMetrics(); - DisplayMetrics screenDisplayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics(); - - WritableMap windowDisplayMetricsMap = Arguments.createMap(); - windowDisplayMetricsMap.putInt("width", windowDisplayMetrics.widthPixels); - windowDisplayMetricsMap.putInt("height", windowDisplayMetrics.heightPixels); - windowDisplayMetricsMap.putDouble("scale", windowDisplayMetrics.density); - windowDisplayMetricsMap.putDouble("fontScale", fontScale); - windowDisplayMetricsMap.putDouble("densityDpi", windowDisplayMetrics.densityDpi); - - WritableMap screenDisplayMetricsMap = Arguments.createMap(); - screenDisplayMetricsMap.putInt("width", screenDisplayMetrics.widthPixels); - screenDisplayMetricsMap.putInt("height", screenDisplayMetrics.heightPixels); - screenDisplayMetricsMap.putDouble("scale", screenDisplayMetrics.density); - screenDisplayMetricsMap.putDouble("fontScale", fontScale); - screenDisplayMetricsMap.putDouble("densityDpi", screenDisplayMetrics.densityDpi); - - WritableMap dimensionsMap = Arguments.createMap(); - dimensionsMap.putMap("windowPhysicalPixels", windowDisplayMetricsMap); - dimensionsMap.putMap("screenPhysicalPixels", screenDisplayMetricsMap); - - return dimensionsMap; - } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java index dbbd283d0e3943..825f81dfe3220e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java @@ -43,9 +43,8 @@ */ /* package */ static Map createConstants( List viewManagers, - boolean lazyViewManagersEnabled, - float fontScale) { - Map constants = UIManagerModuleConstants.getConstants(fontScale); + boolean lazyViewManagersEnabled) { + Map constants = UIManagerModuleConstants.getConstants(); Map bubblingEventTypesConstants = UIManagerModuleConstants.getBubblingEventTypeConstants(); Map directEventTypesConstants = UIManagerModuleConstants.getDirectEventTypeConstants(); diff --git a/jest/setup.js b/jest/setup.js index ee7ae562ae22a9..413d3b91a9260f 100644 --- a/jest/setup.js +++ b/jest/setup.js @@ -87,6 +87,16 @@ const mockNativeModules = { DataManager: { queryData: jest.fn(), }, + DeviceInfo: { + Dimensions: { + window: { + fontScale: 2, + height: 1334, + scale: 2, + width: 750, + }, + }, + }, FacebookSDK: { login: jest.fn(), logout: jest.fn(), @@ -150,14 +160,6 @@ const mockNativeModules = { replaceExistingNonRootView: jest.fn(), customBubblingEventTypes: {}, customDirectEventTypes: {}, - Dimensions: { - window: { - fontScale: 2, - height: 1334, - scale: 2, - width: 750, - }, - }, ModalFullscreenView: { Constants: {}, }, From 11814a5a81a1850a6c28c78252afa68be8348815 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Fri, 17 Mar 2017 16:47:56 -0700 Subject: [PATCH 080/366] Ensure `ResourceDrawableIdHelper` is thread-safe Reviewed By: jaegs Differential Revision: D4696625 fbshipit-source-id: e0aa7870ba02d8e6542c436d7f775bb251cf91ae --- .../imagehelper/ResourceDrawableIdHelper.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ResourceDrawableIdHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ResourceDrawableIdHelper.java index fc0c041f02cf64..dd2465156add5b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ResourceDrawableIdHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ResourceDrawableIdHelper.java @@ -10,6 +10,7 @@ package com.facebook.react.views.imagehelper; import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; import java.util.HashMap; import java.util.Map; @@ -21,12 +22,13 @@ /** * Helper class for obtaining information about local images. */ +@ThreadSafe public class ResourceDrawableIdHelper { private Map mResourceDrawableIdMap; private static final String LOCAL_RESOURCE_SCHEME = "res"; - private static ResourceDrawableIdHelper sResourceDrawableIdHelper; + private static volatile ResourceDrawableIdHelper sResourceDrawableIdHelper; private ResourceDrawableIdHelper() { mResourceDrawableIdMap = new HashMap(); @@ -34,12 +36,16 @@ private ResourceDrawableIdHelper() { public static ResourceDrawableIdHelper getInstance() { if (sResourceDrawableIdHelper == null) { - sResourceDrawableIdHelper = new ResourceDrawableIdHelper(); + synchronized (ResourceDrawableIdHelper.class) { + if (sResourceDrawableIdHelper == null) { + sResourceDrawableIdHelper = new ResourceDrawableIdHelper(); + } + } } return sResourceDrawableIdHelper; } - public void clear() { + public synchronized void clear() { mResourceDrawableIdMap.clear(); } @@ -48,15 +54,18 @@ public int getResourceDrawableId(Context context, @Nullable String name) { return 0; } name = name.toLowerCase().replace("-", "_"); - if (mResourceDrawableIdMap.containsKey(name)) { - return mResourceDrawableIdMap.get(name); - } - int id = context.getResources().getIdentifier( + + synchronized (this) { + if (mResourceDrawableIdMap.containsKey(name)) { + return mResourceDrawableIdMap.get(name); + } + int id = context.getResources().getIdentifier( name, "drawable", context.getPackageName()); - mResourceDrawableIdMap.put(name, id); - return id; + mResourceDrawableIdMap.put(name, id); + return id; + } } public @Nullable Drawable getResourceDrawable(Context context, @Nullable String name) { From f48b54bf6211dbbdd32839b0de68b1d0a451e486 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Fri, 17 Mar 2017 16:47:58 -0700 Subject: [PATCH 081/366] Expose RCTImageLocalAssetURL as a utility Reviewed By: javache Differential Revision: D4696627 fbshipit-source-id: 56d3e59983f524dfd5021835734b9b34203e20f2 --- Libraries/Image/RCTLocalAssetImageLoader.m | 51 +------------------- React/Base/RCTUtils.h | 4 ++ React/Base/RCTUtils.m | 54 ++++++++++++++++++++++ 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/Libraries/Image/RCTLocalAssetImageLoader.m b/Libraries/Image/RCTLocalAssetImageLoader.m index b1bf78c2ddaaaa..17acd15b88c29c 100644 --- a/Libraries/Image/RCTLocalAssetImageLoader.m +++ b/Libraries/Image/RCTLocalAssetImageLoader.m @@ -36,36 +36,6 @@ - (BOOL)shouldCacheLoadedImages return NO; } -static NSString *bundleName(NSBundle *bundle) -{ - NSString *name = bundle.infoDictionary[@"CFBundleName"]; - if (!name) { - name = [[bundle.bundlePath lastPathComponent] stringByDeletingPathExtension]; - } - return name; -} - -static NSBundle *bundleForPath(NSString *key) -{ - static NSMutableDictionary *bundleCache; - if (!bundleCache) { - bundleCache = [NSMutableDictionary new]; - bundleCache[@"main"] = [NSBundle mainBundle]; - - // Initialize every bundle in the array - for (NSString *path in [[NSBundle mainBundle] pathsForResourcesOfType:@"bundle" inDirectory:nil]) { - [NSBundle bundleWithPath:path]; - } - - // The bundles initialized above will now also be in `allBundles` - for (NSBundle *bundle in [NSBundle allBundles]) { - bundleCache[bundleName(bundle)] = bundle; - } - } - - return bundleCache[key]; -} - - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale @@ -80,31 +50,14 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL return; } - NSString *imageName = RCTBundlePathForURL(imageURL); - - NSBundle *bundle; - NSArray *imagePathComponents = [imageName pathComponents]; - if ([imagePathComponents count] > 1 && - [[[imagePathComponents firstObject] pathExtension] isEqualToString:@"bundle"]) { - NSString *bundlePath = [imagePathComponents firstObject]; - bundle = bundleForPath([bundlePath stringByDeletingPathExtension]); - imageName = [imageName substringFromIndex:(bundlePath.length + 1)]; - } - - UIImage *image; - if (bundle) { - image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; - } else { - image = [UIImage imageNamed:imageName]; - } - + UIImage *image = RCTImageFromLocalAssetURL(imageURL); if (image) { if (progressHandler) { progressHandler(1, 1); } completionHandler(nil, image); } else { - NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName]; + NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL]; RCTLogWarn(@"%@", message); completionHandler(RCTErrorWithMessage(message), nil); } diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h index a277120e243acb..14e169ba22a9e3 100644 --- a/React/Base/RCTUtils.h +++ b/React/Base/RCTUtils.h @@ -114,6 +114,10 @@ RCT_EXTERN NSString *__nullable RCTBundlePathForURL(NSURL *__nullable URL); // Determines if a given image URL refers to a local image RCT_EXTERN BOOL RCTIsLocalAssetURL(NSURL *__nullable imageURL); +// Returns an UIImage for a local image asset. Returns nil if the URL +// does not correspond to a local asset. +RCT_EXTERN UIImage *RCTImageFromLocalAssetURL(NSURL *imageURL); + // Creates a new, unique temporary file path with the specified extension RCT_EXTERN NSString *__nullable RCTTempFilePath(NSString *__nullable extension, NSError **error); diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 9617d0a3afb9db..80ff69ed538d49 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -599,6 +599,60 @@ BOOL RCTIsLocalAssetURL(NSURL *__nullable imageURL) return [extension isEqualToString:@"png"] || [extension isEqualToString:@"jpg"]; } +static NSString *bundleName(NSBundle *bundle) +{ + NSString *name = bundle.infoDictionary[@"CFBundleName"]; + if (!name) { + name = [[bundle.bundlePath lastPathComponent] stringByDeletingPathExtension]; + } + return name; +} + +static NSBundle *bundleForPath(NSString *key) +{ + static NSMutableDictionary *bundleCache; + if (!bundleCache) { + bundleCache = [NSMutableDictionary new]; + bundleCache[@"main"] = [NSBundle mainBundle]; + + // Initialize every bundle in the array + for (NSString *path in [[NSBundle mainBundle] pathsForResourcesOfType:@"bundle" inDirectory:nil]) { + [NSBundle bundleWithPath:path]; + } + + // The bundles initialized above will now also be in `allBundles` + for (NSBundle *bundle in [NSBundle allBundles]) { + bundleCache[bundleName(bundle)] = bundle; + } + } + + return bundleCache[key]; +} + +UIImage *RCTImageFromLocalAssetURL(NSURL *imageURL) +{ + if (!RCTIsLocalAssetURL(imageURL)) { + return nil; + } + + NSString *imageName = RCTBundlePathForURL(imageURL); + + NSBundle *bundle; + NSArray *imagePathComponents = [imageName pathComponents]; + if ([imagePathComponents count] > 1 && + [[[imagePathComponents firstObject] pathExtension] isEqualToString:@"bundle"]) { + NSString *bundlePath = [imagePathComponents firstObject]; + bundle = bundleForPath([bundlePath stringByDeletingPathExtension]); + imageName = [imageName substringFromIndex:(bundlePath.length + 1)]; + } + + if (bundle) { + return [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; + } else { + return [UIImage imageNamed:imageName]; + } +} + RCT_EXTERN NSString *__nullable RCTTempFilePath(NSString *extension, NSError **error) { static NSError *setupError = nil; From 9c3e6ae9f0f246e5fd8cdc3599801c37d1c11690 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 17 Mar 2017 22:02:04 -0700 Subject: [PATCH 082/366] Fix Animated.event attach/detach on component re-render Summary: Need to make sure `detach` happens on the old `scrollableNode` before it's unmounted and that `attach` happens on the new `scrollableNode` after it's mounted. This should also be more performant because the detach step no longer requires iterating through all the props, most of which are not animated, and we filter out unneeded updates if props or ref haven't changed. = Test Plan = Hook up native onscroll events in `FlatListExample` and toggle "debug" - before, the events would stop working because they would try to attach to the old unmounted node, but with this diff it keeps working as expected. Reviewed By: fkgozali Differential Revision: D4687186 fbshipit-source-id: 313a7964d4614190308490a51fc4f56abb6690f8 --- .../Animated/src/AnimatedImplementation.js | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index 769b3b0164a64e..09c3c2129452a6 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -1510,9 +1510,9 @@ class AnimatedStyle extends AnimatedWithChildren { // Recursively get values for nested styles (like iOS's shadowOffset) __walkStyleAndGetValues(style) { - let updatedStyle = {}; - for (let key in style) { - let value = style[key]; + const updatedStyle = {}; + for (const key in style) { + const value = style[key]; if (value instanceof Animated) { if (!value.__isNative) { // We cannot use value of natively driven nodes this way as the value we have access from @@ -1535,9 +1535,9 @@ class AnimatedStyle extends AnimatedWithChildren { // Recursively get animated values for nested styles (like iOS's shadowOffset) __walkStyleAndGetAnimatedValues(style) { - let updatedStyle = {}; - for (let key in style) { - let value = style[key]; + const updatedStyle = {}; + for (const key in style) { + const value = style[key]; if (value instanceof Animated) { updatedStyle[key] = value.__getAnimatedValue(); } else if (value && !Array.isArray(value) && typeof value === 'object') { @@ -1690,7 +1690,9 @@ class AnimatedProps extends Animated { } setNativeView(animatedView: any): void { - invariant(this._animatedView === undefined, 'Animated view already set.'); + if (this._animatedView === animatedView) { + return; + } this._animatedView = animatedView; if (this.__isNative) { this.__connectAnimatedView(); @@ -1729,7 +1731,9 @@ class AnimatedProps extends Animated { function createAnimatedComponent(Component: any): any { class AnimatedComponent extends React.Component { _component: any; + _prevComponent: any; _propsAnimated: AnimatedProps; + _eventDetachers: Array = []; _setComponentRef: Function; constructor(props: Object) { @@ -1739,7 +1743,7 @@ function createAnimatedComponent(Component: any): any { componentWillUnmount() { this._propsAnimated && this._propsAnimated.__detach(); - this._detachNativeEvents(this.props); + this._detachNativeEvents(); } setNativeProps(props) { @@ -1752,42 +1756,28 @@ function createAnimatedComponent(Component: any): any { componentDidMount() { this._propsAnimated.setNativeView(this._component); - - this._attachNativeEvents(this.props); + this._attachNativeEvents(); } - _attachNativeEvents(newProps) { - if (newProps !== this.props) { - this._detachNativeEvents(this.props); - } - + _attachNativeEvents() { // Make sure to get the scrollable node for components that implement // `ScrollResponder.Mixin`. - const ref = this._component.getScrollableNode ? + const scrollableNode = this._component.getScrollableNode ? this._component.getScrollableNode() : this._component; - for (const key in newProps) { - const prop = newProps[key]; + for (const key in this.props) { + const prop = this.props[key]; if (prop instanceof AnimatedEvent && prop.__isNative) { - prop.__attach(ref, key); + prop.__attach(scrollableNode, key); + this._eventDetachers.push(() => prop.__detach(scrollableNode, key)); } } } - _detachNativeEvents(props) { - // Make sure to get the scrollable node for components that implement - // `ScrollResponder.Mixin`. - const ref = this._component.getScrollableNode ? - this._component.getScrollableNode() : - this._component; - - for (const key in props) { - const prop = props[key]; - if (prop instanceof AnimatedEvent && prop.__isNative) { - prop.__detach(ref, key); - } - } + _detachNativeEvents() { + this._eventDetachers.forEach(remove => remove()); + this._eventDetachers = []; } _attachProps(nextProps) { @@ -1820,10 +1810,6 @@ function createAnimatedComponent(Component: any): any { callback, ); - if (this._component) { - this._propsAnimated.setNativeView(this._component); - } - // When you call detach, it removes the element from the parent list // of children. If it goes to 0, then the parent also detaches itself // and so on. @@ -1835,9 +1821,18 @@ function createAnimatedComponent(Component: any): any { oldPropsAnimated && oldPropsAnimated.__detach(); } - componentWillReceiveProps(nextProps) { - this._attachProps(nextProps); - this._attachNativeEvents(nextProps); + componentWillReceiveProps(newProps) { + this._attachProps(newProps); + } + + componentDidUpdate(prevProps) { + if (this._component !== this._prevComponent) { + this._propsAnimated.setNativeView(this._component); + } + if (this._component !== this._prevComponent || prevProps !== this.props) { + this._detachNativeEvents(); + this._attachNativeEvents(); + } } render() { @@ -1850,6 +1845,7 @@ function createAnimatedComponent(Component: any): any { } _setComponentRef(c) { + this._prevComponent = this._component; this._component = c; } From 09b8ef41e1ab937b660d513fe7728334272b3f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Sat, 18 Mar 2017 12:21:50 -0700 Subject: [PATCH 083/366] Fix issue with whitespace in path to source and stabilise CI. Summary: * Fixes issue where headers could not be found due to whitespace in an unquoted header search path. https://github.com/facebook/react-native/issues/11781#issuecomment-287176373 * Stabilises CI but not needing to download the source for Yoga, but use the existing cloned repo instead. /cc mkonicek Closes https://github.com/facebook/react-native/pull/13007 Differential Revision: D4735347 fbshipit-source-id: 933aefcb0e65537d2e759d25f4e3b81cdf4b8cb5 --- React.podspec | 4 ++-- ReactCommon/yoga/Yoga.podspec | 2 +- scripts/process-podspecs.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/React.podspec b/React.podspec index e6352b3fd821d2..dcc10543f45e61 100644 --- a/React.podspec +++ b/React.podspec @@ -62,7 +62,7 @@ Pod::Spec.new do |s| s.subspec "jschelpers" do |ss| ss.source_files = "ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.{cpp,h}", "ReactCommon/jschelpers/systemJSCWrapper.cpp" ss.private_header_files = "ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.h" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "$(PODS_TARGET_SRCROOT)/ReactCommon" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" } ss.framework = "JavaScriptCore" end @@ -70,7 +70,7 @@ Pod::Spec.new do |s| ss.dependency "React/jschelpers" ss.source_files = "ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.{cpp,h}" ss.private_header_files = "ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.h" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "$(PODS_TARGET_SRCROOT)/ReactCommon" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" } end s.subspec "ART" do |ss| diff --git a/ReactCommon/yoga/Yoga.podspec b/ReactCommon/yoga/Yoga.podspec index 1e9c75ff7721ed..6d053b34eee654 100644 --- a/ReactCommon/yoga/Yoga.podspec +++ b/ReactCommon/yoga/Yoga.podspec @@ -1,7 +1,7 @@ package = JSON.parse(File.read(File.expand_path('../../package.json', __dir__))) version = package['version'] -source = { :git => 'https://github.com/facebook/react-native.git' } +source = { :git => ENV['INSTALL_YOGA_FROM_LOCATION'] || 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. source[:commit] = `git rev-parse HEAD`.strip diff --git a/scripts/process-podspecs.sh b/scripts/process-podspecs.sh index d87fa4f719131b..e39f8c78b421c1 100755 --- a/scripts/process-podspecs.sh +++ b/scripts/process-podspecs.sh @@ -44,7 +44,7 @@ push() { local SPEC_DIR="$SPEC_REPO_DIR/$POD_NAME/$(version $SPEC_NAME)" local SPEC_PATH="$SPEC_DIR/$SPEC_NAME.json" mkdir -p $SPEC_DIR - env INSTALL_YOGA_WITHOUT_PATH_OPTION=1 pod ipc spec $SPEC_NAME > $SPEC_PATH + env INSTALL_YOGA_WITHOUT_PATH_OPTION=1 INSTALL_YOGA_FROM_LOCATION="$ROOT" pod ipc spec $SPEC_NAME > $SPEC_PATH } # Perform linting and publishing of podspec in cwd. From 06dd08316f449d63ccb67a788f4bc845c1e5c060 Mon Sep 17 00:00:00 2001 From: Kevin Cooper Date: Sat, 18 Mar 2017 12:48:19 -0700 Subject: [PATCH 084/366] Fix suggestion to "npm start -- --reset-cache" Summary: As discussed in https://github.com/facebook/react-native/pull/11983. The double dash is necessary to pass through the argument to node. Based on the comments [here](https://github.com/facebook/react-native/issues/1924#issuecomment-249861004), it looks like most people use the double dash; it's unclear whether it would do anything at all if the dashes were omitted. If anyone else has better insight, let me know! Closes https://github.com/facebook/react-native/pull/13003 Differential Revision: D4731566 Pulled By: hramos fbshipit-source-id: 62562536db7589a03a511762117cbf0e36d3aafb --- packager/src/node-haste/DependencyGraph/ResolutionRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/src/node-haste/DependencyGraph/ResolutionRequest.js b/packager/src/node-haste/DependencyGraph/ResolutionRequest.js index 46583d9643b529..0ffa7446373b32 100644 --- a/packager/src/node-haste/DependencyGraph/ResolutionRequest.js +++ b/packager/src/node-haste/DependencyGraph/ResolutionRequest.js @@ -391,7 +391,7 @@ class ResolutionRequest { `To resolve try the following:\n` + ` 1. Clear watchman watches: \`watchman watch-del-all\`.\n` + ` 2. Delete the \`node_modules\` folder: \`rm -rf node_modules && npm install\`.\n` + - ' 3. Reset packager cache: `rm -fr $TMPDIR/react-*` or `npm start --reset-cache`.' + ' 3. Reset packager cache: `rm -fr $TMPDIR/react-*` or `npm start -- --reset-cache`.' ); }); }); From 22da6f2f3f7bab852f05921d32d8425123e70b55 Mon Sep 17 00:00:00 2001 From: Arman Dezfuli-Arjomandi Date: Sat, 18 Mar 2017 12:56:24 -0700 Subject: [PATCH 085/366] Fix typo in FlatList docs Summary: Thanks for submitting a PR! Please read these instructions carefully: - [x] Explain the **motivation** for making this change. - [x] Provide a **test plan** demonstrating that the code is solid. - [x] Match the **code formatting** of the rest of the codebase. - [x] Target the `master` branch, NOT a "stable" branch. There was a typo in the FlatList docs for numColumns affecting the formatting of its description. N/A Sign the [CLA][2], if you haven't already. Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. Make sure all **tests pass** on both [Travis][3] and [Circle CI][4]. PRs that break tests are unlikely to be merged. For more info, see the ["Pull Requests"][5] section of our "Contributing" guidelines. [1]: https://medium.com/martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9 [2]: https://code.facebook.com/cla Closes https://github.com/facebook/react-native/pull/13002 Differential Revision: D4735343 fbshipit-source-id: f781c50c892d64e69f61aec980614e948a48f48b --- Libraries/CustomComponents/Lists/FlatList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Lists/FlatList.js b/Libraries/CustomComponents/Lists/FlatList.js index 5fedff5d612446..daf99d33c2fd51 100644 --- a/Libraries/CustomComponents/Lists/FlatList.js +++ b/Libraries/CustomComponents/Lists/FlatList.js @@ -103,7 +103,7 @@ type OptionalProps = { */ keyExtractor: (item: ItemT, index: number) => string, /** - * Multiple columns can only be rendered with `horizontal={false}`` and will zig-zag like a + * Multiple columns can only be rendered with `horizontal={false}` and will zig-zag like a * `flexWrap` layout. Items should all be the same height - masonry layouts are not supported. */ numColumns: number, From cbd46aaa7c95a42be682736f4be9f7b11201b3c5 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 18 Mar 2017 13:01:54 -0700 Subject: [PATCH 086/366] more accurate product-name regex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Some projects define multiple targets, including app extensions, which are built with a “.appex” extension. This fix prevents the buildProject method from selecting any app extension (e.g. a Today.appex today-widget extension) as the product name. Thanks for submitting a PR! Please read these instructions carefully: - [X] Explain the **motivation** for making this change. - [X] Provide a **test plan** demonstrating that the code is solid. - [X] Match the **code formatting** of the rest of the codebase. - [X] Target the `master` branch, NOT a "stable" branch. When building our workspace, ReactNative was failing to install the app to the simulator because it calculated an incorrect path to the app itself. It was attempting to install "Today.app" when it should have been installing "Remitly.app". I discovered that ReactNative parses the build output to identify the generated app name, and that this was broken when the build also generated an app extension. The f Closes https://github.com/facebook/react-native/pull/13001 Differential Revision: D4735360 fbshipit-source-id: afeeb2073ccd65c95916b153fcde574b5343af8c --- local-cli/runIOS/runIOS.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-cli/runIOS/runIOS.js b/local-cli/runIOS/runIOS.js index ea605530c8a30a..e890d643b1317c 100644 --- a/local-cli/runIOS/runIOS.js +++ b/local-cli/runIOS/runIOS.js @@ -157,7 +157,7 @@ function buildProject(xcodeProject, udid, scheme, configuration = 'Debug', launc }); buildProcess.on('close', function(code) { //FULL_PRODUCT_NAME is the actual file name of the app, which actually comes from the Product Name in the build config, which does not necessary match a scheme name, example output line: export FULL_PRODUCT_NAME="Super App Dev.app" - let productNameMatch = /export FULL_PRODUCT_NAME="?(.+).app/.exec(buildOutput); + let productNameMatch = /export FULL_PRODUCT_NAME="?(.+).app"?$/.exec(buildOutput); if (productNameMatch && productNameMatch.length && productNameMatch.length > 1) { return resolve(productNameMatch[1]);//0 is the full match, 1 is the app name } From 5328d952a8dd0945005d9f7305aba913f438ffa7 Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Sun, 19 Mar 2017 13:16:04 -0700 Subject: [PATCH 087/366] kill bridge only after cleaning up NativeModules Reviewed By: javache Differential Revision: D4734634 fbshipit-source-id: c2d425485679454397d18b1a0c389714c0e3c484 --- .../com/facebook/react/cxxbridge/CatalystInstanceImpl.java | 2 +- .../facebook/react/uimanager/events/EventDispatcher.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java index 80be1fbe51a559..6133b2576765dc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -292,11 +292,11 @@ public void destroy() { // TODO: tell all APIs to shut down mDestroyed = true; - mHybridData.resetNative(); mReactQueueConfiguration.getNativeModulesQueueThread().runOnQueue(new Runnable() { @Override public void run() { mJavaRegistry.notifyJSInstanceDestroy(); + mHybridData.resetNative(); } }); boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java index d00d14c38e6ccb..540714b2feefb0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java @@ -171,7 +171,12 @@ public void onHostDestroy() { } public void onCatalystInstanceDestroyed() { - stopFrameCallback(); + UiThreadUtil.runOnUiThread(new Runnable() { + @Override + public void run() { + stopFrameCallback(); + } + }); } private void stopFrameCallback() { From 47d2cfeac5addb24f8fcfbeeb2cb8ff11a604e7a Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Sun, 19 Mar 2017 13:59:41 -0700 Subject: [PATCH 088/366] Whitelist some stuff in React Reviewed By: mzlee Differential Revision: D4731968 fbshipit-source-id: 1d1ae12a50beef4cd024a467427ec3b6cd446f4c --- ReactCommon/cxxreact/BUCK | 1 + ReactCommon/microprofiler/BUCK | 1 + 2 files changed, 2 insertions(+) diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index 5b58dbd2dc2c63..8e94880e254625 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -93,6 +93,7 @@ cxx_library( ], force_static = True, header_namespace = "cxxreact", + labels = ["accounts_for_platform_and_build_mode_flags"], visibility = [ "PUBLIC", ], diff --git a/ReactCommon/microprofiler/BUCK b/ReactCommon/microprofiler/BUCK index adbc87ba12024f..d65e3f927d73e6 100644 --- a/ReactCommon/microprofiler/BUCK +++ b/ReactCommon/microprofiler/BUCK @@ -14,6 +14,7 @@ cxx_library( ], force_static = True, header_namespace = "microprofiler", + labels = ["accounts_for_platform_and_build_mode_flags"], visibility = [ "PUBLIC", ], From 23c2a6cf1ae297182a62d1a8143b55b186f5e1b7 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Sun, 19 Mar 2017 21:43:14 -0700 Subject: [PATCH 089/366] Removed harmful optimization in ReactNativeEventEmitter Reviewed By: spicyj Differential Revision: D4729779 fbshipit-source-id: 2dd5ec10d42df7f24804796c4100eca107edeedb --- .../Renderer/src/renderers/native/ReactNativeEventEmitter.js | 5 ----- .../src/renderers/shared/shared/event/EventPropagators.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Libraries/Renderer/src/renderers/native/ReactNativeEventEmitter.js b/Libraries/Renderer/src/renderers/native/ReactNativeEventEmitter.js index 1e1c97e308f6e7..b7b18f9ff0177b 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNativeEventEmitter.js +++ b/Libraries/Renderer/src/renderers/native/ReactNativeEventEmitter.js @@ -103,11 +103,6 @@ var ReactNativeEventEmitter = { ) { var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT; var inst = ReactNativeComponentTree.getInstanceFromNode(rootNodeID); - if (!inst) { - // If the original instance is already gone, we don't have to dispatch - // any events. - return; - } ReactGenericBatching.batchedUpdates(function() { ReactNativeEventEmitter.handleTopLevel( topLevelType, diff --git a/Libraries/Renderer/src/renderers/shared/shared/event/EventPropagators.js b/Libraries/Renderer/src/renderers/shared/shared/event/EventPropagators.js index 159d2e6c1992b0..c97f880939d16d 100644 --- a/Libraries/Renderer/src/renderers/shared/shared/event/EventPropagators.js +++ b/Libraries/Renderer/src/renderers/shared/shared/event/EventPropagators.js @@ -93,7 +93,7 @@ function accumulateTwoPhaseDispatchesSingleSkipTarget(event) { * requiring that the `dispatchMarker` be the same as the dispatched ID. */ function accumulateDispatches(inst, ignoredDirection, event) { - if (event && event.dispatchConfig.registrationName) { + if (inst && event && event.dispatchConfig.registrationName) { var registrationName = event.dispatchConfig.registrationName; var listener = getListener(inst, registrationName); if (listener) { From 64c327ae684690ae9cabaff412467b54095839ea Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Sun, 19 Mar 2017 21:48:22 -0700 Subject: [PATCH 090/366] Fixed issue where setting `zero scale` transfrom matrix to UIView brokes `hitTest` mechanism Summary: The Math Strikes Back Several related things: * When we specify `scale: 0;` style for some view it ends up with calling `CATransform3DScale` with zero scale parameter. * In this case `CATransform3DScale` returns transform matrix full of zeros. It actually depends on representation and matrix-type (2d or 3d) but in UIView debugger it appears as [0, 0, 0, 0, ...]. And probably it is correct result. * By default, for hit-testing, UIKit uses specially optimized logic based on GPU/CALayer infrastructure under the hood. And the transform matrix full of zeros breaks this algorithm. I guess, it happens because zero-matrix doesn't quite make sense. So, `scale: 0;` is a weird edge case, and in this diff, we are trying to illuminate it by replacing with epsilon value. Related SO issues: http://stackoverflow.com/questions/25964224/cgaffinetransformscale-not-working-with-zero-scale http://stackoverflow.com/questions/7937369/animate-uiview-scale-to-zero Reviewed By: blairvanderhoof Differential Revision: D4734475 fbshipit-source-id: 7241cdffa86c05a6552860a25789e2281588ba23 --- React/Views/RCTConvert+Transform.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/React/Views/RCTConvert+Transform.m b/React/Views/RCTConvert+Transform.m index 11639105626725..32a1abfb57c7e2 100644 --- a/React/Views/RCTConvert+Transform.m +++ b/React/Views/RCTConvert+Transform.m @@ -90,15 +90,15 @@ + (CATransform3D)CATransform3D:(id)json transform = CATransform3DRotate(transform, rotate, 0, 0, 1); } else if ([property isEqualToString:@"scale"]) { - CGFloat scale = [value floatValue]; + CGFloat scale = MAX([value floatValue], FLT_EPSILON); transform = CATransform3DScale(transform, scale, scale, 1); } else if ([property isEqualToString:@"scaleX"]) { - CGFloat scale = [value floatValue]; + CGFloat scale = MAX([value floatValue], FLT_EPSILON); transform = CATransform3DScale(transform, scale, 1, 1); } else if ([property isEqualToString:@"scaleY"]) { - CGFloat scale = [value floatValue]; + CGFloat scale = MAX([value floatValue], FLT_EPSILON); transform = CATransform3DScale(transform, 1, scale, 1); } else if ([property isEqualToString:@"translate"]) { From d2723341864b92e7ad44e39aad2436cb1c9a3394 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Sun, 19 Mar 2017 21:49:26 -0700 Subject: [PATCH 091/366] RCTRootView is now has empty autoresizing mask by default Summary: `autoresizingMask` is supposed to be set outside self class, this is UIKit convention. Reviewed By: mmmulani Differential Revision: D4697098 fbshipit-source-id: 7e0aa5d3032184de980b3cecafebbc4ce8ef9ada --- React/Base/RCTRootView.m | 1 - 1 file changed, 1 deletion(-) diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index db1ed02343c415..9870ebfaeee436 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -69,7 +69,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _loadingViewFadeDelay = 0.25; _loadingViewFadeDuration = 0.25; _sizeFlexibility = RCTRootViewSizeFlexibilityNone; - self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bridgeDidReload) From 3acafd1f3dd42cc27a592cdee0530e0dbf1c8afd Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 20 Mar 2017 00:00:17 -0700 Subject: [PATCH 092/366] Better TextInput: Removed redundant UIScrollView from RCTTextView Reviewed By: mmmulani Differential Revision: D4640207 fbshipit-source-id: 01fc65b0212ad6baef500625679dab5e99da9db5 --- Libraries/Text/RCTTextView.m | 65 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index 83cde446a19355..b640d7a3712133 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -58,6 +58,13 @@ - (void)didMoveToWindow } } +- (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated +{ + // Turning off scroll animation. + // This fixes the problem also known as "flaky scrolling". + [super setContentOffset:contentOffset animated:NO]; +} + @end @implementation RCTTextView @@ -69,7 +76,6 @@ @implementation RCTTextView UITextView *_textView; RCTText *_richTextView; NSAttributedString *_pendingAttributedText; - UIScrollView *_scrollView; UITextRange *_previousSelectionRange; NSUInteger _previousTextLength; @@ -100,17 +106,10 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher #if !TARGET_OS_TV _textView.scrollsToTop = NO; #endif - _textView.scrollEnabled = NO; + _textView.scrollEnabled = YES; _textView.delegate = self; - _scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero]; -#if !TARGET_OS_TV - _scrollView.scrollsToTop = NO; -#endif - _scrollView.delegate = self; - [_scrollView addSubview:_textView]; - - [self addSubview:_scrollView]; + [self addSubview:_textView]; } return self; } @@ -118,9 +117,12 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) +#pragma mark - RCTComponent + - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index { [super insertReactSubview:subview atIndex:index]; + if ([subview isKindOfClass:[RCTText class]]) { if (_richTextView) { RCTLogError(@"Tried to insert a second into - there can only be one."); @@ -143,11 +145,6 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index } } -- (void)dealloc -{ - _scrollView.delegate = nil; -} - - (void)removeReactSubview:(UIView *)subview { [super removeReactSubview:subview]; @@ -159,9 +156,11 @@ - (void)removeReactSubview:(UIView *)subview - (void)didUpdateReactSubviews { - // Do nothing, as we don't allow non-text subviews + // Do nothing, as we don't allow non-text subviews. } +#pragma mark - Routine + - (void)setMostRecentEventCount:(NSInteger)mostRecentEventCount { _mostRecentEventCount = mostRecentEventCount; @@ -262,7 +261,6 @@ - (void)updateFrames CGRect frame = UIEdgeInsetsInsetRect(self.bounds, adjustedFrameInset); _textView.frame = frame; _placeholderView.frame = frame; - _scrollView.frame = frame; [self updateContentSize]; _textView.textContainerInset = adjustedTextContainerInset; @@ -271,10 +269,8 @@ - (void)updateFrames - (void)updateContentSize { - CGSize size = (CGSize){_scrollView.frame.size.width, INFINITY}; - size.height = [_textView sizeThatFits:size].height; - _scrollView.contentSize = size; - _textView.frame = (CGRect){CGPointZero, size}; + CGSize size = _textView.frame.size; + size.height = [_textView sizeThatFits:CGSizeMake(size.width, INFINITY)].height; if (_viewDidCompleteInitialLayout && _onContentSizeChange && !CGSizeEqualToSize(_previousContentSize, size)) { _previousContentSize = size; @@ -725,26 +721,31 @@ - (UIColor *)defaultPlaceholderTextColor - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (_onScroll) { + CGPoint contentOffset = scrollView.contentOffset; + CGSize contentSize = scrollView.contentSize; + CGSize size = scrollView.bounds.size; + UIEdgeInsets contentInset = scrollView.contentInset; + _onScroll(@{ @"contentOffset": @{ - @"x": @(scrollView.contentOffset.x), - @"y": @(scrollView.contentOffset.y) + @"x": @(contentOffset.x), + @"y": @(contentOffset.y) }, @"contentInset": @{ - @"top": @(_scrollView.contentInset.top), - @"left": @(_scrollView.contentInset.left), - @"bottom": @(_scrollView.contentInset.bottom), - @"right": @(_scrollView.contentInset.right) + @"top": @(contentInset.top), + @"left": @(contentInset.left), + @"bottom": @(contentInset.bottom), + @"right": @(contentInset.right) }, @"contentSize": @{ - @"width": @(_scrollView.contentSize.width), - @"height": @(_scrollView.contentSize.height) + @"width": @(contentSize.width), + @"height": @(contentSize.height) }, @"layoutMeasurement": @{ - @"width": @(_scrollView.frame.size.width), - @"height": @(_scrollView.frame.size.height) + @"width": @(size.width), + @"height": @(size.height) }, - @"zoomScale": @(_scrollView.zoomScale ?: 1), + @"zoomScale": @(scrollView.zoomScale ?: 1), }); } } From 1b013cd30ce201cafb376782bdf427f601940220 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 20 Mar 2017 00:00:18 -0700 Subject: [PATCH 093/366] Better TextInput: Fixing multiline insets and prepare for auto-expanding feature Summary: Several things: * The mess with insets was fixed. Previously we tried to compensate the insets difference with `UITextField` by adjusting `textContainerInset` property, moreover we delegated negative part of this compensation to the view inset. That was terrible because it breaks `contentSize` computation, complicates whole insets consept, complicates everything; it just was not right. Now we are fixing the top and left inset differences in different places. We disable left and right 5pt margin by setting `_textView.textContainer.lineFragmentPadding = 0` and we introduce top 5px inset as a DEFAULT value for top inset for common multiline (this value can be easilly overwritten in Javascript). * Internal layout and contentSize computations were unified and simplified. * Now we report `intrinsicContentSize` value to Yoga, one step before auto-expandable TextInput. Depends on D4640207. Reviewed By: mmmulani Differential Revision: D4645921 fbshipit-source-id: da5988ebac50be967caecd71e780c014f6eb257a --- Libraries/Components/TextInput/TextInput.js | 7 + Libraries/Text/RCTTextView.h | 5 +- Libraries/Text/RCTTextView.m | 136 +++++++++++--------- Libraries/Text/RCTTextViewManager.m | 2 +- React/Modules/RCTUIManager.h | 3 +- 5 files changed, 88 insertions(+), 65 deletions(-) diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index d1c3e6fd27f723..c3c9eff2b4ae9b 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -678,6 +678,7 @@ const TextInput = React.createClass({ if (props.inputView) { children = [children, props.inputView]; } + props.style.unshift(styles.multilineInput); textContainer = #import -@class RCTEventDispatcher; +@class RCTBridge; @interface RCTTextView : RCTView @@ -28,6 +28,7 @@ @property (nonatomic, strong) UIFont *font; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, strong) NSNumber *maxLength; +@property (nonatomic, assign, readonly) CGSize contentSize; @property (nonatomic, copy) RCTDirectEventBlock onChange; @property (nonatomic, copy) RCTDirectEventBlock onContentSizeChange; @@ -35,7 +36,7 @@ @property (nonatomic, copy) RCTDirectEventBlock onTextInput; @property (nonatomic, copy) RCTDirectEventBlock onScroll; -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; - (void)performTextUpdate; diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index b640d7a3712133..b88f34bff0aad5 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -11,6 +11,7 @@ #import #import +#import #import #import @@ -69,6 +70,7 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated @implementation RCTTextView { + RCTBridge *_bridge; RCTEventDispatcher *_eventDispatcher; NSString *_placeholder; @@ -87,22 +89,25 @@ @implementation RCTTextView NSInteger _nativeEventCount; CGSize _previousContentSize; - BOOL _viewDidCompleteInitialLayout; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithBridge:(RCTBridge *)bridge { - RCTAssertParam(eventDispatcher); + RCTAssertParam(bridge); - if ((self = [super initWithFrame:CGRectZero])) { + if (self = [super initWithFrame:CGRectZero]) { _contentInset = UIEdgeInsetsZero; - _eventDispatcher = eventDispatcher; + _bridge = bridge; + _eventDispatcher = bridge.eventDispatcher; _placeholderTextColor = [self defaultPlaceholderTextColor]; _blurOnSubmit = NO; - _textView = [[RCTUITextView alloc] initWithFrame:CGRectZero]; + _textView = [[RCTUITextView alloc] initWithFrame:self.bounds]; + _textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _textView.backgroundColor = [UIColor clearColor]; _textView.textColor = [UIColor blackColor]; + // This line actually removes 5pt (default value) left and right padding in UITextView. + _textView.textContainer.lineFragmentPadding = 0; #if !TARGET_OS_TV _textView.scrollsToTop = NO; #endif @@ -132,7 +137,7 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index // If this is in rich text editing mode, and the child node providing rich text // styling has a backgroundColor, then the attributedText produced by the child node will have an // NSBackgroundColor attribute. We need to forward this attribute to the text view manually because the text view - // always has a clear background color in -initWithEventDispatcher:. + // always has a clear background color in `initWithBridge:`. // // TODO: This should be removed when the related hack in -performPendingTextUpdate is removed. if (subview.backgroundColor) { @@ -237,60 +242,20 @@ - (void)performPendingTextUpdate [_textView layoutIfNeeded]; [self updatePlaceholderVisibility]; - [self updateContentSize]; + [self invalidateContentSize]; _blockTextShouldChange = NO; } -- (void)updateFrames -{ - // Adjust the insets so that they are as close as possible to single-line - // RCTTextField defaults, using the system defaults of font size 17 and a - // height of 31 points. - // - // We apply the left inset to the frame since a negative left text-container - // inset mysteriously causes the text to be hidden until the text view is - // first focused. - UIEdgeInsets adjustedFrameInset = UIEdgeInsetsZero; - adjustedFrameInset.left = _contentInset.left - 5; - - UIEdgeInsets adjustedTextContainerInset = _contentInset; - adjustedTextContainerInset.top += 5; - adjustedTextContainerInset.left = 0; - - CGRect frame = UIEdgeInsetsInsetRect(self.bounds, adjustedFrameInset); - _textView.frame = frame; - _placeholderView.frame = frame; - [self updateContentSize]; - - _textView.textContainerInset = adjustedTextContainerInset; - _placeholderView.textContainerInset = adjustedTextContainerInset; -} - -- (void)updateContentSize -{ - CGSize size = _textView.frame.size; - size.height = [_textView sizeThatFits:CGSizeMake(size.width, INFINITY)].height; - - if (_viewDidCompleteInitialLayout && _onContentSizeChange && !CGSizeEqualToSize(_previousContentSize, size)) { - _previousContentSize = size; - _onContentSizeChange(@{ - @"contentSize": @{ - @"height": @(size.height), - @"width": @(size.width), - }, - @"target": self.reactTag, - }); - } -} - - (void)updatePlaceholder { [_placeholderView removeFromSuperview]; _placeholderView = nil; if (_placeholder) { - _placeholderView = [[UITextView alloc] initWithFrame:self.bounds]; + _placeholderView = [[UITextView alloc] initWithFrame:_textView.frame]; + _placeholderView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _placeholderView.textContainer.lineFragmentPadding = 0; _placeholderView.userInteractionEnabled = NO; _placeholderView.backgroundColor = [UIColor clearColor]; _placeholderView.scrollEnabled = NO; @@ -340,7 +305,9 @@ - (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor - (void)setContentInset:(UIEdgeInsets)contentInset { _contentInset = contentInset; - [self updateFrames]; + _textView.textContainerInset = contentInset; + _placeholderView.textContainerInset = contentInset; + [self setNeedsLayout]; } #pragma mark - UITextViewDelegate @@ -503,8 +470,7 @@ - (void)setText:(NSString *)text } [self updatePlaceholderVisibility]; - [self updateContentSize]; //keep the text wrapping when the length of - //the textline has been extended longer than the length of textinputView + [self invalidateContentSize]; } else if (eventLag > RCTTextUpdateLagWarningThreshold) { RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); } @@ -595,7 +561,7 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange, - (void)textViewDidChange:(UITextView *)textView { [self updatePlaceholderVisibility]; - [self updateContentSize]; + [self invalidateContentSize]; // Detect when textView updates happend that didn't invoke `shouldChangeTextInRange` // (e.g. typing simplified chinese in pinyin will insert and remove spaces without @@ -664,6 +630,8 @@ - (void)textViewDidEndEditing:(UITextView *)textView eventCount:_nativeEventCount]; } +#pragma mark - UIResponder + - (BOOL)isFirstResponder { return [_textView isFirstResponder]; @@ -695,17 +663,63 @@ - (BOOL)resignFirstResponder return [_textView resignFirstResponder]; } -- (void)layoutSubviews +#pragma mark - Content Size + +- (CGSize)contentSize { - [super layoutSubviews]; + // Returning value does NOT include insets. + CGSize contentSize = self.intrinsicContentSize; + contentSize.width -= _contentInset.left + _contentInset.right; + contentSize.height -= _contentInset.top + _contentInset.bottom; + return contentSize; +} + +- (void)invalidateContentSize +{ + CGSize contentSize = self.contentSize; + + if (CGSizeEqualToSize(_previousContentSize, contentSize)) { + return; + } + _previousContentSize = contentSize; + + [_bridge.uiManager setIntrinsicContentSize:contentSize forView:self]; + + if (_onContentSizeChange) { + _onContentSizeChange(@{ + @"contentSize": @{ + @"height": @(contentSize.height), + @"width": @(contentSize.width), + }, + @"target": self.reactTag, + }); + } +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize +{ + // Calling `sizeThatFits:` is probably more expensive method to compute + // content size compare to direct access `_textView.contentSize` property, + // but seems `sizeThatFits:` returns more reliable and consistent result. + // Returning value DOES include insets. + return [self sizeThatFits:CGSizeMake(self.bounds.size.width, INFINITY)]; +} - // Start sending content size updates only after the view has been laid out - // otherwise we send multiple events with bad dimensions on initial render. - _viewDidCompleteInitialLayout = YES; +- (CGSize)sizeThatFits:(CGSize)size +{ + return [_textView sizeThatFits:size]; +} - [self updateFrames]; +- (void)layoutSubviews +{ + [super layoutSubviews]; + [self invalidateContentSize]; } +#pragma mark - Default values + - (UIFont *)defaultPlaceholderFont { return [UIFont systemFontOfSize:17]; diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index 7b7f5d0473d422..ab26d1b8ec8858 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -29,7 +29,7 @@ - (RCTShadowView *)shadowView - (UIView *)view { - return [[RCTTextView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [[RCTTextView alloc] initWithBridge:self.bridge]; } RCT_REMAP_VIEW_PROPERTY(autoCapitalize, textView.autocapitalizationType, UITextAutocapitalizationType) diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index 14f866f754b69c..f1aa37b954aba4 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -87,7 +87,8 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey; /** * Set the natural size of a view, which is used when no explicit size is set. - * Use UIViewNoIntrinsicMetric to ignore a dimension. + * Use `UIViewNoIntrinsicMetric` to ignore a dimension. + * The `size` must NOT include padding and border. */ - (void)setIntrinsicContentSize:(CGSize)size forView:(UIView *)view; From 26e2c08544f3beac9926652f1b8f38ca7647df6e Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 20 Mar 2017 00:00:21 -0700 Subject: [PATCH 094/366] Better TextInput: Native auto-expandable is here (iOS only) Reviewed By: mmmulani Differential Revision: D4646962 fbshipit-source-id: bc054d9c68c385b13222e7c9fb8728d21f987a48 --- .../UIExplorer/js/TextInputExample.ios.js | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/Examples/UIExplorer/js/TextInputExample.ios.js b/Examples/UIExplorer/js/TextInputExample.ios.js index f6499160448063..960066872b270c 100644 --- a/Examples/UIExplorer/js/TextInputExample.ios.js +++ b/Examples/UIExplorer/js/TextInputExample.ios.js @@ -103,34 +103,6 @@ class TextEventsExample extends React.Component { } } -class AutoExpandingTextInput extends React.Component { - state: any; - - constructor(props) { - super(props); - this.state = { - text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.', - height: 0, - }; - } - render() { - return ( - { - this.setState({text}); - }} - onContentSizeChange={(event) => { - this.setState({height: event.nativeEvent.contentSize.height}); - }} - style={[styles.default, {height: Math.max(35, this.state.height)}]} - value={this.state.text} - /> - ); - } -} - class RewriteExample extends React.Component { state: any; @@ -403,6 +375,10 @@ var styles = StyleSheet.create({ padding: 4, marginBottom: 4, }, + multilineExpandable: { + height: 'auto', + maxHeight: 100, + }, multilineWithFontStyles: { color: 'blue', fontWeight: 'bold', @@ -801,10 +777,13 @@ exports.examples = [ render: function() { return ( - ); From b53d76efb7c5e7239061267a28d69b51fb068dfe Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 20 Mar 2017 00:00:23 -0700 Subject: [PATCH 095/366] Better TextInput: RCTUITextView was decoupled in separate file and now handles placeholder feature Reviewed By: mmmulani Differential Revision: D4663151 fbshipit-source-id: ce57ca4bebf4676df2ae5e586a1b175ec2aac760 --- .../UIExplorer/js/TextInputExample.ios.js | 2 +- .../Text/RCTText.xcodeproj/project.pbxproj | 8 + Libraries/Text/RCTTextView.h | 1 + Libraries/Text/RCTTextView.m | 283 ++++++------------ Libraries/Text/RCTUITextView.h | 28 ++ Libraries/Text/RCTUITextView.m | 189 ++++++++++++ React/Base/RCTBatchedBridge.m | 1 + 7 files changed, 319 insertions(+), 193 deletions(-) create mode 100644 Libraries/Text/RCTUITextView.h create mode 100644 Libraries/Text/RCTUITextView.m diff --git a/Examples/UIExplorer/js/TextInputExample.ios.js b/Examples/UIExplorer/js/TextInputExample.ios.js index 960066872b270c..89f8ccaff2ec2b 100644 --- a/Examples/UIExplorer/js/TextInputExample.ios.js +++ b/Examples/UIExplorer/js/TextInputExample.ios.js @@ -779,7 +779,7 @@ exports.examples = [ RCTTextUpdateLagWarningThreshold) { + RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); + } +} + +- (NSString *)text +{ + return _textView.text; +} + +- (void)setText:(NSString *)text +{ + NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; + if (eventLag == 0 && ![text isEqualToString:_textView.text]) { + UITextRange *selection = _textView.selectedTextRange; + NSInteger oldTextLength = _textView.text.length; + + _predictedText = text; + _textView.text = text; + + if (selection.empty) { + // maintain cursor position relative to the end of the old text + NSInteger start = [_textView offsetFromPosition:_textView.beginningOfDocument toPosition:selection.start]; + NSInteger offsetFromEnd = oldTextLength - start; + NSInteger newOffset = text.length - offsetFromEnd; + UITextPosition *position = [_textView positionFromPosition:_textView.beginningOfDocument offset:newOffset]; + _textView.selectedTextRange = [_textView textRangeFromPosition:position toPosition:position]; + } + + [self invalidateContentSize]; + } else if (eventLag > RCTTextUpdateLagWarningThreshold) { + RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); + } +} + +- (NSString *)placeholder +{ + return _textView.placeholderText; } - (void)setPlaceholder:(NSString *)placeholder { - _placeholder = placeholder; - [self updatePlaceholder]; + _textView.placeholderText = placeholder; +} + +- (UIColor *)placeholderTextColor +{ + return _textView.placeholderTextColor; } - (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor { - if (placeholderTextColor) { - _placeholderTextColor = placeholderTextColor; - } else { - _placeholderTextColor = [self defaultPlaceholderTextColor]; - } - [self updatePlaceholder]; + _textView.placeholderTextColor = placeholderTextColor; } -- (void)setContentInset:(UIEdgeInsets)contentInset +- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType { - _contentInset = contentInset; - _textView.textContainerInset = contentInset; - _placeholderView.textContainerInset = contentInset; - [self setNeedsLayout]; + _textView.autocorrectionType = autocorrectionType; +} + +- (UITextAutocorrectionType)autocorrectionType +{ + return _textView.autocorrectionType; +} + +- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType +{ + _textView.spellCheckingType = spellCheckingType; +} + +- (UITextSpellCheckingType)spellCheckingType +{ + return _textView.spellCheckingType; } #pragma mark - UITextViewDelegate - (BOOL)textView:(RCTUITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { - if (textView.textWasPasted) { - textView.textWasPasted = NO; - } else { + if (!textView.textWasPasted) { [_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress reactTag:self.reactTag text:nil @@ -425,86 +417,6 @@ - (void)textViewDidChangeSelection:(RCTUITextView *)textView } } -- (NSString *)text -{ - return _textView.text; -} - -- (void)setSelection:(RCTTextSelection *)selection -{ - if (!selection) { - return; - } - - UITextRange *currentSelection = _textView.selectedTextRange; - UITextPosition *start = [_textView positionFromPosition:_textView.beginningOfDocument offset:selection.start]; - UITextPosition *end = [_textView positionFromPosition:_textView.beginningOfDocument offset:selection.end]; - UITextRange *selectedTextRange = [_textView textRangeFromPosition:start toPosition:end]; - - NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - if (eventLag == 0 && ![currentSelection isEqual:selectedTextRange]) { - _previousSelectionRange = selectedTextRange; - _textView.selectedTextRange = selectedTextRange; - } else if (eventLag > RCTTextUpdateLagWarningThreshold) { - RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); - } -} - -- (void)setText:(NSString *)text -{ - NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - if (eventLag == 0 && ![text isEqualToString:_textView.text]) { - UITextRange *selection = _textView.selectedTextRange; - NSInteger oldTextLength = _textView.text.length; - - _predictedText = text; - _textView.text = text; - - if (selection.empty) { - // maintain cursor position relative to the end of the old text - NSInteger start = [_textView offsetFromPosition:_textView.beginningOfDocument toPosition:selection.start]; - NSInteger offsetFromEnd = oldTextLength - start; - NSInteger newOffset = text.length - offsetFromEnd; - UITextPosition *position = [_textView positionFromPosition:_textView.beginningOfDocument offset:newOffset]; - _textView.selectedTextRange = [_textView textRangeFromPosition:position toPosition:position]; - } - - [self updatePlaceholderVisibility]; - [self invalidateContentSize]; - } else if (eventLag > RCTTextUpdateLagWarningThreshold) { - RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); - } -} - -- (void)updatePlaceholderVisibility -{ - if (_textView.text.length > 0) { - [_placeholderView setHidden:YES]; - } else { - [_placeholderView setHidden:NO]; - } -} - -- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType -{ - _textView.autocorrectionType = autocorrectionType; -} - -- (UITextAutocorrectionType)autocorrectionType -{ - return _textView.autocorrectionType; -} - -- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType -{ - _textView.spellCheckingType = spellCheckingType; -} - -- (UITextSpellCheckingType)spellCheckingType -{ - return _textView.spellCheckingType; -} - - (BOOL)textViewShouldBeginEditing:(UITextView *)textView { if (_selectTextOnFocus) { @@ -519,7 +431,6 @@ - (void)textViewDidBeginEditing:(UITextView *)textView { if (_clearTextOnFocus) { _textView.text = @""; - [self updatePlaceholderVisibility]; } [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus @@ -560,7 +471,6 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange, - (void)textViewDidChange:(UITextView *)textView { - [self updatePlaceholderVisibility]; [self invalidateContentSize]; // Detect when textView updates happend that didn't invoke `shouldChangeTextInRange` @@ -580,6 +490,7 @@ - (void)textViewDidChange:(UITextView *)textView _nativeUpdatesInFlight = NO; _nativeEventCount++; + // TODO: t16435709 This part will be removed soon. if (!self.reactTag || !_onChange) { return; } @@ -718,18 +629,6 @@ - (void)layoutSubviews [self invalidateContentSize]; } -#pragma mark - Default values - -- (UIFont *)defaultPlaceholderFont -{ - return [UIFont systemFontOfSize:17]; -} - -- (UIColor *)defaultPlaceholderTextColor -{ - return [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.098/255.0 alpha:0.22]; -} - #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView diff --git a/Libraries/Text/RCTUITextView.h b/Libraries/Text/RCTUITextView.h new file mode 100644 index 00000000000000..762c1c2ad70cdb --- /dev/null +++ b/Libraries/Text/RCTUITextView.h @@ -0,0 +1,28 @@ +/** + * 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. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/* + * Just regular UITextView... but much better! + */ +@interface RCTUITextView : UITextView + +- (instancetype)initWithFrame:(CGRect)frame textContainer:(nullable NSTextContainer *)textContainer NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; + +@property (nonatomic, assign, readonly) BOOL textWasPasted; +@property (nonatomic, copy, nullable) NSString *placeholderText; +@property (nonatomic, assign, nullable) UIColor *placeholderTextColor; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Text/RCTUITextView.m b/Libraries/Text/RCTUITextView.m new file mode 100644 index 00000000000000..24302f823ed89a --- /dev/null +++ b/Libraries/Text/RCTUITextView.m @@ -0,0 +1,189 @@ +/** + * 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. + */ + +#import "RCTUITextView.h" + +@implementation RCTUITextView +{ + BOOL _jsRequestingFirstResponder; + UILabel *_placeholderView; + UITextView *_detachedTextView; +} + +static UIFont *defaultPlaceholderFont() +{ + return [UIFont systemFontOfSize:17]; +} + +static UIColor *defaultPlaceholderTextColor() +{ + // Default placeholder color from UITextField. + return [UIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22]; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textDidChange) + name:UITextViewTextDidChangeNotification + object:self]; + + _placeholderView = [[UILabel alloc] initWithFrame:self.bounds]; + _placeholderView.hidden = YES; + _placeholderView.isAccessibilityElement = NO; + _placeholderView.numberOfLines = 0; + [self addSubview:_placeholderView]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Properties + +- (void)setPlaceholderText:(NSString *)placeholderText +{ + _placeholderText = placeholderText; + [self invalidatePlaceholder]; +} + +- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor +{ + _placeholderTextColor = placeholderTextColor; + [self invalidatePlaceholder]; +} + + +- (void)textDidChange +{ + _textWasPasted = NO; + [self invalidatePlaceholder]; +} + +#pragma mark - UIResponder + +- (void)reactWillMakeFirstResponder +{ + _jsRequestingFirstResponder = YES; +} + +- (BOOL)canBecomeFirstResponder +{ + return _jsRequestingFirstResponder; +} + +- (void)reactDidMakeFirstResponder +{ + _jsRequestingFirstResponder = NO; +} + +- (void)didMoveToWindow +{ + if (_jsRequestingFirstResponder) { + [self becomeFirstResponder]; + [self reactDidMakeFirstResponder]; + } +} + +#pragma mark - Overrides + +- (void)setFont:(UIFont *)font +{ + [super setFont:font]; + [self invalidatePlaceholder]; +} + +- (void)setText:(NSString *)text +{ + [super setText:text]; + [self textDidChange]; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + [super setAttributedText:attributedText]; + [self textDidChange]; +} + +- (void)paste:(id)sender +{ + [super paste:sender]; + _textWasPasted = YES; +} + +- (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated +{ + // Turning off scroll animation. + // This fixes the problem also known as "flaky scrolling". + [super setContentOffset:contentOffset animated:NO]; +} + +#pragma mark - Layout + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGRect textFrame = UIEdgeInsetsInsetRect(self.bounds, self.textContainerInset); + CGFloat placeholderHeight = [_placeholderView sizeThatFits:textFrame.size].height; + textFrame.size.height = MIN(placeholderHeight, textFrame.size.height); + _placeholderView.frame = textFrame; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + // UITextView on iOS 8 has a bug that automatically scrolls to the top + // when calling `sizeThatFits:`. Use a copy so that self is not screwed up. + static BOOL useCustomImplementation = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + useCustomImplementation = ![[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9,0,0}]; + }); + + if (!useCustomImplementation) { + return [super sizeThatFits:size]; + } + + if (!_detachedTextView) { + _detachedTextView = [UITextView new]; + } + + _detachedTextView.attributedText = self.attributedText; + _detachedTextView.font = self.font; + _detachedTextView.textContainerInset = self.textContainerInset; + + return [_detachedTextView sizeThatFits:size]; +} + +#pragma mark - Placeholder + +- (void)invalidatePlaceholder +{ + BOOL wasVisible = !_placeholderView.isHidden; + BOOL isVisible = _placeholderText.length != 0 && self.text.length == 0; + + if (wasVisible != isVisible) { + _placeholderView.hidden = !isVisible; + } + + if (isVisible) { + _placeholderView.font = self.font ?: defaultPlaceholderFont(); + _placeholderView.textColor = _placeholderTextColor ?: defaultPlaceholderTextColor(); + _placeholderView.textAlignment = self.textAlignment; + _placeholderView.text = _placeholderText; + [self setNeedsLayout]; + } +} + +@end diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 4c065d0d612f61..480df01a953518 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -10,6 +10,7 @@ #import #import "RCTAssert.h" + #import "RCTBridge+Private.h" #import "RCTBridge.h" #import "RCTBridgeMethod.h" From 7168ed295496dbfb503076737f1931eee9d6e137 Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Mon, 20 Mar 2017 04:57:35 -0700 Subject: [PATCH 096/366] Revert D4676282: [rn] Move idle detection classes to its own directory Differential Revision: D4676282 fbshipit-source-id: ad5ee4f2cdbe407f67cfed74af7ddec18dabff3d --- .../java/com/facebook/react/testing/BUCK | 6 +- .../{idledetection => }/IdleWaiter.java | 2 +- .../ReactAppInstrumentationTestCase.java | 2 +- .../react/testing/ReactAppTestActivity.java | 2 - .../ReactBridgeIdleSignaler.java | 2 +- .../react/testing/ReactIdleDetectionUtil.java | 2 +- .../testing/ReactIntegrationTestCase.java | 2 - .../testing/SingleTouchGestureGenerator.java | 2 - .../facebook/react/testing/idledetection/BUCK | 14 -- .../idledetection/ReactIdleDetectionUtil.java | 125 ------------------ .../java/com/facebook/react/tests/BUCK | 1 - 11 files changed, 5 insertions(+), 155 deletions(-) rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{idledetection => }/IdleWaiter.java (90%) rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{idledetection => }/ReactBridgeIdleSignaler.java (97%) delete mode 100644 ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK delete mode 100644 ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK index 6814e3aa316138..5cf6affe9a6fab 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK @@ -2,10 +2,7 @@ include_defs("//ReactAndroid/DEFS") android_library( name = "testing", - srcs = glob( - ["**/*.java"], - excludes = ["idledetection/**/*.java"], - ), + srcs = glob(["**/*.java"]), visibility = [ "PUBLIC", ], @@ -28,6 +25,5 @@ android_library( react_native_target("java/com/facebook/react/modules/debug:interfaces"), react_native_target("java/com/facebook/react/shell:shell"), react_native_target("java/com/facebook/react/uimanager:uimanager"), - react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), ], ) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java similarity index 90% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java index 1b94b7c1fc88db..98884c1033a375 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing.idledetection; +package com.facebook.react.testing; /** * Interface for something that knows how to wait for bridge and UI idle. diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java index eeb8c02e06715d..aaba651def949c 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java @@ -20,7 +20,6 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.testing.idledetection.IdleWaiter; /** * Base class for instrumentation tests that runs React based react application in UI mode @@ -124,6 +123,7 @@ public void run() { } }; + getActivity().runOnUiThread(getScreenshotRunnable); try { if (!latch.await(5000, TimeUnit.MILLISECONDS)) { diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java index 11edda17a429b0..022e7826c1fe83 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java @@ -28,8 +28,6 @@ import com.facebook.react.common.LifecycleState; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.shell.MainReactPackage; -import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; -import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.react.uimanager.UIImplementationProvider; public class ReactAppTestActivity extends FragmentActivity implements diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java similarity index 97% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java index 4aaa451e43ab24..ffd941f9a228e6 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing.idledetection; +package com.facebook.react.testing; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java index af6ca2ebb82a55..49e2219327b96f 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing.idledetection; +package com.facebook.react.testing; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java index 77f5492c36cafd..752c89ea618161 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java @@ -32,8 +32,6 @@ import com.facebook.react.common.futures.SimpleSettableFuture; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.modules.core.Timing; -import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; -import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.soloader.SoLoader; import static org.mockito.Mockito.mock; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java index f0ae877a953d42..278facbcb280b4 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java @@ -13,8 +13,6 @@ import android.view.View; import android.view.ViewConfiguration; -import com.facebook.react.testing.idledetection.IdleWaiter; - /** * Provides methods for generating touch events and dispatching them directly to a given view. * Events scenarios are based on {@link android.test.TouchUtils} but they get gets dispatched diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK deleted file mode 100644 index f6b33535737d3f..00000000000000 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK +++ /dev/null @@ -1,14 +0,0 @@ -include_defs("//ReactAndroid/DEFS") - -android_library( - name = "idledetection", - srcs = glob(["**/*.java"]), - visibility = [ - "PUBLIC", - ], - deps = [ - react_native_dep("third-party/java/testing-support-lib:runner"), - react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/modules/core:core"), - ], -) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java deleted file mode 100644 index af6ca2ebb82a55..00000000000000 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) 2014-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. - */ - -package com.facebook.react.testing.idledetection; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import android.app.Instrumentation; -import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; - -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.UiThreadUtil; -import com.facebook.react.modules.core.ChoreographerCompat; - -public class ReactIdleDetectionUtil { - - /** - * Waits for both the UI thread and bridge to be idle. It determines this by waiting for the - * bridge to become idle, then waiting for the UI thread to become idle, then checking if the - * bridge is idle again (if the bridge was idle before and is still idle after running the UI - * thread to idle, then there are no more events to process in either place). - *

- * Also waits for any Choreographer callbacks to run after the initial sync since things like UI - * events are initiated from Choreographer callbacks. - */ - public static void waitForBridgeAndUIIdle( - ReactBridgeIdleSignaler idleSignaler, - final ReactContext reactContext, - long timeoutMs) { - UiThreadUtil.assertNotOnUiThread(); - - long startTime = SystemClock.uptimeMillis(); - waitInner(idleSignaler, timeoutMs); - - long timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime)); - waitForChoreographer(timeToWait); - waitForJSIdle(reactContext); - - timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime)); - waitInner(idleSignaler, timeToWait); - timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime)); - waitForChoreographer(timeToWait); - } - - private static void waitForChoreographer(long timeToWait) { - final int waitFrameCount = 2; - final CountDownLatch latch = new CountDownLatch(1); - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - ChoreographerCompat.getInstance().postFrameCallback( - new ChoreographerCompat.FrameCallback() { - - private int frameCount = 0; - - @Override - public void doFrame(long frameTimeNanos) { - frameCount++; - if (frameCount == waitFrameCount) { - latch.countDown(); - } else { - ChoreographerCompat.getInstance().postFrameCallback(this); - } - } - }); - } - }); - try { - if (!latch.await(timeToWait, TimeUnit.MILLISECONDS)) { - throw new RuntimeException("Timed out waiting for Choreographer"); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void waitForJSIdle(ReactContext reactContext) { - if (!reactContext.hasActiveCatalystInstance()) { - return; - } - final CountDownLatch latch = new CountDownLatch(1); - - reactContext.runOnJSQueueThread( - new Runnable() { - @Override - public void run() { - latch.countDown(); - } - }); - - try { - if (!latch.await(5000, TimeUnit.MILLISECONDS)) { - throw new RuntimeException("Timed out waiting for JS thread"); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void waitInner(ReactBridgeIdleSignaler idleSignaler, long timeToWait) { - // TODO gets broken in gradle, do we need it? - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - long startTime = SystemClock.uptimeMillis(); - boolean bridgeWasIdle = false; - while (SystemClock.uptimeMillis() - startTime < timeToWait) { - boolean bridgeIsIdle = idleSignaler.isBridgeIdle(); - if (bridgeIsIdle && bridgeWasIdle) { - return; - } - bridgeWasIdle = bridgeIsIdle; - long newTimeToWait = Math.max(1, timeToWait - (SystemClock.uptimeMillis() - startTime)); - idleSignaler.waitForIdle(newTimeToWait); - instrumentation.waitForIdleSync(); - } - throw new RuntimeException("Timed out waiting for bridge and UI idle!"); - } -} diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 992351f62026aa..96872ba647b382 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -5,7 +5,6 @@ deps = [ react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), react_native_integration_tests_target("java/com/facebook/react/testing:testing"), - react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), From 5b3920567d98f4b6219b6752dd06e1a5a82f9755 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Mon, 20 Mar 2017 05:34:57 -0700 Subject: [PATCH 097/366] Fix indentation of polyfills/require.js Summary: Fixes the messed-up indentation of `polyfills/require.js`. Over half of the lines were indented with an odd number of spaces. Reviewed By: arcanis, bestander Differential Revision: D4737435 fbshipit-source-id: a5b9baf0a27f236a4d3d6b6c1c5a92f52859f62c --- packager/src/Resolver/polyfills/require.js | 360 ++++++++++----------- 1 file changed, 180 insertions(+), 180 deletions(-) diff --git a/packager/src/Resolver/polyfills/require.js b/packager/src/Resolver/polyfills/require.js index d532f4a90b1ae8..7638675a44048d 100644 --- a/packager/src/Resolver/polyfills/require.js +++ b/packager/src/Resolver/polyfills/require.js @@ -10,30 +10,30 @@ * @flow */ - 'use strict'; +'use strict'; - declare var __DEV__: boolean; +declare var __DEV__: boolean; - type DependencyMap = Array; - type Exports = any; - type FactoryFn = ( +type DependencyMap = Array; +type Exports = any; +type FactoryFn = ( global: Object, require: RequireFn, moduleObject: {exports: {}}, exports: {}, dependencyMap: ?DependencyMap, ) => void; - type HotModuleReloadingAcceptFn = Function; - type HotModuleReloadingData = {| +type HotModuleReloadingAcceptFn = Function; +type HotModuleReloadingData = {| acceptCallback: ?HotModuleReloadingAcceptFn, accept: (callback: HotModuleReloadingAcceptFn) => void, |}; - type Module = { +type Module = { exports: Exports, hot?: HotModuleReloadingData, }; - type ModuleID = number; - type ModuleDefinition = {| +type ModuleID = number; +type ModuleDefinition = {| dependencyMap: ?DependencyMap, exports: Exports, factory: FactoryFn, @@ -42,247 +42,247 @@ isInitialized: boolean, verboseName?: string, |}; - type ModuleMap = +type ModuleMap = {[key: ModuleID]: (ModuleDefinition)}; - type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports; - type VerboseModuleNameForDev = string; +type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports; +type VerboseModuleNameForDev = string; - global.require = require; - global.__d = define; +global.require = require; +global.__d = define; - const modules: ModuleMap = Object.create(null); - if (__DEV__) { - var verboseNamesToModuleIds: {[key: string]: number} = Object.create(null); - } +const modules: ModuleMap = Object.create(null); +if (__DEV__) { + var verboseNamesToModuleIds: {[key: string]: number} = Object.create(null); +} - function define( +function define( factory: FactoryFn, moduleId: number, dependencyMap?: DependencyMap, ) { - if (moduleId in modules) { + if (moduleId in modules) { // prevent repeated calls to `global.nativeRequire` to overwrite modules // that are already loaded - return; - } - modules[moduleId] = { - dependencyMap, - exports: undefined, - factory, - hasError: false, - isInitialized: false, - }; - if (__DEV__) { + return; + } + modules[moduleId] = { + dependencyMap, + exports: undefined, + factory, + hasError: false, + isInitialized: false, + }; + if (__DEV__) { // HMR - modules[moduleId].hot = createHotReloadingObject(); + modules[moduleId].hot = createHotReloadingObject(); // DEBUGGABLE MODULES NAMES // we take `verboseName` from `arguments` to avoid an unused named parameter // in `define` in production. - const verboseName: string | void = arguments[3]; - if (verboseName) { - modules[moduleId].verboseName = verboseName; - verboseNamesToModuleIds[verboseName] = moduleId; - } - } - } - - function require(moduleId: ModuleID | VerboseModuleNameForDev) { - if (__DEV__ && typeof moduleId === 'string') { - const verboseName = moduleId; - moduleId = verboseNamesToModuleIds[moduleId]; - if (moduleId == null) { - throw new Error(`Unknown named module: '${verboseName}'`); - } else { - console.warn( + const verboseName: string | void = arguments[3]; + if (verboseName) { + modules[moduleId].verboseName = verboseName; + verboseNamesToModuleIds[verboseName] = moduleId; + } + } +} + +function require(moduleId: ModuleID | VerboseModuleNameForDev) { + if (__DEV__ && typeof moduleId === 'string') { + const verboseName = moduleId; + moduleId = verboseNamesToModuleIds[moduleId]; + if (moduleId == null) { + throw new Error(`Unknown named module: '${verboseName}'`); + } else { + console.warn( `Requiring module '${verboseName}' by name is only supported for ` + 'debugging purposes and will BREAK IN PRODUCTION!' ); - } - } + } + } //$FlowFixMe: at this point we know that moduleId is a number - const moduleIdReallyIsNumber: number = moduleId; - const module = modules[moduleIdReallyIsNumber]; - return module && module.isInitialized + const moduleIdReallyIsNumber: number = moduleId; + const module = modules[moduleIdReallyIsNumber]; + return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module); - } - - let inGuard = false; - function guardedLoadModule(moduleId: ModuleID, module) { - if (!inGuard && global.ErrorUtils) { - inGuard = true; - let returnValue; - try { - returnValue = loadModuleImplementation(moduleId, module); - } catch (e) { - global.ErrorUtils.reportFatalError(e); - } - inGuard = false; - return returnValue; - } else { - return loadModuleImplementation(moduleId, module); - } - } - - function loadModuleImplementation(moduleId, module) { - const nativeRequire = global.nativeRequire; - if (!module && nativeRequire) { - nativeRequire(moduleId); - module = modules[moduleId]; - } - - if (!module) { - throw unknownModuleError(moduleId); - } - - if (module.hasError) { - throw moduleThrewError(moduleId); - } +} + +let inGuard = false; +function guardedLoadModule(moduleId: ModuleID, module) { + if (!inGuard && global.ErrorUtils) { + inGuard = true; + let returnValue; + try { + returnValue = loadModuleImplementation(moduleId, module); + } catch (e) { + global.ErrorUtils.reportFatalError(e); + } + inGuard = false; + return returnValue; + } else { + return loadModuleImplementation(moduleId, module); + } +} + +function loadModuleImplementation(moduleId, module) { + const nativeRequire = global.nativeRequire; + if (!module && nativeRequire) { + nativeRequire(moduleId); + module = modules[moduleId]; + } + + if (!module) { + throw unknownModuleError(moduleId); + } + + if (module.hasError) { + throw moduleThrewError(moduleId); + } // `require` calls int the require polyfill itself are not analyzed and // replaced so that they use numeric module IDs. // The systrace module will expose itself on the require function so that // it can be used here. // TODO(davidaurelio) Scan polyfills for dependencies, too (t9759686) - if (__DEV__) { - var {Systrace} = require; - } + if (__DEV__) { + var {Systrace} = require; + } // We must optimistically mark module as initialized before running the // factory to keep any require cycles inside the factory from causing an // infinite require loop. - module.isInitialized = true; - const exports = module.exports = {}; - const {factory, dependencyMap} = module; - try { - if (__DEV__) { + module.isInitialized = true; + const exports = module.exports = {}; + const {factory, dependencyMap} = module; + try { + if (__DEV__) { // $FlowFixMe: we know that __DEV__ is const and `Systrace` exists - Systrace.beginEvent('JS_require_' + (module.verboseName || moduleId)); - } + Systrace.beginEvent('JS_require_' + (module.verboseName || moduleId)); + } - const moduleObject: Module = {exports}; - if (__DEV__ && module.hot) { - moduleObject.hot = module.hot; - } + const moduleObject: Module = {exports}; + if (__DEV__ && module.hot) { + moduleObject.hot = module.hot; + } // keep args in sync with with defineModuleCode in // packager/src//Resolver/index.js // and packager/src//ModuleGraph/worker.js - factory(global, require, moduleObject, exports, dependencyMap); + factory(global, require, moduleObject, exports, dependencyMap); // avoid removing factory in DEV mode as it breaks HMR - if (!__DEV__) { + if (!__DEV__) { // $FlowFixMe: This is only sound because we never access `factory` again - module.factory = undefined; - } + module.factory = undefined; + } - if (__DEV__) { + if (__DEV__) { // $FlowFixMe: we know that __DEV__ is const and `Systrace` exists - Systrace.endEvent(); - } - return (module.exports = moduleObject.exports); - } catch (e) { - module.hasError = true; - module.isInitialized = false; - module.exports = undefined; - throw e; - } - } - - function unknownModuleError(id) { - let message = 'Requiring unknown module "' + id + '".'; - if (__DEV__) { - message += + Systrace.endEvent(); + } + return (module.exports = moduleObject.exports); + } catch (e) { + module.hasError = true; + module.isInitialized = false; + module.exports = undefined; + throw e; + } +} + +function unknownModuleError(id) { + let message = 'Requiring unknown module "' + id + '".'; + if (__DEV__) { + message += 'If you are sure the module is there, try restarting the packager. ' + 'You may also want to run `npm install`, or `yarn` (depending on your environment).'; - } - return Error(message); - } + } + return Error(message); +} - function moduleThrewError(id) { - return Error('Requiring module "' + id + '", which threw an exception.'); - } +function moduleThrewError(id) { + return Error('Requiring module "' + id + '", which threw an exception.'); +} - if (__DEV__) { - require.Systrace = {beginEvent: () => {}, endEvent: () => {}}; +if (__DEV__) { + require.Systrace = {beginEvent: () => {}, endEvent: () => {}}; // HOT MODULE RELOADING - var createHotReloadingObject = function() { - const hot: HotModuleReloadingData = { - acceptCallback: null, - accept: callback => { hot.acceptCallback = callback; }, - }; - return hot; - }; - - const acceptAll = function( + var createHotReloadingObject = function() { + const hot: HotModuleReloadingData = { + acceptCallback: null, + accept: callback => { hot.acceptCallback = callback; }, + }; + return hot; + }; + + const acceptAll = function( dependentModules, inverseDependencies, ) { - if (!dependentModules || dependentModules.length === 0) { - return true; - } + if (!dependentModules || dependentModules.length === 0) { + return true; + } - const notAccepted = dependentModules.filter( + const notAccepted = dependentModules.filter( module => !accept(module, /*factory*/ undefined, inverseDependencies)); - const parents = []; - for (let i = 0; i < notAccepted.length; i++) { + const parents = []; + for (let i = 0; i < notAccepted.length; i++) { // if the module has no parents then the change cannot be hot loaded - if (inverseDependencies[notAccepted[i]].length === 0) { - return false; - } + if (inverseDependencies[notAccepted[i]].length === 0) { + return false; + } - parents.push(...inverseDependencies[notAccepted[i]]); - } + parents.push(...inverseDependencies[notAccepted[i]]); + } - return acceptAll(parents, inverseDependencies); - }; + return acceptAll(parents, inverseDependencies); + }; - const accept = function( + const accept = function( id: ModuleID, factory?: FactoryFn, inverseDependencies: {[key: ModuleID]: Array}, ) { - const mod = modules[id]; + const mod = modules[id]; - if (!mod && factory) { // new modules need a factory - define(factory, id); - return true; // new modules don't need to be accepted - } + if (!mod && factory) { // new modules need a factory + define(factory, id); + return true; // new modules don't need to be accepted + } - const {hot} = mod; - if (!hot) { - console.warn( + const {hot} = mod; + if (!hot) { + console.warn( 'Cannot accept module because Hot Module Replacement ' + 'API was not installed.' ); - return false; - } + return false; + } // replace and initialize factory - if (factory) { - mod.factory = factory; - } - mod.hasError = false; - mod.isInitialized = false; - require(id); - - if (hot.acceptCallback) { - hot.acceptCallback(); - return true; - } else { + if (factory) { + mod.factory = factory; + } + mod.hasError = false; + mod.isInitialized = false; + require(id); + + if (hot.acceptCallback) { + hot.acceptCallback(); + return true; + } else { // need to have inverseDependencies to bubble up accept - if (!inverseDependencies) { - throw new Error('Undefined `inverseDependencies`'); - } + if (!inverseDependencies) { + throw new Error('Undefined `inverseDependencies`'); + } // accept parent modules recursively up until all siblings are accepted - return acceptAll(inverseDependencies[id], inverseDependencies); - } - }; + return acceptAll(inverseDependencies[id], inverseDependencies); + } + }; - global.__accept = accept; - } + global.__accept = accept; +} From 2f92ee8a9cb95f6e67e25f2ed9061e1e255666bf Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 20 Mar 2017 05:57:48 -0700 Subject: [PATCH 098/366] Attempt to fix CircleCI tests Summary: Attempt to always call `mBridgeIdleListeners` on the native modules thread mBridgeIdleListeners Closes https://github.com/facebook/react-native/pull/13004 Differential Revision: D4734342 Pulled By: mkonicek fbshipit-source-id: f7054015a1d4517abab9bb8fc61402efabdd6ac1 --- .../react/cxxbridge/CatalystInstanceImpl.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java index 6133b2576765dc..81f2773befcb34 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -93,6 +93,7 @@ public PendingJSCall( private final NativeModuleRegistry mJavaRegistry; private final NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; + private final MessageQueueThread mNativeModulesQueueThread; private boolean mInitialized = false; private volatile boolean mAcceptCalls = false; @@ -121,6 +122,7 @@ private CatalystInstanceImpl( mJSModuleRegistry = jsModuleRegistry; mJSBundleLoader = jsBundleLoader; mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; + mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread(); mTraceListener = new JSProfilerTraceListener(this); FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge"); @@ -128,7 +130,7 @@ private CatalystInstanceImpl( new BridgeCallback(this), jsExecutor, mReactQueueConfiguration.getJSQueueThread(), - mReactQueueConfiguration.getNativeModulesQueueThread(), + mNativeModulesQueueThread, mJavaRegistry.getJavaModules(this), mJavaRegistry.getCxxModules()); FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge"); @@ -292,19 +294,19 @@ public void destroy() { // TODO: tell all APIs to shut down mDestroyed = true; - mReactQueueConfiguration.getNativeModulesQueueThread().runOnQueue(new Runnable() { + mNativeModulesQueueThread.runOnQueue(new Runnable() { @Override public void run() { mJavaRegistry.notifyJSInstanceDestroy(); + boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0); + if (!wasIdle && !mBridgeIdleListeners.isEmpty()) { + for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { + listener.onTransitionToBridgeIdle(); + } + } mHybridData.resetNative(); } }); - boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0); - if (!wasIdle && !mBridgeIdleListeners.isEmpty()) { - for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { - listener.onTransitionToBridgeIdle(); - } - } // This is a noop if the listener was not yet registered. Systrace.unregisterListener(mTraceListener); @@ -332,7 +334,7 @@ public void initialize() { mAcceptCalls, "RunJSBundle hasn't completed."); mInitialized = true; - mReactQueueConfiguration.getNativeModulesQueueThread().runOnQueue(new Runnable() { + mNativeModulesQueueThread.runOnQueue(new Runnable() { @Override public void run() { mJavaRegistry.notifyJSInstanceInitialized(); @@ -442,9 +444,14 @@ private void incrementPendingJSCalls() { mJsPendingCallsTitleForTrace, oldPendingCalls + 1); if (wasIdle && !mBridgeIdleListeners.isEmpty()) { - for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { - listener.onTransitionToBridgeBusy(); - } + mNativeModulesQueueThread.runOnQueue(new Runnable() { + @Override + public void run() { + for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { + listener.onTransitionToBridgeBusy(); + } + } + }); } } @@ -459,9 +466,14 @@ private void decrementPendingJSCalls() { newPendingCalls); if (isNowIdle && !mBridgeIdleListeners.isEmpty()) { - for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { - listener.onTransitionToBridgeIdle(); - } + mNativeModulesQueueThread.runOnQueue(new Runnable() { + @Override + public void run() { + for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { + listener.onTransitionToBridgeIdle(); + } + } + }); } } From 68c77395b182dd2d673ba60b7a9a63f585a12de7 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Mon, 20 Mar 2017 07:57:58 -0700 Subject: [PATCH 099/366] Allow flow-declared variables when inlining Summary: The logic of the `inline` babel transform regarded identifiers as global if no binding exists for them. We extend that logic to also accept flow-declared variables. Reviewed By: arcanis Differential Revision: D4737620 fbshipit-source-id: e71cfdf77c7b7751265cfa4412430b4f29e9e853 --- packager/package.json | 2 +- .../worker/__tests__/inline-test.js | 28 +++++++++++++++++++ packager/src/JSTransformer/worker/inline.js | 8 +++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packager/package.json b/packager/package.json index 2ea83333e54065..91de355c3be53f 100644 --- a/packager/package.json +++ b/packager/package.json @@ -1,5 +1,5 @@ { - "version": "0.5.0", + "version": "0.5.1", "name": "react-native-packager", "description": "Build native apps with React!", "repository": { diff --git a/packager/src/JSTransformer/worker/__tests__/inline-test.js b/packager/src/JSTransformer/worker/__tests__/inline-test.js index d2a10f556452f1..a08a242f791d93 100644 --- a/packager/src/JSTransformer/worker/__tests__/inline-test.js +++ b/packager/src/JSTransformer/worker/__tests__/inline-test.js @@ -306,4 +306,32 @@ describe('inline constants', () => { expect(toString(ast)).toEqual( normalize(code.replace(/require\([^)]+\)\.Platform\.OS/, '"android"'))); }); + + it('works with flow-declared variables', () => { + const stripFlow = require('babel-plugin-transform-flow-strip-types'); + const code = `declare var __DEV__; + const a: boolean = __DEV__;`; + + const transformed = transform( + code, + {...babelOptions, plugins: [stripFlow, [inline.plugin, {dev: false}]]}, + ).code; + + expect(transformed).toEqual('const a=false;'); + }); + + it('works with flow-declared variables in wrapped modules', () => { + const stripFlow = require('babel-plugin-transform-flow-strip-types'); + const code = `__d(() => { + declare var __DEV__; + const a: boolean = __DEV__; + });`; + + const transformed = transform( + code, + {...babelOptions, plugins: [stripFlow, [inline.plugin, {dev: true}]]}, + ).code; + + expect(transformed).toEqual('__d(()=>{const a=true;});'); + }); }); diff --git a/packager/src/JSTransformer/worker/inline.js b/packager/src/JSTransformer/worker/inline.js index 1b14bcdcbc8940..91811a1f864432 100644 --- a/packager/src/JSTransformer/worker/inline.js +++ b/packager/src/JSTransformer/worker/inline.js @@ -34,6 +34,12 @@ const importMap = new Map([['ReactNative', 'react-native']]); const isGlobal = binding => !binding; +const isFlowDeclared = binding => + t.isDeclareVariable(binding.path); + +const isGlobalOrFlowDeclared = binding => + isGlobal(binding) || isFlowDeclared(binding); + const isToplevelBinding = (binding, isWrappedModule) => isGlobal(binding) || !binding.scope.parent || @@ -93,7 +99,7 @@ const isReactPlatformSelect = (node, scope, isWrappedModule) => const isDev = (node, parent, scope) => t.isIdentifier(node, dev) && - isGlobal(scope.getBinding(dev.name)) && + isGlobalOrFlowDeclared(scope.getBinding(dev.name)) && !(t.isMemberExpression(parent)); function findProperty(objectExpression, key, fallback) { From 14fee735a2fde73f1e505543ee40af0df66f63aa Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Mon, 20 Mar 2017 08:24:31 -0700 Subject: [PATCH 100/366] Use verbose module name when requiring module that errored Summary: When requiring a module that has previously errored, the implementation of `require` only used the numerical module ID. In this diff, we enable usage of the verbose module name if present. Reviewed By: bestander Differential Revision: D4737723 fbshipit-source-id: 1c2d3906435a637f3e440e57f904489d84495bd2 --- packager/src/Resolver/polyfills/require.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packager/src/Resolver/polyfills/require.js b/packager/src/Resolver/polyfills/require.js index 7638675a44048d..655d762ac0a663 100644 --- a/packager/src/Resolver/polyfills/require.js +++ b/packager/src/Resolver/polyfills/require.js @@ -202,7 +202,8 @@ function unknownModuleError(id) { } function moduleThrewError(id) { - return Error('Requiring module "' + id + '", which threw an exception.'); + const displayName = __DEV__ && modules[id] && modules[id].verboseName || id; + return Error('Requiring module "' + displayName + '", which threw an exception.'); } if (__DEV__) { From c451dd6cceb526d4814993e4d0eb431375a9b01c Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 20 Mar 2017 10:37:58 -0700 Subject: [PATCH 101/366] Update message with instructions Summary: The middleware for automatically converting Systrace traces to HTML and popping the browser hasn't worked properly for a while, since the version on Homebrew generates some code that uses `Object.observe`, which was deleted from Chrome ages ago. People have complained about it, but fixing it properly has proven to be harder than expected, so I suggest we simply update the message with instructions for people to load it on Chrome, which is what all of us have been doing anyway (AFAIK). Closes https://github.com/facebook/react-native/pull/12445 Reviewed By: javache Differential Revision: D4700153 Pulled By: gaearon fbshipit-source-id: 0c33099babed93b3c70d36ae9dfc7d82460c8269 --- .../middleware/systraceProfileMiddleware.js | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/local-cli/server/middleware/systraceProfileMiddleware.js b/local-cli/server/middleware/systraceProfileMiddleware.js index 02b274c9e7a475..a06f0100a9ddd9 100644 --- a/local-cli/server/middleware/systraceProfileMiddleware.js +++ b/local-cli/server/middleware/systraceProfileMiddleware.js @@ -8,9 +8,7 @@ */ 'use strict'; -const exec = require('child_process').exec; const fs = require('fs'); -const path = require('path'); module.exports = function(req, res, next) { if (req.url !== '/systrace') { @@ -20,33 +18,12 @@ module.exports = function(req, res, next) { console.log('Dumping profile information...'); var dumpName = '/tmp/dump_' + Date.now() + '.json'; - var prefix = process.env.TRACE_VIEWER_PATH || ''; - var cmd = path.join(prefix, 'trace2html') + ' ' + dumpName; fs.writeFileSync(dumpName, req.rawBody); - exec(cmd, function(error) { - if (error) { - if (error.code === 127) { - var response = '\n** Failed executing `' + cmd + '` **\n\n' + - 'Google trace-viewer is required to visualize the data, ' + - 'You can install it with `brew install trace2html`\n\n' + - 'NOTE: Your profile data was kept at:\n' + dumpName; - console.log(response); - res.end(response); - } else { - console.error(error); - res.end('Unknown error: ' + error.message); - } - return; - } else { - exec('rm ' + dumpName); - exec('open ' + dumpName.replace(/json$/, 'html'), function(err) { - if (err) { - console.error(err); - res.end(err.message); - } else { - res.end(); - } - }); - } - }); + var response = + 'Your profile was saved at:\n' + dumpName + '\n\n' + + 'On Google Chrome navigate to chrome://tracing and then click on "load" ' + + 'to load and visualise your profile.\n\n' + + 'This message is also printed to your console by the packager so you can copy it :)'; + console.log(response); + res.end(response); }; From 439889262c9055faa3582a18441029ea063a7a6b Mon Sep 17 00:00:00 2001 From: Jeff Thomas Date: Mon, 20 Mar 2017 11:48:04 -0700 Subject: [PATCH 102/366] RCTLocalAssetImageLoader: Add searching shipped frameworks beyond mainBundle Reviewed By: javache Differential Revision: D4715608 fbshipit-source-id: 5cb2febf543e2ff6e30d0c6d8737de9a2cce2383 --- React/Base/RCTUtils.m | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 80ff69ed538d49..130c5b1b358955 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -637,7 +637,7 @@ BOOL RCTIsLocalAssetURL(NSURL *__nullable imageURL) NSString *imageName = RCTBundlePathForURL(imageURL); - NSBundle *bundle; + NSBundle *bundle = nil; NSArray *imagePathComponents = [imageName pathComponents]; if ([imagePathComponents count] > 1 && [[[imagePathComponents firstObject] pathExtension] isEqualToString:@"bundle"]) { @@ -646,11 +646,30 @@ BOOL RCTIsLocalAssetURL(NSURL *__nullable imageURL) imageName = [imageName substringFromIndex:(bundlePath.length + 1)]; } + UIImage *image = nil; if (bundle) { - return [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; + image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; } else { - return [UIImage imageNamed:imageName]; + image = [UIImage imageNamed:imageName]; + } + + if (!image && !bundle) { + // We did not find the image in the mainBundle, check in other shipped frameworks. + NSArray *possibleFrameworks = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[[NSBundle mainBundle] privateFrameworksURL] + includingPropertiesForKeys:@[] + options:nil + error:nil]; + for (NSURL *frameworkURL in possibleFrameworks) { + bundle = [NSBundle bundleWithURL:frameworkURL]; + image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; + if (image) { + RCTLogWarn(@"Image %@ not found in mainBundle, but found in %@", imageName, bundle); + break; + } + } } + + return image; } RCT_EXTERN NSString *__nullable RCTTempFilePath(NSString *extension, NSError **error) From f352aa129a0264671d7e294a1f895eb1beddc3f1 Mon Sep 17 00:00:00 2001 From: Jhen Date: Mon, 20 Mar 2017 12:40:16 -0700 Subject: [PATCH 103/366] Add missing `toggleElementInspector` event send when `jsLoaded` Summary: - [x] Explain the **motivation** for making this change. - [x] Provide a **test plan** demonstrating that the code is solid. - [x] Match the **code formatting** of the rest of the codebase. - [x] Target the `master` branch, NOT a "stable" branch. The PR #11613 (0.43) removed this missing `toggleElementInspector` event send when `jsLoaded` in DevMenu (Now is DevSettings), it should open the inspector if `isElementInspectorShown` is true when we reload JS. The dev menu text `Show / Hide Inspector` is dependent on `isElementInspectorShown` bool value. ([This behavior in 0.42](https://github.com/facebook/react-native/blob/0.42-stable/React/Modules/RCTDevMenu.mm#L436-L442)) Manual testing in UIExplorer: * Open the dev menu and click `Show Inspector` * Open the dev menu and click `Reload JS` * The built-in inspector should keep open (dev menu text: `Hide Inspector`) Closes https://github.com/facebook/react-native/pull/12999 Differential Revision: D4738959 Pulled By: javache fbshipit-source-id: b3f584db51aa0e1b463c52003967b00bcd81bc99 --- React/Modules/RCTDevSettings.mm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/React/Modules/RCTDevSettings.mm b/React/Modules/RCTDevSettings.mm index 171b2532094946..d97aa6031feefe 100644 --- a/React/Modules/RCTDevSettings.mm +++ b/React/Modules/RCTDevSettings.mm @@ -454,6 +454,14 @@ - (void)jsLoaded:(NSNotification *)notification dispatch_async(dispatch_get_main_queue(), ^{ // update state again after the bridge has finished loading [self _synchronizeAllSettings]; + + // Inspector can only be shown after JS has loaded + if ([self isElementInspectorShown]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self.bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil]; +#pragma clang diagnostic pop + } }); } From 08c404d2939a3c2e75a514da1a7c03151fbb30e7 Mon Sep 17 00:00:00 2001 From: Daniele Conti Date: Mon, 20 Mar 2017 12:40:45 -0700 Subject: [PATCH 104/366] Eagerly change the listeners count Summary: While working with `RCTEventEmitter` I noticed that if an event is emitted before `_listenerCount` is updated, it will not go through because the listeners count hasn't been updated. Moving the count update before the invokation of `startObserving` and `stopObserving` fixes the issue. Same way if you remove the last listener and an event is fired before the count is updated (while it shouldn't be fired). **Test plan (required)** An easy test to demonstrate it is to implement `startObserving` to synchronously fire an event. Without the change, a warning is thrown, with the change, the event is fired. Not very strong on Obj-C here and I didn't know how to mock out the native stuff. Would be glad to write a failing unit test tho :) Closes https://github.com/facebook/react-native/pull/11907 Differential Revision: D4738965 Pulled By: javache fbshipit-source-id: cf175051be5b9c5de761d3dcd290560e1639b05e --- React/Modules/RCTEventEmitter.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index 4cfec65755b1fa..9f54f1f499dc29 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -78,10 +78,10 @@ - (void)dealloc RCTLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`", eventName, [self class], [[self supportedEvents] componentsJoinedByString:@"`, `"]); } - if (_listenerCount == 0) { + _listenerCount++; + if (_listenerCount == 1) { [self startObserving]; } - _listenerCount++; } RCT_EXPORT_METHOD(removeListeners:(NSInteger)count) @@ -89,10 +89,10 @@ - (void)dealloc if (RCT_DEBUG && count > _listenerCount) { RCTLogError(@"Attempted to remove more %@ listeners than added", [self class]); } - if (count == _listenerCount) { + _listenerCount = MAX(_listenerCount - count, 0); + if (_listenerCount == 0) { [self stopObserving]; } - _listenerCount -= count; } @end From 242a58ffe08bf89ec4156ddc0bda89055f410886 Mon Sep 17 00:00:00 2001 From: Petter Hesselberg Date: Mon, 20 Mar 2017 12:44:41 -0700 Subject: [PATCH 105/366] Fix NullPointerException in ReactShadowNode.toString() Summary: Fix `NullPointerException` in `ReactShadowNode.toString` Simplified `ReactShadowNode.hasNewLayout` since I was already in there It seems to me unlikely that this bug impacts anything but the debugging experience, so no biggie. Closes https://github.com/facebook/react-native/pull/12953 Differential Revision: D4739215 fbshipit-source-id: 94955cc783216fdb8868fc8d08010e0d8a238052 --- .../com/facebook/react/uimanager/ReactShadowNode.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java index 2cad696e502860..d8c34346f261fd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java @@ -372,7 +372,7 @@ public void calculateLayout() { } public final boolean hasNewLayout() { - return mYogaNode == null ? false : mYogaNode.hasNewLayout(); + return mYogaNode != null && mYogaNode.hasNewLayout(); } public final void markLayoutSeen() { @@ -770,7 +770,11 @@ public void setMeasureFunction(YogaMeasureFunction measureFunction) { @Override public String toString() { - return mYogaNode.toString(); + if (mYogaNode != null) { + return mYogaNode.toString(); + } + + return getClass().getSimpleName() + " (virtual node)"; } public void dispose() { From ebb55c6bcc05f60aa04f9ca08a872a73b17832be Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Mon, 20 Mar 2017 12:50:20 -0700 Subject: [PATCH 106/366] log difference in QPL vs PerformanceLogger Reviewed By: alexeylang Differential Revision: D4736500 fbshipit-source-id: e5f8590ae7482dbfbbe64403b0162fb496572ac6 --- Libraries/Utilities/PerformanceLogger.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Libraries/Utilities/PerformanceLogger.js b/Libraries/Utilities/PerformanceLogger.js index fd77037d890c4f..053abb9ae6c69f 100644 --- a/Libraries/Utilities/PerformanceLogger.js +++ b/Libraries/Utilities/PerformanceLogger.js @@ -108,6 +108,10 @@ var PerformanceLogger = { extras = {}; }, + currentTimestamp() { + return performanceNow(); + }, + getTimespans() { return timespans; }, From d7314661fb4930f0c9e01ccc8de21be1f53d6f54 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Mon, 20 Mar 2017 12:56:30 -0700 Subject: [PATCH 107/366] Don't swallow the error if a module require fails Reviewed By: davidaurelio Differential Revision: D4733269 fbshipit-source-id: 2cca14c023b148b62cf24f204cdb355f8d2f3590 --- packager/src/Resolver/polyfills/require.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packager/src/Resolver/polyfills/require.js b/packager/src/Resolver/polyfills/require.js index 655d762ac0a663..3c1f0b24bad46b 100644 --- a/packager/src/Resolver/polyfills/require.js +++ b/packager/src/Resolver/polyfills/require.js @@ -38,6 +38,7 @@ type ModuleDefinition = {| exports: Exports, factory: FactoryFn, hasError: boolean, + error?: any, hot?: HotModuleReloadingData, isInitialized: boolean, verboseName?: string, @@ -138,7 +139,7 @@ function loadModuleImplementation(moduleId, module) { } if (module.hasError) { - throw moduleThrewError(moduleId); + throw moduleThrewError(moduleId, module.error); } // `require` calls int the require polyfill itself are not analyzed and @@ -185,6 +186,7 @@ function loadModuleImplementation(moduleId, module) { return (module.exports = moduleObject.exports); } catch (e) { module.hasError = true; + module.error = e; module.isInitialized = false; module.exports = undefined; throw e; @@ -201,9 +203,9 @@ function unknownModuleError(id) { return Error(message); } -function moduleThrewError(id) { +function moduleThrewError(id, error: any) { const displayName = __DEV__ && modules[id] && modules[id].verboseName || id; - return Error('Requiring module "' + displayName + '", which threw an exception.'); + return Error('Requiring module "' + displayName + '", which threw an exception: ' + error); } if (__DEV__) { From 9344f3a95b56833d29cd18438a94a0c22f67b0f8 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 20 Mar 2017 12:58:08 -0700 Subject: [PATCH 108/366] Support string return type from RN createReactNativeFiberComponentClass() Reviewed By: sebmarkbage Differential Revision: D4607283 fbshipit-source-id: 466d2373dd570f77ebcced306d2f20a3f72d79c6 --- Libraries/Components/TextInput/TextInput.js | 7 - Libraries/Components/View/View.js | 19 +- Libraries/ReactNative/UIManager.js | 23 +- .../ReactNative/requireNativeComponent.js | 2 +- .../renderers/native/NativeMethodsMixin.js | 206 +++++++++++------- .../native/NativeMethodsMixinUtils.js | 100 +++++++++ .../src/renderers/native/ReactNative.js | 2 +- .../src/renderers/native/ReactNativeFiber.js | 44 ++-- .../native/ReactNativeFiberHostComponent.js | 108 +++++++++ .../src/renderers/native/ReactNativeStack.js | 18 +- .../native/ReactNativeStackInjection.js | 4 +- .../native/createReactNativeComponentClass.js | 16 +- .../src/renderers/native/findNodeHandle.js | 5 +- 13 files changed, 424 insertions(+), 130 deletions(-) create mode 100644 Libraries/Renderer/src/renderers/native/NativeMethodsMixinUtils.js create mode 100644 Libraries/Renderer/src/renderers/native/ReactNativeFiberHostComponent.js diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index c3c9eff2b4ae9b..3f01022cb6c405 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -540,13 +540,6 @@ const TextInput = React.createClass({ */ mixins: [NativeMethodsMixin, TimerMixin], - viewConfig: - ((Platform.OS === 'ios' && RCTTextField ? - RCTTextField.viewConfig : - (Platform.OS === 'android' && AndroidTextInput ? - AndroidTextInput.viewConfig : - {})) : Object), - /** * Returns `true` if the input is currently focused; `false` otherwise. */ diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index a519dfbb6b1edc..385ffb124a48c8 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -16,6 +16,7 @@ const NativeMethodsMixin = require('NativeMethodsMixin'); const NativeModules = require('NativeModules'); const Platform = require('Platform'); const React = require('React'); +const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes'); const ReactNativeViewAttributes = require('ReactNativeViewAttributes'); const StyleSheetPropType = require('StyleSheetPropType'); @@ -119,6 +120,9 @@ const View = React.createClass({ ...statics, }, + // TODO (bvaughn) Replace this with a deprecated getter warning. This object + // should be accessible via a separate import. It will not be available in + // production mode in the future and so should not be directly accessed. propTypes: { ...TVViewPropTypes, @@ -536,11 +540,20 @@ if (__DEV__) { } } +// TODO (bvaughn) Remove feature flags once all static View accessors are gone. +// We temporarily wrap fiber native views with the create-class View above, +// Because external code sometimes accesses static properties of this view. let ViewToExport = RCTView; -if (__DEV__) { +if ( + __DEV__ || + ReactNativeFeatureFlags.useFiber +) { ViewToExport = View; } else { - Object.assign(RCTView, statics); + // TODO (bvaughn) Remove this mixin once all static View accessors are gone. + Object.assign((RCTView : any), statics); } -module.exports = ViewToExport; +// TODO (bvaughn) Temporarily mask Flow warnings for View property accesses. +// We're wrapping the string type (Fiber) for now to avoid any actual problems. +module.exports = ((ViewToExport : any) : typeof View); diff --git a/Libraries/ReactNative/UIManager.js b/Libraries/ReactNative/UIManager.js index ee2fc4b8625c71..3b131de7041c11 100644 --- a/Libraries/ReactNative/UIManager.js +++ b/Libraries/ReactNative/UIManager.js @@ -26,6 +26,27 @@ invariant(UIManager, 'UIManager is undefined. The native module config is probab const _takeSnapshot = UIManager.takeSnapshot; +// findNodeHandle() returns a reference to a wrapper component with viewConfig. +// This wrapper is required for NativeMethodsMixin.setNativeProps, but most +// callers want the native tag (number) and not the wrapper. For this purpose, +// the ReactNative renderer decorates findNodeHandle() and extracts the tag. +// However UIManager can't require ReactNative without introducing a cycle, and +// deferring the require causes a significant performance regression in Wilde +// (along the lines of 17% regression in RN Bridge startup). So as a temporary +// workaround, this wrapper method mimics what the native renderer does. +// TODO (bvaughn) Remove this and use findNodeHandle directly once stack is gone +function findNodeHandleWrapper(componentOrHandle : any) : ?number { + const instance: any = findNodeHandle(componentOrHandle); + + if (instance) { + return typeof instance._nativeTag === 'number' + ? instance._nativeTag + : instance.getHostNode(); + } else { + return null; + } +} + /** * Capture an image of the screen, window or an individual view. The image * will be stored in a temporary file that will only exist for as long as the @@ -57,7 +78,7 @@ UIManager.takeSnapshot = async function( return; } if (typeof view !== 'number' && view !== 'window') { - view = findNodeHandle(view) || 'window'; + view = findNodeHandleWrapper(view) || 'window'; } return _takeSnapshot(view, options); }; diff --git a/Libraries/ReactNative/requireNativeComponent.js b/Libraries/ReactNative/requireNativeComponent.js index c404e60bf66ac1..682a8240c57b43 100644 --- a/Libraries/ReactNative/requireNativeComponent.js +++ b/Libraries/ReactNative/requireNativeComponent.js @@ -46,7 +46,7 @@ function requireNativeComponent( viewName: string, componentInterface?: ?ComponentInterface, extraConfig?: ?{nativeOnly?: Object}, -): Function { +): ReactClass | string { const viewConfig = UIManager[viewName]; if (!viewConfig || !viewConfig.NativeProps) { warning(false, 'Native component for "%s" does not exist', viewName); diff --git a/Libraries/Renderer/src/renderers/native/NativeMethodsMixin.js b/Libraries/Renderer/src/renderers/native/NativeMethodsMixin.js index aa7af7f066a004..f3d0570a246138 100644 --- a/Libraries/Renderer/src/renderers/native/NativeMethodsMixin.js +++ b/Libraries/Renderer/src/renderers/native/NativeMethodsMixin.js @@ -13,45 +13,25 @@ var ReactNative = require('ReactNative'); var ReactNativeAttributePayload = require('ReactNativeAttributePayload'); +var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); var TextInputState = require('TextInputState'); var UIManager = require('UIManager'); var invariant = require('fbjs/lib/invariant'); +var findNodeHandle = require('findNodeHandle'); -type MeasureOnSuccessCallback = ( - x: number, - y: number, - width: number, - height: number, - pageX: number, - pageY: number -) => void - -type MeasureInWindowOnSuccessCallback = ( - x: number, - y: number, - width: number, - height: number, -) => void - -type MeasureLayoutOnSuccessCallback = ( - left: number, - top: number, - width: number, - height: number -) => void - -function warnForStyleProps(props, validAttributes) { - for (var key in validAttributes.style) { - if (!(validAttributes[key] || props[key] === undefined)) { - console.error( - 'You are setting the style `{ ' + key + ': ... }` as a prop. You ' + - 'should nest it in a style object. ' + - 'E.g. `{ style: { ' + key + ': ... } }`' - ); - } - } -} +var { + mountSafeCallback, + throwOnStylesProp, + warnForStyleProps, +} = require('NativeMethodsMixinUtils'); + +import type { + MeasureInWindowOnSuccessCallback, + MeasureLayoutOnSuccessCallback, + MeasureOnSuccessCallback, +} from 'NativeMethodsMixinUtils'; +import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry'; /** * `NativeMethodsMixin` provides methods to access the underlying native @@ -65,6 +45,10 @@ function warnForStyleProps(props, validAttributes) { * information, see [Direct * Manipulation](docs/direct-manipulation.html). */ +// TODO (bvaughn) Figure out how to use the NativeMethodsInterface type to- +// ensure that these mixins and ReactNativeFiberHostComponent stay in sync. +// Unfortunately, using it causes Flow to complain WRT createClass mixins: +// "call of method `createClass`. Expected an exact object instead of ..." var NativeMethodsMixin = { /** * Determines the location on screen, width, and height of the given view and @@ -140,20 +124,15 @@ var NativeMethodsMixin = { * Manipulation](docs/direct-manipulation.html)). */ setNativeProps: function(nativeProps: Object) { - if (__DEV__) { - warnForStyleProps(nativeProps, this.viewConfig.validAttributes); - } + // Ensure ReactNative factory function has configured findNodeHandle. + // Requiring it won't execute the factory function until first referenced. + // It's possible for tests that use ReactTestRenderer to reach this point, + // Without having executed ReactNative. + // Defer the factory function until now to avoid a cycle with UIManager. + // TODO (bvaughn) Remove this once ReactNativeStack is dropped. + require('ReactNative'); - var updatePayload = ReactNativeAttributePayload.create( - nativeProps, - this.viewConfig.validAttributes - ); - - UIManager.updateView( - (ReactNative.findNodeHandle(this) : any), - this.viewConfig.uiViewClassName, - updatePayload - ); + injectedSetNativeProps(this, nativeProps); }, /** @@ -172,19 +151,116 @@ var NativeMethodsMixin = { }, }; -function throwOnStylesProp(component, props) { - if (props.styles !== undefined) { - var owner = component._owner || null; - var name = component.constructor.displayName; - var msg = '`styles` is not a supported property of `' + name + '`, did ' + - 'you mean `style` (singular)?'; - if (owner && owner.constructor && owner.constructor.displayName) { - msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' + - ' component.'; +// TODO (bvaughn) Inline this once ReactNativeStack is dropped. +function setNativePropsFiber(componentOrHandle: any, nativeProps: Object) { + // Class components don't have viewConfig -> validateAttributes. + // Nor does it make sense to set native props on a non-native component. + // Instead, find the nearest host component and set props on it. + // Use findNodeHandle() rather than ReactNative.findNodeHandle() because + // We want the instance/wrapper (not the native tag). + let maybeInstance; + + // Fiber errors if findNodeHandle is called for an umounted component. + // Tests using ReactTestRenderer will trigger this case indirectly. + // Mimicking stack behavior, we should silently ignore this case. + // TODO Fix ReactTestRenderer so we can remove this try/catch. + try { + maybeInstance = findNodeHandle(componentOrHandle); + } catch (error) {} + + // If there is no host component beneath this we should fail silently. + // This is not an error; it could mean a class component rendered null. + if (maybeInstance == null) { + return; + } + + const viewConfig : ReactNativeBaseComponentViewConfig = + maybeInstance.viewConfig; + + if (__DEV__) { + warnForStyleProps(nativeProps, viewConfig.validAttributes); + } + + var updatePayload = ReactNativeAttributePayload.create( + nativeProps, + viewConfig.validAttributes, + ); + + UIManager.updateView( + maybeInstance._nativeTag, + viewConfig.uiViewClassName, + updatePayload, + ); +} + +// TODO (bvaughn) Remove this once ReactNativeStack is dropped. +function setNativePropsStack(componentOrHandle: any, nativeProps: Object) { + // Class components don't have viewConfig -> validateAttributes. + // Nor does it make sense to set native props on a non-native component. + // Instead, find the nearest host component and set props on it. + // Use findNodeHandle() rather than ReactNative.findNodeHandle() because + // We want the instance/wrapper (not the native tag). + let maybeInstance = findNodeHandle(componentOrHandle); + + // If there is no host component beneath this we should fail silently. + // This is not an error; it could mean a class component rendered null. + if (maybeInstance == null) { + return; + } + + let viewConfig : ReactNativeBaseComponentViewConfig; + if (maybeInstance.viewConfig !== undefined) { + // ReactNativeBaseComponent + viewConfig = maybeInstance.viewConfig; + } else if ( + maybeInstance._instance !== undefined && + maybeInstance._instance.viewConfig !== undefined + ) { + // ReactCompositeComponentWrapper + // Some instances (eg Text) define their own viewConfig + viewConfig = maybeInstance._instance.viewConfig; + } else { + // ReactCompositeComponentWrapper + // Other instances (eg TextInput) defer to their children's viewConfig + while (maybeInstance._renderedComponent !== undefined) { + maybeInstance = maybeInstance._renderedComponent; } - throw new Error(msg); + viewConfig = maybeInstance.viewConfig; + } + + const tag : number = typeof maybeInstance.getHostNode === 'function' + ? maybeInstance.getHostNode() + : maybeInstance._rootNodeID; + + if (__DEV__) { + warnForStyleProps(nativeProps, viewConfig.validAttributes); } + + var updatePayload = ReactNativeAttributePayload.create( + nativeProps, + viewConfig.validAttributes, + ); + + UIManager.updateView( + tag, + viewConfig.uiViewClassName, + updatePayload, + ); +} + +// Switching based on fiber vs stack to avoid a lot of inline checks at runtime. +// HACK Normally this injection would be done by the renderer, but in this case +// that would result in a cycle between ReactNative and NativeMethodsMixin. +// We avoid requiring additional code for this injection so it's probably ok? +// TODO (bvaughn) Remove this once ReactNativeStack is gone. +let injectedSetNativeProps : + (componentOrHandle: any, nativeProps: Object) => void; +if (ReactNativeFeatureFlags.useFiber) { + injectedSetNativeProps = setNativePropsFiber; +} else { + injectedSetNativeProps = setNativePropsStack; } + if (__DEV__) { // hide this from Flow since we can't define these properties outside of // __DEV__ without actually implementing them (setting them to undefined @@ -203,20 +279,4 @@ if (__DEV__) { }; } -/** - * In the future, we should cleanup callbacks by cancelling them instead of - * using this. - */ -function mountSafeCallback( - context: ReactComponent, - callback: ?Function -): any { - return function() { - if (!callback || (typeof context.isMounted === 'function' && !context.isMounted())) { - return undefined; - } - return callback.apply(context, arguments); - }; -} - module.exports = NativeMethodsMixin; diff --git a/Libraries/Renderer/src/renderers/native/NativeMethodsMixinUtils.js b/Libraries/Renderer/src/renderers/native/NativeMethodsMixinUtils.js new file mode 100644 index 00000000000000..b5aa4ae8d0ad78 --- /dev/null +++ b/Libraries/Renderer/src/renderers/native/NativeMethodsMixinUtils.js @@ -0,0 +1,100 @@ +/** + * 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. + * + * @providesModule NativeMethodsMixinUtils + * @flow + */ +'use strict'; + +export type MeasureOnSuccessCallback = ( + x: number, + y: number, + width: number, + height: number, + pageX: number, + pageY: number +) => void + +export type MeasureInWindowOnSuccessCallback = ( + x: number, + y: number, + width: number, + height: number, +) => void + +export type MeasureLayoutOnSuccessCallback = ( + left: number, + top: number, + width: number, + height: number +) => void + +/** + * Shared between ReactNativeFiberHostComponent and NativeMethodsMixin to keep + * API in sync. + */ +export interface NativeMethodsInterface { + blur() : void, + focus() : void, + measure(callback : MeasureOnSuccessCallback) : void, + measureInWindow(callback : MeasureInWindowOnSuccessCallback) : void, + measureLayout( + relativeToNativeNode: number, + onSuccess: MeasureLayoutOnSuccessCallback, + onFail: () => void /* currently unused */ + ) : void, + setNativeProps(nativeProps: Object) : void, +} + +/** + * In the future, we should cleanup callbacks by cancelling them instead of + * using this. + */ +function mountSafeCallback( + context: any, + callback: ?Function +): any { + return function() { + if (!callback || (typeof context.isMounted === 'function' && !context.isMounted())) { + return undefined; + } + return callback.apply(context, arguments); + }; +} + +function throwOnStylesProp(component : any, props : any) { + if (props.styles !== undefined) { + var owner = component._owner || null; + var name = component.constructor.displayName; + var msg = '`styles` is not a supported property of `' + name + '`, did ' + + 'you mean `style` (singular)?'; + if (owner && owner.constructor && owner.constructor.displayName) { + msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' + + ' component.'; + } + throw new Error(msg); + } +} + +function warnForStyleProps(props : any, validAttributes : any) { + for (var key in validAttributes.style) { + if (!(validAttributes[key] || props[key] === undefined)) { + console.error( + 'You are setting the style `{ ' + key + ': ... }` as a prop. You ' + + 'should nest it in a style object. ' + + 'E.g. `{ style: { ' + key + ': ... } }`' + ); + } + } +} + +module.exports = { + mountSafeCallback, + throwOnStylesProp, + warnForStyleProps, +}; diff --git a/Libraries/Renderer/src/renderers/native/ReactNative.js b/Libraries/Renderer/src/renderers/native/ReactNative.js index 527a53920b741a..82b12de9a1ca7d 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNative.js +++ b/Libraries/Renderer/src/renderers/native/ReactNative.js @@ -15,4 +15,4 @@ const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); module.exports = ReactNativeFeatureFlags.useFiber ? require('ReactNativeFiber') - : require('ReactNativeStack') + : require('ReactNativeStack'); diff --git a/Libraries/Renderer/src/renderers/native/ReactNativeFiber.js b/Libraries/Renderer/src/renderers/native/ReactNativeFiber.js index 1d73e80b88af19..e2ad6701a4c0aa 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNativeFiber.js +++ b/Libraries/Renderer/src/renderers/native/ReactNativeFiber.js @@ -12,16 +12,11 @@ 'use strict'; -import type { Element } from 'React'; -import type { Fiber } from 'ReactFiber'; -import type { ReactNodeList } from 'ReactTypes'; -import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry'; - -const NativeMethodsMixin = require('NativeMethodsMixin'); const ReactFiberReconciler = require('ReactFiberReconciler'); const ReactGenericBatching = require('ReactGenericBatching'); const ReactNativeAttributePayload = require('ReactNativeAttributePayload'); const ReactNativeComponentTree = require('ReactNativeComponentTree'); +const ReactNativeFiberHostComponent = require('ReactNativeFiberHostComponent'); const ReactNativeInjection = require('ReactNativeInjection'); const ReactNativeTagHandles = require('ReactNativeTagHandles'); const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry'); @@ -34,6 +29,11 @@ const findNodeHandle = require('findNodeHandle'); const invariant = require('fbjs/lib/invariant'); const { injectInternals } = require('ReactFiberDevToolsHook'); + +import type { Element } from 'React'; +import type { Fiber } from 'ReactFiber'; +import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry'; +import type { ReactNodeList } from 'ReactTypes'; const { precacheFiberNode, uncacheFiberNode, @@ -43,7 +43,7 @@ const { ReactNativeInjection.inject(); type Container = number; -type Instance = { +export type Instance = { _children: Array, _nativeTag: number, viewConfig: ReactNativeBaseComponentViewConfig, @@ -51,13 +51,6 @@ type Instance = { type Props = Object; type TextInstance = number; -function NativeHostComponent(tag, viewConfig) { - this._nativeTag = tag; - this._children = []; - this.viewConfig = viewConfig; -} -Object.assign(NativeHostComponent.prototype, NativeMethodsMixin); - function recursivelyUncacheFiberNode(node : Instance | TextInstance) { if (typeof node === 'number') { // Leaf node (eg text) uncacheFiberNode(node); @@ -156,7 +149,7 @@ const NativeRenderer = ReactFiberReconciler({ const viewConfig = ReactNativeViewConfigRegistry.get(type); if (__DEV__) { - for (let key in viewConfig.validAttributes) { + for (const key in viewConfig.validAttributes) { if (props.hasOwnProperty(key)) { deepFreezeAndThrowOnMutationInDev(props[key]); } @@ -175,12 +168,14 @@ const NativeRenderer = ReactFiberReconciler({ updatePayload, // props ); - const component = new NativeHostComponent(tag, viewConfig); + const component = new ReactNativeFiberHostComponent(tag, viewConfig); precacheFiberNode(internalInstanceHandle, tag); updateFiberProps(tag, props); - return component; + // Not sure how to avoid this cast. Flow is okay if the component is defined + // in the same file but if it's external it can't see the types. + return ((component : any) : Instance); }, createTextInstance( @@ -367,17 +362,20 @@ ReactGenericBatching.injection.injectFiberBatchedUpdates( const roots = new Map(); findNodeHandle.injection.injectFindNode( - (fiber: Fiber) => { - const instance: any = NativeRenderer.findHostInstance(fiber); - return instance ? instance._nativeTag : null; - } + (fiber: Fiber) => NativeRenderer.findHostInstance(fiber) ); findNodeHandle.injection.injectFindRootNodeID( - (instance) => instance._nativeTag + (instance) => instance ); const ReactNative = { - findNodeHandle, + // External users of findNodeHandle() expect the host tag number return type. + // The injected findNodeHandle() strategy returns the instance wrapper though. + // See NativeMethodsMixin#setNativeProps for more info on why this is done. + findNodeHandle(componentOrHandle : any) : ?number { + const instance: any = findNodeHandle(componentOrHandle); + return instance ? instance._nativeTag : null; + }, render(element : Element, containerTag : any, callback: ?Function) { let root = roots.get(containerTag); diff --git a/Libraries/Renderer/src/renderers/native/ReactNativeFiberHostComponent.js b/Libraries/Renderer/src/renderers/native/ReactNativeFiberHostComponent.js new file mode 100644 index 00000000000000..2935b199d15f0b --- /dev/null +++ b/Libraries/Renderer/src/renderers/native/ReactNativeFiberHostComponent.js @@ -0,0 +1,108 @@ +/** + * Copyright 2013-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. + * + * @providesModule ReactNativeFiberHostComponent + * @flow + * @preventMunge + */ + +'use strict'; + +var ReactNativeAttributePayload = require('ReactNativeAttributePayload'); +var TextInputState = require('TextInputState'); +var UIManager = require('UIManager'); + +var { + mountSafeCallback, + warnForStyleProps, +} = require('NativeMethodsMixinUtils'); + +import type { + MeasureInWindowOnSuccessCallback, + MeasureLayoutOnSuccessCallback, + MeasureOnSuccessCallback, + NativeMethodsInterface, +} from 'NativeMethodsMixinUtils'; +import type { Instance } from 'ReactNativeFiber'; +import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry'; + +/** + * This component defines the same methods as NativeMethodsMixin but without the + * findNodeHandle wrapper. This wrapper is unnecessary for HostComponent views + * and would also result in a circular require.js dependency (since + * ReactNativeFiber depends on this component and NativeMethodsMixin depends on + * ReactNativeFiber). + */ +class ReactNativeFiberHostComponent implements NativeMethodsInterface { + _children: Array + _nativeTag: number + viewConfig: ReactNativeBaseComponentViewConfig + + constructor( + tag : number, + viewConfig : ReactNativeBaseComponentViewConfig + ) { + this._nativeTag = tag; + this._children = []; + this.viewConfig = viewConfig; + } + + blur() { + TextInputState.blurTextInput(this._nativeTag); + } + + focus() { + TextInputState.focusTextInput(this._nativeTag); + } + + measure(callback: MeasureOnSuccessCallback) { + UIManager.measure( + this._nativeTag, + mountSafeCallback(this, callback) + ); + } + + measureInWindow(callback: MeasureInWindowOnSuccessCallback) { + UIManager.measureInWindow( + this._nativeTag, + mountSafeCallback(this, callback) + ); + } + + measureLayout( + relativeToNativeNode: number, + onSuccess: MeasureLayoutOnSuccessCallback, + onFail: () => void /* currently unused */ + ) { + UIManager.measureLayout( + this._nativeTag, + relativeToNativeNode, + mountSafeCallback(this, onFail), + mountSafeCallback(this, onSuccess) + ); + } + + setNativeProps(nativeProps: Object) { + if (__DEV__) { + warnForStyleProps(nativeProps, this.viewConfig.validAttributes); + } + + var updatePayload = ReactNativeAttributePayload.create( + nativeProps, + this.viewConfig.validAttributes + ); + + UIManager.updateView( + this._nativeTag, + this.viewConfig.uiViewClassName, + updatePayload + ); + } +} + +module.exports = ReactNativeFiberHostComponent; diff --git a/Libraries/Renderer/src/renderers/native/ReactNativeStack.js b/Libraries/Renderer/src/renderers/native/ReactNativeStack.js index 55cf71fa1c6bec..d6ae88792d9d1e 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNativeStack.js +++ b/Libraries/Renderer/src/renderers/native/ReactNativeStack.js @@ -13,8 +13,8 @@ var ReactNativeComponentTree = require('ReactNativeComponentTree'); var ReactNativeInjection = require('ReactNativeInjection'); -var ReactNativeStackInjection = require('ReactNativeStackInjection'); var ReactNativeMount = require('ReactNativeMount'); +var ReactNativeStackInjection = require('ReactNativeStackInjection'); var ReactUpdates = require('ReactUpdates'); var findNodeHandle = require('findNodeHandle'); @@ -30,16 +30,16 @@ var render = function( return ReactNativeMount.renderComponent(element, mountInto, callback); }; -findNodeHandle.injection.injectFindNode( - (instance) => instance.getHostNode() -); -findNodeHandle.injection.injectFindRootNodeID( - (instance) => instance._rootNodeID -); - var ReactNative = { hasReactNativeInitialized: false, - findNodeHandle: findNodeHandle, + + // External users of findNodeHandle() expect the host tag number return type. + // The injected findNodeHandle() strategy returns the instance wrapper though. + // See NativeMethodsMixin#setNativeProps for more info on why this is done. + findNodeHandle(componentOrHandle : any) : ?number { + return findNodeHandle(componentOrHandle).getHostNode(); + }, + render: render, unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode, diff --git a/Libraries/Renderer/src/renderers/native/ReactNativeStackInjection.js b/Libraries/Renderer/src/renderers/native/ReactNativeStackInjection.js index fe4f4cd4c03566..cc1f812a65cf21 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNativeStackInjection.js +++ b/Libraries/Renderer/src/renderers/native/ReactNativeStackInjection.js @@ -63,10 +63,10 @@ function inject() { }; findNodeHandle.injection.injectFindNode( - (instance) => instance.getHostNode() + (instance) => instance ); findNodeHandle.injection.injectFindRootNodeID( - (instance) => instance._rootNodeID + (instance) => instance ); ReactEmptyComponent.injection.injectEmptyComponentFactory(EmptyComponent); diff --git a/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass.js b/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass.js index bf53cf81616e8f..3549c750fbeebe 100644 --- a/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass.js +++ b/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass.js @@ -13,8 +13,8 @@ 'use strict'; const ReactNativeBaseComponent = require('ReactNativeBaseComponent'); -const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry'); const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); +const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry'); // See also ReactNativeBaseComponent type ReactNativeBaseComponentViewConfig = { @@ -27,14 +27,12 @@ type ReactNativeBaseComponentViewConfig = { * @param {string} config iOS View configuration. * @private */ - const createReactNativeFiberComponentClass = function( - viewConfig: ReactNativeBaseComponentViewConfig - ): ReactClass { - // TODO(sema): This actually returns a string. Need to fix this before - // we deploy Fiber. - return (ReactNativeViewConfigRegistry.register(viewConfig) : any); - }; - +const createReactNativeFiberComponentClass = function( + viewConfig: ReactNativeBaseComponentViewConfig +): string { + return ReactNativeViewConfigRegistry.register(viewConfig); +}; + /** * @param {string} config iOS View configuration. * @private diff --git a/Libraries/Renderer/src/renderers/native/findNodeHandle.js b/Libraries/Renderer/src/renderers/native/findNodeHandle.js index 7edb378924d441..2230ee37b45ac0 100644 --- a/Libraries/Renderer/src/renderers/native/findNodeHandle.js +++ b/Libraries/Renderer/src/renderers/native/findNodeHandle.js @@ -53,7 +53,10 @@ import type { ReactInstance } from 'ReactInstanceType'; let injectedFindNode; let injectedFindRootNodeID; -function findNodeHandle(componentOrHandle: any): ?number { +// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive +// eg findInternalHostInstance. This will reduce the likelihood of someone +// accidentally deep-requiring this version. +function findNodeHandle(componentOrHandle: any): any { if (__DEV__) { // TODO: fix this unsafe cast to work with Fiber. var owner = ((ReactCurrentOwner.current: any): ReactInstance | null); From 6dc3a83e88ed120decbeaed8e4e62dc2bb7107a3 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Mon, 20 Mar 2017 13:03:04 -0700 Subject: [PATCH 109/366] Don't load native module support as part of the initial CS bundle Reviewed By: javache Differential Revision: D4720386 fbshipit-source-id: cd8b6137aaff2d907adf089060bf7d356cd2f437 --- ReactCommon/cxxreact/JSCExecutor.cpp | 84 ++++++++++++++++++---------- ReactCommon/cxxreact/JSCExecutor.h | 2 + ReactCommon/jschelpers/Value.h | 7 +++ 3 files changed, 63 insertions(+), 30 deletions(-) diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index cee2f2ece2e350..810d0ecfcf5cb9 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -361,7 +361,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr scrip evaluateSourceCode(m_context, bcSourceCode, jsSourceURL); - bindBridge(); flush(); ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); @@ -412,7 +411,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr scrip evaluateScript(m_context, jsScript, jsSourceURL); } - bindBridge(); flush(); ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); @@ -428,24 +426,32 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr unbund void JSCExecutor::bindBridge() throw(JSException) { SystraceSection s("JSCExecutor::bindBridge"); - if (!m_delegate || !m_delegate->getModuleRegistry()) { - return; - } - auto global = Object::getGlobalObject(m_context); - auto batchedBridgeValue = global.getProperty("__fbBatchedBridge"); - if (batchedBridgeValue.isUndefined()) { - throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly"); - } + std::call_once(m_bindFlag, [this] { + auto global = Object::getGlobalObject(m_context); + auto batchedBridgeValue = global.getProperty("__fbBatchedBridge"); + if (batchedBridgeValue.isUndefined()) { + auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge"); + if (!requireBatchedBridge.isUndefined()) { + batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({}); + } + if (batchedBridgeValue.isUndefined()) { + throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly"); + } + } - auto batchedBridge = batchedBridgeValue.asObject(); - m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject(); - m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject(); - m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject(); - m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject(); + auto batchedBridge = batchedBridgeValue.asObject(); + m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject(); + m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject(); + m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject(); + m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject(); + }); } void JSCExecutor::callNativeModules(Value&& value) { SystraceSection s("JSCExecutor::callNativeModules"); + // If this fails, you need to pass a fully functional delegate with a + // module registry to the factory/ctor. + CHECK(m_delegate) << "Attempting to use native modules without a delegate"; try { auto calls = value.toJSONString(); m_delegate->callNativeModules(*this, folly::parseJson(calls), true); @@ -462,17 +468,31 @@ void JSCExecutor::callNativeModules(Value&& value) { void JSCExecutor::flush() { SystraceSection s("JSCExecutor::flush"); - if (!m_delegate) { - // do nothing - } else if (!m_delegate->getModuleRegistry()) { - callNativeModules(Value::makeNull(m_context)); - } else { - // If this is failing, chances are you have provided a delegate with a - // module registry, but haven't loaded the JS which enables native function - // queueing. Add BatchedBridge.js to your bundle, pass a nullptr delegate, - // or make delegate->getModuleRegistry() return nullptr. - CHECK(m_flushedQueueJS) << "Attempting to use native methods without loading BatchedBridge.js"; + + if (m_flushedQueueJS) { callNativeModules(m_flushedQueueJS->callAsFunction({})); + return; + } + + // When a native module is called from JS, BatchedBridge.enqueueNativeCall() + // is invoked. For that to work, require('BatchedBridge') has to be called, + // and when that happens, __fbBatchedBridge is set as a side effect. + auto global = Object::getGlobalObject(m_context); + auto batchedBridgeValue = global.getProperty("__fbBatchedBridge"); + // So here, if __fbBatchedBridge doesn't exist, then we know no native calls + // have happened, and we were able to determine this without forcing + // BatchedBridge to be loaded as a side effect. + if (!batchedBridgeValue.isUndefined()) { + // If calls were made, we bind to the JS bridge methods, and use them to + // get the pending queue of native calls. + bindBridge(); + callNativeModules(m_flushedQueueJS->callAsFunction({})); + } else if (m_delegate) { + // If we have a delegate, we need to call it; we pass a null list to + // callNativeModules, since we know there are no native calls, without + // calling into JS again. If no calls were made and there's no delegate, + // nothing happens, which is correct. + callNativeModules(Value::makeNull(m_context)); } } @@ -483,9 +503,9 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m auto result = [&] { try { - // See flush() - CHECK(m_callFunctionReturnFlushedQueueJS) - << "Attempting to call native methods without loading BatchedBridge.js"; + if (!m_callFunctionReturnResultAndFlushedQueueJS) { + bindBridge(); + } return m_callFunctionReturnFlushedQueueJS->callAsFunction({ Value(m_context, String::createExpectingAscii(m_context, moduleId)), Value(m_context, String::createExpectingAscii(m_context, methodId)), @@ -504,6 +524,9 @@ void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& SystraceSection s("JSCExecutor::invokeCallback"); auto result = [&] { try { + if (!m_invokeCallbackAndReturnFlushedQueueJS) { + bindBridge(); + } return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({ Value::makeNumber(m_context, callbackId), Value::fromDynamic(m_context, std::move(arguments)) @@ -521,8 +544,9 @@ Value JSCExecutor::callFunctionSyncWithValue( const std::string& module, const std::string& method, Value args) { SystraceSection s("JSCExecutor::callFunction"); - // See flush() - CHECK(m_callFunctionReturnResultAndFlushedQueueJS); + if (!m_callFunctionReturnResultAndFlushedQueueJS) { + bindBridge(); + } Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({ Value(m_context, String::createExpectingAscii(m_context, module)), Value(m_context, String::createExpectingAscii(m_context, method)), diff --git a/ReactCommon/cxxreact/JSCExecutor.h b/ReactCommon/cxxreact/JSCExecutor.h index 3fbb03504a6f2b..c3a1449e05949f 100644 --- a/ReactCommon/cxxreact/JSCExecutor.h +++ b/ReactCommon/cxxreact/JSCExecutor.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -112,6 +113,7 @@ class RN_EXPORT JSCExecutor : public JSExecutor { std::unique_ptr m_unbundle; JSCNativeModules m_nativeModules; folly::dynamic m_jscConfig; + std::once_flag m_bindFlag; folly::Optional m_invokeCallbackAndReturnFlushedQueueJS; folly::Optional m_callFunctionReturnFlushedQueueJS; diff --git a/ReactCommon/jschelpers/Value.h b/ReactCommon/jschelpers/Value.h index b5d79c53df283f..ca8b86bf65f8e2 100644 --- a/ReactCommon/jschelpers/Value.h +++ b/ReactCommon/jschelpers/Value.h @@ -237,6 +237,13 @@ class Value : public noncopyable { Value(JSContextRef context, JSStringRef value); Value(Value&&); + Value& operator=(Value&& other) { + m_context = other.m_context; + m_value = other.m_value; + other.m_value = NULL; + return *this; + }; + operator JSValueRef() const { return m_value; } From 39544005650f886f8741e20e081fa91e4a9eb7b4 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 20 Mar 2017 16:22:24 -0700 Subject: [PATCH 110/366] Added MobileConfig for ReactNative use-fiber Reviewed By: yungsters Differential Revision: D4740267 fbshipit-source-id: d2cf76a22ce0f6337e1055b9f4b869c8bd82fa7d --- .../Renderer/src/renderers/native/ReactNativeFeatureFlags.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Libraries/Renderer/src/renderers/native/ReactNativeFeatureFlags.js b/Libraries/Renderer/src/renderers/native/ReactNativeFeatureFlags.js index 1686eec51aed2f..1224b86f7b74b9 100644 --- a/Libraries/Renderer/src/renderers/native/ReactNativeFeatureFlags.js +++ b/Libraries/Renderer/src/renderers/native/ReactNativeFeatureFlags.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactNativeFeatureFlags + * @flow */ 'use strict'; From a4300dab6726aa0588ab10476164846b10794f2f Mon Sep 17 00:00:00 2001 From: "Andrew Y. Chen" Date: Mon, 20 Mar 2017 17:22:50 -0700 Subject: [PATCH 111/366] Move idle detection classes to its own directory Reviewed By: AaaChiuuu Differential Revision: D4738755 fbshipit-source-id: df3b215991a45932283f6ba9d800aeff1c31d2e6 --- .../java/com/facebook/react/testing/BUCK | 6 +++++- .../testing/ReactAppInstrumentationTestCase.java | 2 +- .../react/testing/ReactAppTestActivity.java | 2 ++ .../react/testing/ReactIntegrationTestCase.java | 2 ++ .../react/testing/SingleTouchGestureGenerator.java | 2 ++ .../com/facebook/react/testing/idledetection/BUCK | 14 ++++++++++++++ .../testing/{ => idledetection}/IdleWaiter.java | 2 +- .../ReactBridgeIdleSignaler.java | 2 +- .../ReactIdleDetectionUtil.java | 2 +- .../androidTest/java/com/facebook/react/tests/BUCK | 1 + 10 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{ => idledetection}/IdleWaiter.java (90%) rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{ => idledetection}/ReactBridgeIdleSignaler.java (97%) rename ReactAndroid/src/androidTest/java/com/facebook/react/testing/{ => idledetection}/ReactIdleDetectionUtil.java (98%) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK index 5cf6affe9a6fab..6814e3aa316138 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK @@ -2,7 +2,10 @@ include_defs("//ReactAndroid/DEFS") android_library( name = "testing", - srcs = glob(["**/*.java"]), + srcs = glob( + ["**/*.java"], + excludes = ["idledetection/**/*.java"], + ), visibility = [ "PUBLIC", ], @@ -25,5 +28,6 @@ android_library( react_native_target("java/com/facebook/react/modules/debug:interfaces"), react_native_target("java/com/facebook/react/shell:shell"), react_native_target("java/com/facebook/react/uimanager:uimanager"), + react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), ], ) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java index aaba651def949c..eeb8c02e06715d 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java @@ -20,6 +20,7 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.testing.idledetection.IdleWaiter; /** * Base class for instrumentation tests that runs React based react application in UI mode @@ -123,7 +124,6 @@ public void run() { } }; - getActivity().runOnUiThread(getScreenshotRunnable); try { if (!latch.await(5000, TimeUnit.MILLISECONDS)) { diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java index 022e7826c1fe83..11edda17a429b0 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java @@ -28,6 +28,8 @@ import com.facebook.react.common.LifecycleState; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.shell.MainReactPackage; +import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; +import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.react.uimanager.UIImplementationProvider; public class ReactAppTestActivity extends FragmentActivity implements diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java index 752c89ea618161..77f5492c36cafd 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java @@ -32,6 +32,8 @@ import com.facebook.react.common.futures.SimpleSettableFuture; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.modules.core.Timing; +import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; +import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.soloader.SoLoader; import static org.mockito.Mockito.mock; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java index 278facbcb280b4..f0ae877a953d42 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/SingleTouchGestureGenerator.java @@ -13,6 +13,8 @@ import android.view.View; import android.view.ViewConfiguration; +import com.facebook.react.testing.idledetection.IdleWaiter; + /** * Provides methods for generating touch events and dispatching them directly to a given view. * Events scenarios are based on {@link android.test.TouchUtils} but they get gets dispatched diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK new file mode 100644 index 00000000000000..f6b33535737d3f --- /dev/null +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK @@ -0,0 +1,14 @@ +include_defs("//ReactAndroid/DEFS") + +android_library( + name = "idledetection", + srcs = glob(["**/*.java"]), + visibility = [ + "PUBLIC", + ], + deps = [ + react_native_dep("third-party/java/testing-support-lib:runner"), + react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/modules/core:core"), + ], +) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java similarity index 90% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java index 98884c1033a375..1b94b7c1fc88db 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/IdleWaiter.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/IdleWaiter.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing; +package com.facebook.react.testing.idledetection; /** * Interface for something that knows how to wait for bridge and UI idle. diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java similarity index 97% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java index ffd941f9a228e6..4aaa451e43ab24 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactBridgeIdleSignaler.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactBridgeIdleSignaler.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing; +package com.facebook.react.testing.idledetection; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java similarity index 98% rename from ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java rename to ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java index 49e2219327b96f..af6ca2ebb82a55 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIdleDetectionUtil.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.facebook.react.testing; +package com.facebook.react.testing.idledetection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 96872ba647b382..992351f62026aa 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -5,6 +5,7 @@ deps = [ react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), react_native_integration_tests_target("java/com/facebook/react/testing:testing"), + react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), From b3be5743e99f01acdf628c48d3a6f9126aca2c5b Mon Sep 17 00:00:00 2001 From: Ben Roth Date: Tue, 21 Mar 2017 04:03:00 -0700 Subject: [PATCH 112/366] Fix property accessor warning in RCTDevSettings::websocketExecutorName Summary: Motivation: Fixes Xcode warning `Ivar '_websocketExecutorName' which backs the property is not referenced in this property's accessor` which shows up because this property has no setter (and is never set anywhere). Closes https://github.com/facebook/react-native/pull/12639 Differential Revision: D4745437 Pulled By: javache fbshipit-source-id: 3ab4b0df62f90adc2b62d891197dc783e07da4e3 --- React/Modules/RCTDevSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/Modules/RCTDevSettings.h b/React/Modules/RCTDevSettings.h index 6fb6fb4652f936..d8ffd7c9095de7 100644 --- a/React/Modules/RCTDevSettings.h +++ b/React/Modules/RCTDevSettings.h @@ -46,7 +46,7 @@ * Alternate name for the websocket executor, if not the generic term "remote". * TODO t16297016: this seems to be unused, remove? */ -@property (nonatomic, copy) NSString *websocketExecutorName; +@property (nonatomic, readonly) NSString *websocketExecutorName; /* * Whether shaking will show RCTDevMenu. The menu is enabled by default if RCT_DEV=1, but From 2f69c5f46cad550692f41000f7fa6f32efc72745 Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Tue, 21 Mar 2017 05:05:54 -0700 Subject: [PATCH 113/366] exit out early and continue if no annotations are found Differential Revision: D4742299 fbshipit-source-id: 8006c5c9b25c951aec12ad5c63fdaf03fe1f6e67 --- .../react/module/processing/ReactModuleSpecProcessor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java b/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java index add1ef0b6ef904..d7c419cda8a852 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java +++ b/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java @@ -80,11 +80,15 @@ public boolean process(Set annotations, RoundEnvironment ReactModuleList.class); for (Element reactModuleListElement : reactModuleListElements) { TypeElement typeElement = (TypeElement) reactModuleListElement; + ReactModuleList reactModuleList = typeElement.getAnnotation(ReactModuleList.class); + if (reactModuleList == null) { + continue; + } + ClassName className = ClassName.get(typeElement); String packageName = ClassName.get(typeElement).packageName(); String fileName = className.simpleName(); - ReactModuleList reactModuleList = typeElement.getAnnotation(ReactModuleList.class); List nativeModules = new ArrayList<>(); try { reactModuleList.nativeModules(); // throws MirroredTypesException From 80e1dbf69227af7a16932a7f443e116799811a3b Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Tue, 21 Mar 2017 05:35:57 -0700 Subject: [PATCH 114/366] require `fbjs/lib/invariant`, not `invariant` Summary: `invariant` is only available in open source because we install it as a transitive dependency into node_modules Reviewed By: jeanlauliac Differential Revision: D4745582 fbshipit-source-id: 27c49b576254c8d1d667dea7097d16cdd1205daf --- Libraries/Utilities/DeviceInfo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Utilities/DeviceInfo.js b/Libraries/Utilities/DeviceInfo.js index d398f808312459..819916b153f84a 100644 --- a/Libraries/Utilities/DeviceInfo.js +++ b/Libraries/Utilities/DeviceInfo.js @@ -13,7 +13,7 @@ const DeviceInfo = require('NativeModules').DeviceInfo; -const invariant = require('invariant'); +const invariant = require('fbjs/lib/invariant'); invariant(DeviceInfo, 'DeviceInfo native module is not installed correctly'); From 5c128ad0491808b8aa22828ee14fcc1672201995 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 21 Mar 2017 05:54:03 -0700 Subject: [PATCH 115/366] packager: GlobalTransformCache: add unit test Summary: Finally adding some unit test to increase confidence in the correctness of that piece of code. Reviewed By: davidaurelio Differential Revision: D4721543 fbshipit-source-id: 56776290d61f2b51c69d7eeae09663e3bc892b50 --- .../__tests__/GlobalTransformCache-test.js | 91 +++++++++++++++++++ .../GlobalTransformCache-test.js.snap | 32 +++++++ 2 files changed, 123 insertions(+) create mode 100644 packager/src/lib/__tests__/GlobalTransformCache-test.js create mode 100644 packager/src/lib/__tests__/__snapshots__/GlobalTransformCache-test.js.snap diff --git a/packager/src/lib/__tests__/GlobalTransformCache-test.js b/packager/src/lib/__tests__/GlobalTransformCache-test.js new file mode 100644 index 00000000000000..1900ed85f884ae --- /dev/null +++ b/packager/src/lib/__tests__/GlobalTransformCache-test.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2016-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. + */ + +'use strict'; + +jest.disableAutomock(); +jest.useRealTimers(); + +const fetchMock = jest.fn(); +jest.mock('node-fetch', () => fetchMock); + +const GlobalTransformCache = require('../GlobalTransformCache'); +const FetchError = require('node-fetch/lib/fetch-error'); + +async function fetchResultURIs(keys: Array): Promise> { + return new Map(keys.map(key => [key, `http://globalcache.com/${key}`])); +} + +async function fetchResultFromURI(uri: string): Promise { + return { + code: `/* code from ${uri} */`, + dependencies: [], + dependencyOffsets: [], + }; +} + +describe('GlobalTransformCache', () => { + + it('fetches results', async () => { + const cache = new GlobalTransformCache(fetchResultURIs, fetchResultFromURI, null, [ + {dev: true, minify: false, platform: 'ios'}, + ]); + const transformOptions = { + dev: true, + minify: false, + platform: 'ios', + transform: {projectRoots: [__dirname]}, + }; + const result = await Promise.all([cache.fetch({ + filePath: 'foo.js', + sourceCode: '/* beep */', + getTransformCacheKey: () => 'abcd', + transformOptions, + }), cache.fetch({ + filePath: 'bar.js', + sourceCode: '/* boop */', + getTransformCacheKey: () => 'abcd', + transformOptions, + })]); + expect(result).toMatchSnapshot(); + }); + + describe('fetchResultFromURI', () => { + + const defaultFetchMockImpl = async uri => ({ + status: 200, + json: async () => ({ + code: `/* code from ${uri} */`, + dependencies: [], + dependencyOffsets: [], + }), + }); + + beforeEach(() => { + fetchMock.mockReset(); + }); + + it('fetches result', async () => { + fetchMock.mockImplementation(defaultFetchMockImpl); + const result = await GlobalTransformCache.fetchResultFromURI('http://globalcache.com/foo'); + expect(result).toMatchSnapshot(); + }); + + it('retries once on timeout', async () => { + fetchMock.mockImplementation(async uri => { + fetchMock.mockImplementation(defaultFetchMockImpl); + throw new FetchError('timeout!', 'request-timeout'); + }); + const result = await GlobalTransformCache.fetchResultFromURI('http://globalcache.com/foo'); + expect(result).toMatchSnapshot(); + }); + + }); + +}); diff --git a/packager/src/lib/__tests__/__snapshots__/GlobalTransformCache-test.js.snap b/packager/src/lib/__tests__/__snapshots__/GlobalTransformCache-test.js.snap new file mode 100644 index 00000000000000..c0fdf1ad5bb838 --- /dev/null +++ b/packager/src/lib/__tests__/__snapshots__/GlobalTransformCache-test.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GlobalTransformCache fetchResultFromURI fetches result 1`] = ` +Object { + "code": "/* code from http://globalcache.com/foo */", + "dependencies": Array [], + "dependencyOffsets": Array [], +} +`; + +exports[`GlobalTransformCache fetchResultFromURI retries once on timeout 1`] = ` +Object { + "code": "/* code from http://globalcache.com/foo */", + "dependencies": Array [], + "dependencyOffsets": Array [], +} +`; + +exports[`GlobalTransformCache fetches results 1`] = ` +Array [ + Object { + "code": "/* code from http://globalcache.com/2ad175cb80ae79fd33b914bfb392fb6742982d2a-foo.js */", + "dependencies": Array [], + "dependencyOffsets": Array [], + }, + Object { + "code": "/* code from http://globalcache.com/d6c0a1a4199d572ab68b36c07d0d68607eebb131-bar.js */", + "dependencies": Array [], + "dependencyOffsets": Array [], + }, +] +`; From 4cbb64521a9a3fbbef9cf741314060ef71f07357 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 21 Mar 2017 05:54:06 -0700 Subject: [PATCH 116/366] packager: GlobalTransformCache: ignore errors related to fetching Reviewed By: davidaurelio Differential Revision: D4745584 fbshipit-source-id: 2c9b2451d3525c90308fb88784945462cd827d1f --- packager/src/lib/GlobalTransformCache.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packager/src/lib/GlobalTransformCache.js b/packager/src/lib/GlobalTransformCache.js index b74a1981cc2330..79a169ed26c2a0 100644 --- a/packager/src/lib/GlobalTransformCache.js +++ b/packager/src/lib/GlobalTransformCache.js @@ -145,6 +145,13 @@ class TransformProfileSet { } } +class FetchFailedError extends Error { + constructor(message) { + super(); + this.message = message; + } +} + /** * For some reason the result stored by the server for a key might mismatch what * we expect a result to be. So we need to verify carefully the data. @@ -171,6 +178,8 @@ class GlobalTransformCache { _profileSet: TransformProfileSet; _store: ?KeyResultStore; + static FetchFailedError; + /** * For using the global cache one needs to have some kind of central key-value * store that gets prefilled using keyOf() and the transformed results. The @@ -214,12 +223,13 @@ class GlobalTransformCache { static async _fetchResultFromURI(uri: string): Promise { const response = await fetch(uri, {method: 'GET', timeout: 8000}); if (response.status !== 200) { - throw new Error(`Unexpected HTTP status: ${response.status} ${response.statusText} `); + const msg = `Unexpected HTTP status: ${response.status} ${response.statusText} `; + throw new FetchFailedError(msg); } const unvalidatedResult = await response.json(); const result = validateCachedResult(unvalidatedResult); if (result == null) { - throw new Error('Server returned invalid result.'); + throw new FetchFailedError('Server returned invalid result.'); } return result; } @@ -260,4 +270,6 @@ class GlobalTransformCache { } +GlobalTransformCache.FetchFailedError = FetchFailedError; + module.exports = GlobalTransformCache; From 439cb76a005fbffd23be6c5b6a61c7806f1b716d Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 21 Mar 2017 05:54:08 -0700 Subject: [PATCH 117/366] packager: GlobalTransformCache: reduce asynchronicity for non-cached bundles Reviewed By: davidaurelio Differential Revision: D4745595 fbshipit-source-id: 3126fdcc11dd1c8085316457ead0cabae633d0db --- packager/src/lib/GlobalTransformCache.js | 7 ++++--- packager/src/node-haste/Module.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packager/src/lib/GlobalTransformCache.js b/packager/src/lib/GlobalTransformCache.js index 79a169ed26c2a0..31d27cef26128c 100644 --- a/packager/src/lib/GlobalTransformCache.js +++ b/packager/src/lib/GlobalTransformCache.js @@ -247,14 +247,15 @@ class GlobalTransformCache { }); } + shouldFetch(props: FetchProps): boolean { + return this._profileSet.has(props.transformOptions); + } + /** * This may return `null` if either the cache doesn't have a value for that * key yet, or an error happened, processed separately. */ async fetch(props: FetchProps): Promise { - if (!this._profileSet.has(props.transformOptions)) { - return null; - } const uri = await this._fetcher.fetch(GlobalTransformCache.keyOf(props)); if (uri == null) { return null; diff --git a/packager/src/node-haste/Module.js b/packager/src/node-haste/Module.js index 0f1892ca56a231..5b89683ecfa7a5 100644 --- a/packager/src/node-haste/Module.js +++ b/packager/src/node-haste/Module.js @@ -303,7 +303,7 @@ class Module { callback: (error: ?Error, result: ?TransformedCode) => void, ) { const {_globalCache} = this; - if (_globalCache == null) { + if (_globalCache == null || !_globalCache.shouldFetch(cacheProps)) { this._transformCodeForCallback(cacheProps, callback); return; } From a34956f2fb51a95096a519feaae12a0b9db12fb1 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Tue, 21 Mar 2017 07:49:54 -0700 Subject: [PATCH 118/366] Remove `copyProperties` Summary: remove `copyProperties` module, and replace the functionality with `Object.assign` Reviewed By: javache Differential Revision: D4745771 fbshipit-source-id: 2440620757e7539dbd7fd39f5920ac0b5b4183c5 --- Libraries/EventEmitter/EventValidator.js | 4 +- Libraries/EventEmitter/mixInEventEmitter.js | 7 ++- Libraries/vendor/core/copyProperties.js | 48 --------------------- 3 files changed, 4 insertions(+), 55 deletions(-) delete mode 100644 Libraries/vendor/core/copyProperties.js diff --git a/Libraries/EventEmitter/EventValidator.js b/Libraries/EventEmitter/EventValidator.js index 26fda76dfab7f5..5fc234b2263ab4 100644 --- a/Libraries/EventEmitter/EventValidator.js +++ b/Libraries/EventEmitter/EventValidator.js @@ -11,8 +11,6 @@ */ 'use strict'; -const copyProperties = require('copyProperties'); - /** * EventValidator is designed to validate event types to make it easier to catch * common mistakes. It accepts a map of all of the different types of events @@ -37,7 +35,7 @@ const EventValidator = { const eventTypes = Object.keys(types); const emitterWithValidation = Object.create(emitter); - copyProperties(emitterWithValidation, { + Object.assign(emitterWithValidation, { emit: function emit(type, a, b, c, d, e, _) { assertAllowsEventType(type, eventTypes); return emitter.emit.call(this, type, a, b, c, d, e, _); diff --git a/Libraries/EventEmitter/mixInEventEmitter.js b/Libraries/EventEmitter/mixInEventEmitter.js index 2e2f47e025488f..27b6726ed6b52d 100644 --- a/Libraries/EventEmitter/mixInEventEmitter.js +++ b/Libraries/EventEmitter/mixInEventEmitter.js @@ -16,7 +16,6 @@ const EventEmitterWithHolding = require('EventEmitterWithHolding'); const EventHolder = require('EventHolder'); const EventValidator = require('EventValidator'); -const copyProperties = require('copyProperties'); const invariant = require('fbjs/lib/invariant'); const keyOf = require('fbjs/lib/keyOf'); @@ -63,13 +62,13 @@ function mixInEventEmitter(cls: Function | Object, types: Object) { // Keep track of the provided types, union the types if they already exist, // which allows for prototype subclasses to provide more types. if (target.hasOwnProperty(TYPES_KEY)) { - copyProperties(target.__types, types); + Object.assign(target.__types, types); } else if (target.__types) { - target.__types = copyProperties({}, target.__types, types); + target.__types = Object.assign({}, target.__types, types); } else { target.__types = types; } - copyProperties(target, EventEmitterMixin); + Object.assign(target, EventEmitterMixin); } const EventEmitterMixin = { diff --git a/Libraries/vendor/core/copyProperties.js b/Libraries/vendor/core/copyProperties.js deleted file mode 100644 index d98ed905580bd0..00000000000000 --- a/Libraries/vendor/core/copyProperties.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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. - * - * @providesModule copyProperties - */ -'use strict'; - -/** - * Copy properties from one or more objects (up to 5) into the first object. - * This is a shallow copy. It mutates the first object and also returns it. - * - * NOTE: `arguments` has a very significant performance penalty, which is why - * we don't support unlimited arguments. - */ -function copyProperties(obj, a, b, c, d, e, f) { - obj = obj || {}; - - if (__DEV__) { - if (f) { - throw new Error('Too many arguments passed to copyProperties'); - } - } - - var args = [a, b, c, d, e]; - var ii = 0, v; - while (args[ii]) { - v = args[ii++]; - for (var k in v) { - obj[k] = v[k]; - } - - // IE ignores toString in object iteration.. See: - // webreflection.blogspot.com/2007/07/quick-fix-internet-explorer-and.html - if (v.hasOwnProperty && v.hasOwnProperty('toString') && - (typeof v.toString !== 'undefined') && (obj.toString !== v.toString)) { - obj.toString = v.toString; - } - } - - return obj; -} - -module.exports = copyProperties; From 2b4762f18469a12f4056e18b5e6b76b2babb5579 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Tue, 21 Mar 2017 09:52:31 -0700 Subject: [PATCH 119/366] Only use `EventValidator` in development mode Summary: Only pulls in `EventValidator` for development mode, as warnings about invalid events are pointless in production builds. Reviewed By: javache Differential Revision: D4745852 fbshipit-source-id: dbab1026df35d54a82e1e620fac08304c58fbeae --- Libraries/EventEmitter/mixInEventEmitter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Libraries/EventEmitter/mixInEventEmitter.js b/Libraries/EventEmitter/mixInEventEmitter.js index 27b6726ed6b52d..d5db027bb630bc 100644 --- a/Libraries/EventEmitter/mixInEventEmitter.js +++ b/Libraries/EventEmitter/mixInEventEmitter.js @@ -14,7 +14,6 @@ const EventEmitter = require('EventEmitter'); const EventEmitterWithHolding = require('EventEmitterWithHolding'); const EventHolder = require('EventHolder'); -const EventValidator = require('EventValidator'); const invariant = require('fbjs/lib/invariant'); const keyOf = require('fbjs/lib/keyOf'); @@ -119,7 +118,10 @@ const EventEmitterMixin = { __getEventEmitter: function() { if (!this.__eventEmitter) { let emitter = new EventEmitter(); - emitter = EventValidator.addValidation(emitter, this.__types); + if (__DEV__) { + const EventValidator = require('EventValidator'); + emitter = EventValidator.addValidation(emitter, this.__types); + } const holder = new EventHolder(); this.__eventEmitter = new EventEmitterWithHolding(emitter, holder); From 4797701b66a1e1b6eef4f283703b4245de52e17b Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Tue, 21 Mar 2017 11:32:14 -0700 Subject: [PATCH 120/366] packager: clear cache Reviewed By: davidaurelio Differential Revision: D4746429 fbshipit-source-id: 1a49a4b6db75658749346f0fd94dad68ff084203 --- packager/src/node-haste/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packager/src/node-haste/index.js b/packager/src/node-haste/index.js index fef220c58b473b..0bf17a9115e192 100644 --- a/packager/src/node-haste/index.js +++ b/packager/src/node-haste/index.js @@ -71,6 +71,8 @@ type Options = { watch: boolean, }; +const JEST_HASTE_MAP_CACHE_BREAKER = 1; + class DependencyGraph extends EventEmitter { _opts: Options; @@ -104,7 +106,7 @@ class DependencyGraph extends EventEmitter { ignorePattern: {test: opts.ignoreFilePath}, maxWorkers: opts.maxWorkerCount, mocksPattern: '', - name: 'react-native-packager', + name: 'react-native-packager-' + JEST_HASTE_MAP_CACHE_BREAKER, platforms: Array.from(opts.platforms), providesModuleNodeModules: opts.providesModuleNodeModules, resetCache: opts.resetCache, From 314ec8726960e381371d2ce48728c41f5fc51f39 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Tue, 21 Mar 2017 12:37:10 -0700 Subject: [PATCH 121/366] Remove unused CxxMessageQueue Differential Revision: D4713064 fbshipit-source-id: 511b782279b89076228f00290e78ed155e2e723e --- React/ReactCxx.xcodeproj/project.pbxproj | 16 - ReactCommon/cxxreact/Android.mk | 1 - ReactCommon/cxxreact/BUCK | 1 - ReactCommon/cxxreact/CxxMessageQueue.cpp | 320 ------------------ ReactCommon/cxxreact/CxxMessageQueue.h | 83 ----- ReactCommon/cxxreact/Instance.cpp | 1 - ReactCommon/cxxreact/tests/BUCK | 1 - .../cxxreact/tests/CxxMessageQueueTest.cpp | 188 ---------- 8 files changed, 611 deletions(-) delete mode 100644 ReactCommon/cxxreact/CxxMessageQueue.cpp delete mode 100644 ReactCommon/cxxreact/CxxMessageQueue.h delete mode 100644 ReactCommon/cxxreact/tests/CxxMessageQueueTest.cpp diff --git a/React/ReactCxx.xcodeproj/project.pbxproj b/React/ReactCxx.xcodeproj/project.pbxproj index cef7f0d506bb2b..c375179209e0dc 100644 --- a/React/ReactCxx.xcodeproj/project.pbxproj +++ b/React/ReactCxx.xcodeproj/project.pbxproj @@ -172,7 +172,6 @@ 13F887581E2971D400C3C7A1 /* Demangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F887521E2971C500C3C7A1 /* Demangle.cpp */; }; 13F887591E2971D400C3C7A1 /* StringBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F887531E2971C500C3C7A1 /* StringBase.cpp */; }; 13F8875A1E2971D400C3C7A1 /* Unicode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F887541E2971C500C3C7A1 /* Unicode.cpp */; }; - 13F8876D1E29726200C3C7A1 /* CxxMessageQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0A51E03699D0018521A /* CxxMessageQueue.cpp */; }; 13F8876E1E29726200C3C7A1 /* CxxNativeModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0A81E03699D0018521A /* CxxNativeModule.cpp */; }; 13F887701E29726200C3C7A1 /* Instance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0AE1E03699D0018521A /* Instance.cpp */; }; 13F887711E29726200C3C7A1 /* JSBundleType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */; }; @@ -191,7 +190,6 @@ 13F8877E1E29726200C3C7A1 /* NativeToJsBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0CF1E03699D0018521A /* NativeToJsBridge.cpp */; }; 13F8877F1E29726200C3C7A1 /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0D11E03699D0018521A /* Platform.cpp */; }; 13F887801E29726200C3C7A1 /* SampleCxxModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0D31E03699D0018521A /* SampleCxxModule.cpp */; }; - 13F887811E29726300C3C7A1 /* CxxMessageQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0A51E03699D0018521A /* CxxMessageQueue.cpp */; }; 13F887821E29726300C3C7A1 /* CxxNativeModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0A81E03699D0018521A /* CxxNativeModule.cpp */; }; 13F887841E29726300C3C7A1 /* Instance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0AE1E03699D0018521A /* Instance.cpp */; }; 13F887851E29726300C3C7A1 /* JSCExecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D92B0B21E03699D0018521A /* JSCExecutor.cpp */; }; @@ -229,7 +227,6 @@ 191E3EBE1C29D9AF00C180A6 /* RCTRefreshControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */; }; 191E3EC11C29DC3800C180A6 /* RCTRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */; }; 19DED2291E77E29200F089BB /* systemJSCWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */; }; - 27595AA31E575C7800CCE2B1 /* CxxMessageQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */; }; 27595AA41E575C7800CCE2B1 /* CxxModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A71E03699D0018521A /* CxxModule.h */; }; 27595AA51E575C7800CCE2B1 /* CxxNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A91E03699D0018521A /* CxxNativeModule.h */; }; 27595AA61E575C7800CCE2B1 /* Executor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0AB1E03699D0018521A /* Executor.h */; }; @@ -256,7 +253,6 @@ 27595ABB1E575C7800CCE2B1 /* Platform.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0D21E03699D0018521A /* Platform.h */; }; 27595ABC1E575C7800CCE2B1 /* SampleCxxModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0D41E03699D0018521A /* SampleCxxModule.h */; }; 27595ABD1E575C7800CCE2B1 /* SystraceSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0D51E03699D0018521A /* SystraceSection.h */; }; - 27595ABE1E575C7800CCE2B1 /* CxxMessageQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */; }; 27595ABF1E575C7800CCE2B1 /* CxxModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A71E03699D0018521A /* CxxModule.h */; }; 27595AC01E575C7800CCE2B1 /* CxxNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A91E03699D0018521A /* CxxNativeModule.h */; }; 27595AC11E575C7800CCE2B1 /* Executor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0AB1E03699D0018521A /* Executor.h */; }; @@ -744,7 +740,6 @@ 3D8ED92C1E5B120100D83D20 /* libcxxreact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D3CD9321DE5FBEE00167DC4 /* libcxxreact.a */; }; 3D8ED92D1E5B120100D83D20 /* libyoga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D3C06751DE3340C00C268FA /* libyoga.a */; }; 3DA9819E1E5B0DBB004F2374 /* NSDataBigString.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D7454B31E54786200E74ADD /* NSDataBigString.h */; }; - 3DA9819F1E5B0E34004F2374 /* CxxMessageQueue.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */; }; 3DA981A01E5B0E34004F2374 /* CxxModule.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A71E03699D0018521A /* CxxModule.h */; }; 3DA981A11E5B0E34004F2374 /* CxxNativeModule.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A91E03699D0018521A /* CxxNativeModule.h */; }; 3DA981A21E5B0E34004F2374 /* Executor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0AB1E03699D0018521A /* Executor.h */; }; @@ -891,7 +886,6 @@ 3DA982361E5B0F7F004F2374 /* RCTWrapperViewController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B080231A694A8400A75B9A /* RCTWrapperViewController.h */; }; 3DA982381E5B0F7F004F2374 /* UIView+React.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13E067531A70F44B002CDEE1 /* UIView+React.h */; }; 3DA982391E5B0F8A004F2374 /* UIView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F15A171B7CC46900F10295 /* UIView+Private.h */; }; - 3DA9823A1E5B1053004F2374 /* CxxMessageQueue.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */; }; 3DA9823B1E5B1053004F2374 /* CxxModule.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A71E03699D0018521A /* CxxModule.h */; }; 3DA9823C1E5B1053004F2374 /* CxxNativeModule.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0A91E03699D0018521A /* CxxNativeModule.h */; }; 3DA9823D1E5B1053004F2374 /* Executor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D92B0AB1E03699D0018521A /* Executor.h */; }; @@ -1201,7 +1195,6 @@ dstPath = include/cxxreact; dstSubfolderSpec = 16; files = ( - 3DA9823A1E5B1053004F2374 /* CxxMessageQueue.h in Copy Headers */, 3DA9823B1E5B1053004F2374 /* CxxModule.h in Copy Headers */, 3DA9823C1E5B1053004F2374 /* CxxNativeModule.h in Copy Headers */, 3DA9823D1E5B1053004F2374 /* Executor.h in Copy Headers */, @@ -1400,7 +1393,6 @@ dstPath = include/cxxreact; dstSubfolderSpec = 16; files = ( - 3DA9819F1E5B0E34004F2374 /* CxxMessageQueue.h in Copy Headers */, 3DA981A01E5B0E34004F2374 /* CxxModule.h in Copy Headers */, 3DA981A11E5B0E34004F2374 /* CxxNativeModule.h in Copy Headers */, 3DA981A21E5B0E34004F2374 /* Executor.h in Copy Headers */, @@ -1720,8 +1712,6 @@ 3D7A27DE1DE32541002E3F95 /* JSCWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCWrapper.h; sourceTree = ""; }; 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCErrorHandling.mm; sourceTree = ""; }; 3D7AA9C31E548CD5001955CF /* NSDataBigString.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NSDataBigString.mm; sourceTree = ""; }; - 3D92B0A51E03699D0018521A /* CxxMessageQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CxxMessageQueue.cpp; sourceTree = ""; }; - 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CxxMessageQueue.h; sourceTree = ""; }; 3D92B0A71E03699D0018521A /* CxxModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CxxModule.h; sourceTree = ""; }; 3D92B0A81E03699D0018521A /* CxxNativeModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CxxNativeModule.cpp; sourceTree = ""; }; 3D92B0A91E03699D0018521A /* CxxNativeModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CxxNativeModule.h; sourceTree = ""; }; @@ -2416,8 +2406,6 @@ AC70D2EA1DE489FC002E6351 /* cxxreact */ = { isa = PBXGroup; children = ( - 3D92B0A51E03699D0018521A /* CxxMessageQueue.cpp */, - 3D92B0A61E03699D0018521A /* CxxMessageQueue.h */, 3D92B0A71E03699D0018521A /* CxxModule.h */, 3D92B0A81E03699D0018521A /* CxxNativeModule.cpp */, 3D92B0A91E03699D0018521A /* CxxNativeModule.h */, @@ -2660,7 +2648,6 @@ 3D3030221DF8294C00D6DDAE /* JSBundleType.h in Headers */, 27595ACA1E575C7800CCE2B1 /* JSCMemory.h in Headers */, 3D74547D1E54758900E74ADD /* JSBigString.h in Headers */, - 27595ABE1E575C7800CCE2B1 /* CxxMessageQueue.h in Headers */, 27595AC71E575C7800CCE2B1 /* JSCExecutor.h in Headers */, 27595ACD1E575C7800CCE2B1 /* JSCSamplingProfiler.h in Headers */, 27595ABF1E575C7800CCE2B1 /* CxxModule.h in Headers */, @@ -2732,7 +2719,6 @@ 3D3CD9471DE5FC7800167DC4 /* oss-compat-util.h in Headers */, 27595AAF1E575C7800CCE2B1 /* JSCMemory.h in Headers */, 3D74547C1E54758900E74ADD /* JSBigString.h in Headers */, - 27595AA31E575C7800CCE2B1 /* CxxMessageQueue.h in Headers */, 27595AAC1E575C7800CCE2B1 /* JSCExecutor.h in Headers */, 27595AB21E575C7800CCE2B1 /* JSCSamplingProfiler.h in Headers */, 27595AA41E575C7800CCE2B1 /* CxxModule.h in Headers */, @@ -3436,7 +3422,6 @@ 13F8877F1E29726200C3C7A1 /* Platform.cpp in Sources */, 13F887701E29726200C3C7A1 /* Instance.cpp in Sources */, 13F8877E1E29726200C3C7A1 /* NativeToJsBridge.cpp in Sources */, - 13F8876D1E29726200C3C7A1 /* CxxMessageQueue.cpp in Sources */, 13F887761E29726200C3C7A1 /* JSCNativeModules.cpp in Sources */, 13F887801E29726200C3C7A1 /* SampleCxxModule.cpp in Sources */, ); @@ -3460,7 +3445,6 @@ 3D80D9181DF6F7A80028D040 /* JSBundleType.cpp in Sources */, 13F8878F1E29726300C3C7A1 /* MethodCall.cpp in Sources */, 13F887921E29726300C3C7A1 /* Platform.cpp in Sources */, - 13F887811E29726300C3C7A1 /* CxxMessageQueue.cpp in Sources */, 13F887911E29726300C3C7A1 /* NativeToJsBridge.cpp in Sources */, 13F887821E29726300C3C7A1 /* CxxNativeModule.cpp in Sources */, 13F887891E29726300C3C7A1 /* JSCNativeModules.cpp in Sources */, diff --git a/ReactCommon/cxxreact/Android.mk b/ReactCommon/cxxreact/Android.mk index 6f33a13ef738f8..f9cdb1976ab87e 100644 --- a/ReactCommon/cxxreact/Android.mk +++ b/ReactCommon/cxxreact/Android.mk @@ -5,7 +5,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := libreactnativefb LOCAL_SRC_FILES := \ - CxxMessageQueue.cpp \ CxxNativeModule.cpp \ Instance.cpp \ JSCExecutor.cpp \ diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index 8e94880e254625..49b2b54e5eca75 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -127,7 +127,6 @@ cxx_library( ) CXXREACT_PUBLIC_HEADERS = [ - "CxxMessageQueue.h", "CxxNativeModule.h", "Executor.h", "ExecutorToken.h", diff --git a/ReactCommon/cxxreact/CxxMessageQueue.cpp b/ReactCommon/cxxreact/CxxMessageQueue.cpp deleted file mode 100644 index e83a3c90a42aa4..00000000000000 --- a/ReactCommon/cxxreact/CxxMessageQueue.cpp +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "CxxMessageQueue.h" - -#include - -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -using detail::BinarySemaphore; -using detail::EventFlag; - -using clock = std::chrono::steady_clock; -using time_point = clock::time_point; -static_assert(std::is_same::value, ""); - -namespace { -time_point now() { - return clock::now(); -} - -class Task { - public: - static Task* create(std::function&& func) { - return new Task{std::move(func), false, time_point()}; - } - - static Task* createSync(std::function&& func) { - return new Task{std::move(func), true, time_point()}; - } - - static Task* createDelayed(std::function&& func, time_point startTime) { - return new Task{std::move(func), false, startTime}; - } - - std::function func; - // This flag is just to mark that the task is expected to be synchronous. If - // a synchronous task races with stopping the queue, the thread waiting on - // the synchronous task might never resume. We use this flag to detect this - // case and throw an error. - bool sync; - time_point startTime; - - folly::AtomicIntrusiveLinkedListHook hook; - - // Should this sort consider id also? - struct Compare { - bool operator()(const Task* a, const Task* b) { - return a->startTime > b->startTime; - } - }; -}; - -class DelayedTaskQueue { - public: - ~DelayedTaskQueue() { - while (!queue_.empty()) { - delete queue_.top(); - queue_.pop(); - } - } - - void process() { - while (!queue_.empty()) { - Task* d = queue_.top(); - if (now() < d->startTime) { - break; - } - auto owned = std::unique_ptr(queue_.top()); - queue_.pop(); - owned->func(); - } - } - - void push(Task* t) { - queue_.push(t); - } - - bool empty() { - return queue_.empty(); - } - - time_point nextTime() { - return queue_.top()->startTime; - } - private: - std::priority_queue, Task::Compare> queue_; -}; - -} - -class CxxMessageQueue::QueueRunner { - public: - ~QueueRunner() { - queue_.sweep([] (Task* t) { - delete t; - }); - } - - void enqueue(std::function&& func) { - enqueueTask(Task::create(std::move(func))); - } - - void enqueueDelayed(std::function&& func, uint64_t delayMs) { - if (delayMs) { - enqueueTask(Task::createDelayed(std::move(func), now() + std::chrono::milliseconds(delayMs))); - } else { - enqueue(std::move(func)); - } - } - - void enqueueSync(std::function&& func) { - EventFlag done; - enqueueTask(Task::createSync([&] () mutable { - func(); - done.set(); - })); - if (stopped_) { - // If this queue is stopped_, the sync task might never actually run. - throw std::runtime_error("Stopped within enqueueSync."); - } - done.wait(); - } - - void stop() { - stopped_ = true; - pending_.set(); - } - - bool isStopped() { - return stopped_; - } - - void quitSynchronous() { - stop(); - finished_.wait(); - } - - void run() { - // If another thread stops this one, then the acquire-release on pending_ - // ensures that we read stopped some time after it was set (and other - // threads just have to deal with the fact that we might run a task "after" - // they stop us). - // - // If we are stopped on this thread, then memory order doesn't really - // matter reading stopped_. - while (!stopped_.load(std::memory_order_relaxed)) { - sweep(); - if (delayed_.empty()) { - pending_.wait(); - } else { - pending_.wait_until(delayed_.nextTime()); - } - } - // This sweep is just to catch erroneous enqueueSync. That is, there could - // be a task marked sync that another thread is waiting for, but we'll - // never actually run it. - sweep(); - finished_.set(); - } - - // We are processing two queues, the posted tasks (queue_) and the delayed - // tasks (delayed_). Delayed tasks first go into posted tasks, and then are - // moved to the delayed queue if we pop them before the time they are - // scheduled for. - // As we pop things from queue_, before dealing with that thing, we run any - // delayed tasks whose scheduled time has arrived. - void sweep() { - queue_.sweep([this] (Task* t) { - std::unique_ptr owned(t); - if (stopped_.load(std::memory_order_relaxed)) { - if (t->sync) { - throw std::runtime_error("Sync task posted while stopped."); - } - return; - } - - delayed_.process(); - if (t->startTime != time_point() && now() <= t->startTime) { - delayed_.push(owned.release()); - } else { - t->func(); - } - }); - delayed_.process(); - } - - void bindToThisThread() { - // TODO: handle nested runloops (either allow them or throw an exception). - if (tid_ != std::thread::id{}) { - throw std::runtime_error("Message queue already bound to thread."); - } - tid_ = std::this_thread::get_id(); - } - - bool isOnQueue() { - return std::this_thread::get_id() == tid_; - } - - private: - void enqueueTask(Task* task) { - if (queue_.insertHead(task)) { - pending_.set(); - } - } - - std::thread::id tid_; - - folly::AtomicIntrusiveLinkedList queue_; - - std::atomic_bool stopped_{false}; - DelayedTaskQueue delayed_; - - BinarySemaphore pending_; - EventFlag finished_; -}; - - -CxxMessageQueue::CxxMessageQueue() : qr_(new QueueRunner()) { - -} - -CxxMessageQueue::~CxxMessageQueue() { - // TODO(cjhopman): Add detach() so that the queue doesn't have to be - // explicitly stopped. - if (!qr_->isStopped()) { - LOG(FATAL) << "Queue not stopped."; - } -} - -void CxxMessageQueue::runOnQueue(std::function&& func) { - qr_->enqueue(std::move(func)); -} - -void CxxMessageQueue::runOnQueueDelayed(std::function&& func, uint64_t delayMs) { - qr_->enqueueDelayed(std::move(func), delayMs); -} - -void CxxMessageQueue::runOnQueueSync(std::function&& func) { - if (isOnQueue()) { - func(); - return; - } - qr_->enqueueSync(std::move(func)); -} - -void CxxMessageQueue::quitSynchronous() { - if (isOnQueue()) { - qr_->stop(); - } else { - qr_->quitSynchronous(); - } -} - -bool CxxMessageQueue::isOnQueue() { - return qr_->isOnQueue(); -} - -namespace { -struct MQRegistry { - std::weak_ptr find(std::thread::id tid) { - std::lock_guard g(lock_); - auto iter = registry_.find(tid); - if (iter == registry_.end()) return std::weak_ptr(); - return iter->second; - } - - void registerQueue(std::thread::id tid, std::weak_ptr mq) { - std::lock_guard g(lock_); - registry_[tid] = mq; - } - - void unregister(std::thread::id tid) { - std::lock_guard g(lock_); - registry_.erase(tid); - } - private: - std::mutex lock_; - std::unordered_map> registry_; -}; - -MQRegistry& getMQRegistry() { - static MQRegistry* mq_registry = new MQRegistry(); - return *mq_registry; -} -} - -std::shared_ptr CxxMessageQueue::current() { - auto tid = std::this_thread::get_id(); - return getMQRegistry().find(tid).lock(); -} - -std::function CxxMessageQueue::getUnregisteredRunLoop() { - return [capture=qr_] { - capture->bindToThisThread(); - capture->run(); - }; -} - -std::function CxxMessageQueue::getRunLoop(std::shared_ptr mq) { - return [capture=mq->qr_, weakMq=std::weak_ptr(mq)] { - capture->bindToThisThread(); - auto tid = std::this_thread::get_id(); - - getMQRegistry().registerQueue(tid, weakMq); - capture->run(); - getMQRegistry().unregister(tid); - }; -} - - - -} // namespace react -} // namespace facebook diff --git a/ReactCommon/cxxreact/CxxMessageQueue.h b/ReactCommon/cxxreact/CxxMessageQueue.h deleted file mode 100644 index 626d058993a8b2..00000000000000 --- a/ReactCommon/cxxreact/CxxMessageQueue.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -namespace detail { -template -class CVFlag { - public: - using time_point = std::chrono::steady_clock::time_point; - void set() { - std::lock_guard lk(mtx_); - flag_ = true; - cv_.notify_one(); - } - - void wait() { - std::unique_lock lk(mtx_); - cv_.wait(lk, [this] { return flag_; }); - if (clearOnWait) flag_ = false; - } - - bool wait_until(time_point d) { - std::unique_lock lk(mtx_); - bool res = cv_.wait_until(lk, d, [this] { return flag_; }); - if (clearOnWait && res) flag_ = false; - return res; - } - - private: - bool flag_{false}; - std::condition_variable cv_; - std::mutex mtx_; -}; - -using BinarySemaphore = CVFlag; -using EventFlag = CVFlag; -} - -class CxxMessageQueue : public MessageQueueThread { - public: - CxxMessageQueue(); - virtual ~CxxMessageQueue() override; - virtual void runOnQueue(std::function&&) override; - void runOnQueueDelayed(std::function&&, uint64_t delayMs); - // runOnQueueSync and quitSynchronous are dangerous. They should only be - // used for initialization and cleanup. - virtual void runOnQueueSync(std::function&&) override; - // Once quitSynchronous() returns, no further work should run on the queue. - virtual void quitSynchronous() override; - - bool isOnQueue(); - - // If this getRunLoop is used, current() will not work. - std::function getUnregisteredRunLoop(); - - // This returns a function that will actually run the runloop. - // This runloop will return some time after quitSynchronous (or after this is destroyed). - // - // When running the runloop, it is important to ensure that no frames in the - // current stack have a strong reference to the queue. - // - // Only one thread should run the runloop. - static std::function getRunLoop(std::shared_ptr mq); - - static std::shared_ptr current(); - private: - class QueueRunner; - std::shared_ptr qr_; -}; - -}} diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index 4188fe5720ea02..5b80fa5df9010a 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -2,7 +2,6 @@ #include "Instance.h" -#include "CxxMessageQueue.h" #include "Executor.h" #include "MethodCall.h" #include "RecoverableError.h" diff --git a/ReactCommon/cxxreact/tests/BUCK b/ReactCommon/cxxreact/tests/BUCK index f181d7474a9153..f8f29de4247357 100644 --- a/ReactCommon/cxxreact/tests/BUCK +++ b/ReactCommon/cxxreact/tests/BUCK @@ -1,5 +1,4 @@ TEST_SRCS = [ - "CxxMessageQueueTest.cpp", "RecoverableErrorTest.cpp", "jsarg_helpers.cpp", "jsbigstring.cpp", diff --git a/ReactCommon/cxxreact/tests/CxxMessageQueueTest.cpp b/ReactCommon/cxxreact/tests/CxxMessageQueueTest.cpp deleted file mode 100644 index f0949cb4043cb7..00000000000000 --- a/ReactCommon/cxxreact/tests/CxxMessageQueueTest.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include - -#include - -#include -#include - -using namespace facebook::react; -using detail::EventFlag; -using time_point = EventFlag::time_point; - -using std::chrono::milliseconds; - -namespace { -time_point now() { - return std::chrono::steady_clock::now(); -} - -std::shared_ptr createAndStartQueue(EventFlag& finishedFlag) { - auto q = std::make_shared(); - std::thread t([q, &finishedFlag] () mutable { - auto loop = CxxMessageQueue::getRunLoop(q); - // Note: make sure that no stack frames above loop() have a strong reference to q. - q.reset(); - loop(); - finishedFlag.set(); - }); - t.detach(); - return q; -} - -std::unique_ptr createAndStartUnregisteredQueue( - EventFlag& finishedFlag) { - auto q = std::make_unique(); - std::thread t([loop=q->getUnregisteredRunLoop(), &finishedFlag] { - loop(); - finishedFlag.set(); - }); - t.detach(); - return q; -} - -// This is just used to start up a queue for a test and make sure that it is -// actually shut down after the test. -struct QueueWithThread { - QueueWithThread() { - queue = createAndStartQueue(done); - } - - ~QueueWithThread() { - queue->quitSynchronous(); - queue.reset(); - EXPECT_TRUE(done.wait_until(now() + milliseconds(300))) << "Queue did not exit"; - } - - EventFlag done; - std::shared_ptr queue; -}; -} - -TEST(CxxMessageQueue, TestQuit) { - EventFlag done; - auto q = createAndStartQueue(done); - q->quitSynchronous(); - EXPECT_TRUE(done.wait_until(now() + milliseconds(300))) - << "Queue did not exit runloop after quitSynchronous"; -} - -TEST(CxxMessageQueue, TestPostTask) { - QueueWithThread qt; - auto q = qt.queue; - - EventFlag flag; - q->runOnQueue([&] { - flag.set(); - }); - flag.wait(); -} - -TEST(CxxMessageQueue, TestPostUnregistered) { - EventFlag qdone; - auto q = createAndStartUnregisteredQueue(qdone); - - EventFlag tflag; - q->runOnQueue([&] { - tflag.set(); - }); - tflag.wait(); - - q->quitSynchronous(); - q.reset(); - EXPECT_TRUE(qdone.wait_until(now() + milliseconds(300))) << "Queue did not exit"; -} - -TEST(CxxMessageQueue, TestPostCurrent) { - QueueWithThread qt; - auto q = qt.queue; - - EventFlag flag; - q->runOnQueue([&] { - CxxMessageQueue::current()->runOnQueue([&] { - flag.set(); - }); - }); - flag.wait(); -} - -TEST(CxxMessageQueue, TestPostTaskMultiple) { - QueueWithThread qt; - auto q = qt.queue; - - std::vector vec(10); - for (int i = 0; i < 10; i++) { - q->runOnQueue([&, i] { - vec[i].set(); - }); - } - for (int i = 0; i < 10; i++) { - vec[i].wait(); - } -} - -TEST(CxxMessageQueue, TestQueuedTaskOrdering) { - QueueWithThread qt; - auto q = qt.queue; - - // Block the runloop so we can get some queued tasks. - EventFlag wait; - q->runOnQueue([&] { - wait.wait(); - }); - - // These tasks should run in order. - int failed = -1; - int i = 0; - for (int j = 0; j < 10; j++) { - q->runOnQueue([&, j] { - if (i != j) { - failed = j; - } - i++; - }); - } - wait.set(); - - // Flush the queue. - q->runOnQueueSync([&] {}); - - ASSERT_EQ(failed, -1); - ASSERT_EQ(i, 10); -} - -TEST(CxxMessageQueue, TestDelayedTaskOrdering) { - QueueWithThread qt; - auto q = qt.queue; - - // Block the runloop so we can get some queued tasks. - EventFlag wait; - q->runOnQueue([&] { - wait.wait(); - }); - - int ids[] = {8, 4, 6, 1, 3, 2, 9, 5, 0, 7}; - - int failed = -1; - int i = 0; - EventFlag done; - // If this loop actually takes longer than the difference between delays, the - // ordering could get screwed up :/ - for (int j = 0; j < 10; j++) { - q->runOnQueueDelayed([&, j] { - if (i != ids[j]) { - failed = j; - } - i++; - if (ids[j] == 9) { - done.set(); - } - }, 50 + 10 * ids[j]); - } - wait.set(); - done.wait(); - - ASSERT_EQ(failed, -1); - ASSERT_EQ(i, 10); -} From 1433185a092f70047cc8ef3f90a953a32d756326 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 21 Mar 2017 12:53:48 -0700 Subject: [PATCH 122/366] Improved implementation of placeholer feature in RCTUITextView Reviewed By: fkgozali Differential Revision: D4746177 fbshipit-source-id: a8c27ec052b046d4732b14ed081dcaebb44bdaa7 --- Libraries/Text/RCTUITextView.m | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Libraries/Text/RCTUITextView.m b/Libraries/Text/RCTUITextView.m index 24302f823ed89a..67ae7e5798ba2f 100644 --- a/Libraries/Text/RCTUITextView.m +++ b/Libraries/Text/RCTUITextView.m @@ -36,7 +36,6 @@ - (instancetype)initWithFrame:(CGRect)frame object:self]; _placeholderView = [[UILabel alloc] initWithFrame:self.bounds]; - _placeholderView.hidden = YES; _placeholderView.isAccessibilityElement = NO; _placeholderView.numberOfLines = 0; [self addSubview:_placeholderView]; @@ -55,20 +54,19 @@ - (void)dealloc - (void)setPlaceholderText:(NSString *)placeholderText { _placeholderText = placeholderText; - [self invalidatePlaceholder]; + _placeholderView.text = _placeholderText; } - (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor { _placeholderTextColor = placeholderTextColor; - [self invalidatePlaceholder]; + _placeholderView.textColor = _placeholderTextColor ?: defaultPlaceholderTextColor(); } - - (void)textDidChange { _textWasPasted = NO; - [self invalidatePlaceholder]; + [self invalidatePlaceholderVisibility]; } #pragma mark - UIResponder @@ -101,7 +99,13 @@ - (void)didMoveToWindow - (void)setFont:(UIFont *)font { [super setFont:font]; - [self invalidatePlaceholder]; + _placeholderView.font = font ?: defaultPlaceholderFont(); +} + +- (void)setTextAlignment:(NSTextAlignment)textAlignment +{ + [super setTextAlignment:textAlignment]; + _placeholderView.textAlignment = textAlignment; } - (void)setText:(NSString *)text @@ -168,22 +172,10 @@ - (CGSize)sizeThatFits:(CGSize)size #pragma mark - Placeholder -- (void)invalidatePlaceholder +- (void)invalidatePlaceholderVisibility { - BOOL wasVisible = !_placeholderView.isHidden; BOOL isVisible = _placeholderText.length != 0 && self.text.length == 0; - - if (wasVisible != isVisible) { - _placeholderView.hidden = !isVisible; - } - - if (isVisible) { - _placeholderView.font = self.font ?: defaultPlaceholderFont(); - _placeholderView.textColor = _placeholderTextColor ?: defaultPlaceholderTextColor(); - _placeholderView.textAlignment = self.textAlignment; - _placeholderView.text = _placeholderText; - [self setNeedsLayout]; - } + _placeholderView.hidden = !isVisible; } @end From ee245b9be8e05b6e2a37b2771bd1b2d9e00ad16d Mon Sep 17 00:00:00 2001 From: "Andrew Y. Chen" Date: Tue, 21 Mar 2017 14:45:37 -0700 Subject: [PATCH 123/366] Fix instrumentation tests for api 22 Summary: Bug in Android https://code.google.com/p/android/issues/detail?id=33868 causes the RN catalyst instrumentation test to fail with ``` java.lang.ArrayIndexOutOfBoundsException: length=253; index=-1 at android.text.StaticLayout.calculateEllipsis(StaticLayout.java:667) at android.text.StaticLayout.out(StaticLayout.java:631) at android.text.StaticLayout.generate(StaticLayout.java:423) ... ``` The fix is to set singleLine to true when there is only one line of text Reviewed By: AaaChiuuu Differential Revision: D4562000 fbshipit-source-id: 84248e3982063b767e8b0465effe2321b54a7fa2 --- .../main/java/com/facebook/react/views/text/ReactTextView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java index 3d5d9668bd8106..fa8f87710b1b8f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java @@ -231,6 +231,7 @@ public void setBackgroundColor(int color) { public void setNumberOfLines(int numberOfLines) { mNumberOfLines = numberOfLines == 0 ? ViewDefaults.NUMBER_OF_LINES : numberOfLines; + setSingleLine(mNumberOfLines == 1); setMaxLines(mNumberOfLines); } From a0304327a9c1989b49ac4c106d824e2b31a6cc16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Tue, 21 Mar 2017 15:13:04 -0700 Subject: [PATCH 124/366] Remove Navigator recommendation Summary: We recommend using `react-navigation` over `Navigator`. Adds a link to the new `native-navigation` component as well. Did not test website generation, this is a comments only edit that should work fine. Closes https://github.com/facebook/react-native/pull/12963 Differential Revision: D4749072 Pulled By: hramos fbshipit-source-id: 4506630306c44b24b95c4f5d5a42c1caa9e2cd4e --- Libraries/Components/Navigation/NavigatorIOS.ios.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index 90a6c37e9820c1..10c18e0c507767 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -133,10 +133,10 @@ type Event = Object; * animations and behavior from UIKIt. * * As the name implies, it is only available on iOS. Take a look at - * [`Navigator`](docs/navigator.html) for a similar solution for your - * cross-platform needs, or check out - * [react-native-navigation](https://github.com/wix/react-native-navigation), a - * component that aims to provide native navigation on both iOS and Android. + * [`React Navigation`](https://reactnavigation.org/) for a cross-platform + * solution in JavaScript, or check out either of these components for native + * solutions: [native-navigation](http://airbnb.io/native-navigation/), + * [react-native-navigation](https://github.com/wix/react-native-navigation). * * To set up the navigator, provide the `initialRoute` prop with a route * object. A route object is used to describe each scene that your app From 68c655a2fdabcd053f7dbf92dcbe70d1f45a40c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Tue, 21 Mar 2017 15:40:04 -0700 Subject: [PATCH 125/366] Update ISSUE_TEMPLATE.md Summary: Some quick copy changes. Closes https://github.com/facebook/react-native/pull/13061 Differential Revision: D4749511 Pulled By: hramos fbshipit-source-id: 5b6f67be40ed071367507ac3c87f7ac67a0584e7 --- .github/ISSUE_TEMPLATE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 7acc05e46e1629..dcb01c326bed0e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -15,7 +15,7 @@ Your issue may be closed without explanation if it does not provide the informat ### Reproduction -[FILL THIS OUT: Try to reproduce your bug on https://sketch.expo.io/ and provide a link. If you can't reproduce the bug on Sketch, provide a sample project.] +[FILL THIS OUT: Try to reproduce your bug on https://sketch.expo.io/ and provide a link. If you can't reproduce the bug on Sketch, provide a sample project. At the very least, provide an example of your code.] ### Solution @@ -23,7 +23,7 @@ Your issue may be closed without explanation if it does not provide the informat ### Additional Information -* React Native version: [FILL THIS OUT: Does the bug reproduce on the latest RN release?] +* React Native version: [FILL THIS OUT: Be specific, filling out "latest" here is not enough.] * Platform: [FILL THIS OUT: iOS, Android, or both?] -* Operating System: [FILL THIS OUT: MacOS, Linux, or Windows?] +* Development Operating System: [FILL THIS OUT: Are you developing on MacOS, Linux, or Windows?] * Dev tools: [FILL THIS OUT: Xcode or Android Studio version, iOS or Android SDK version, if applicable] From ba75d9903368e90b5c7a9b61e222609b5ac9acb7 Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Tue, 21 Mar 2017 16:01:44 -0700 Subject: [PATCH 126/366] don't call clearFrameCallback() if we don't have a ReactChoreographer to clear the frame callback on Reviewed By: achen1 Differential Revision: D4741906 fbshipit-source-id: 2d5fabab6e04c08252513f77149c04e3b8314d2c --- .../facebook/react/animated/NativeAnimatedModule.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index c16e2acd0bcbda..d4fd0680ff5147 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -13,6 +13,7 @@ import java.util.ArrayList; +import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; @@ -70,10 +71,12 @@ * isolates us from the problems that may be caused by concurrent updates of animated graph while UI * thread is "executing" the animation loop. */ -@ReactModule(name = "NativeAnimatedModule") +@ReactModule(name = NativeAnimatedModule.NAME) public class NativeAnimatedModule extends ReactContextBaseJavaModule implements OnBatchCompleteListener, LifecycleEventListener { + protected static final String NAME = "NativeAnimatedModule"; + private interface UIThreadOperation { void execute(NativeAnimatedNodesManager animatedNodesManager); } @@ -159,6 +162,10 @@ public void onBatchComplete() { @Override public void onHostPause() { + if (mReactChoreographer == null) { + FLog.e(NAME, "Called NativeAnimated.onHostPause() with a null ReactChoreographer."); + return; + } clearFrameCallback(); } @@ -169,7 +176,7 @@ public void onHostDestroy() { @Override public String getName() { - return "NativeAnimatedModule"; + return NAME; } private void clearFrameCallback() { From c41b29d6de8241e5b9269034ece30d5c8803c938 Mon Sep 17 00:00:00 2001 From: Jeremi Stadler Date: Tue, 21 Mar 2017 16:21:46 -0700 Subject: [PATCH 127/366] Fixes missleading comment on getInitialURL Summary: The returned value from Linking.getInitialURL is a promise that returns an url. Can be seen here: https://github.com/facebook/react-native/blob/f126540519bd276c0048aa77b543dc863412de46/Libraries/Linking/Linking.js#L175 Closes https://github.com/facebook/react-native/pull/12851 Differential Revision: D4716084 Pulled By: hramos fbshipit-source-id: 309881cfb423a5c9a3f9010ae7ca226b63c91599 --- Libraries/Linking/Linking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Linking/Linking.js b/Libraries/Linking/Linking.js index 1902bfb2a793ef..0f50b15bd5ee19 100644 --- a/Libraries/Linking/Linking.js +++ b/Libraries/Linking/Linking.js @@ -33,7 +33,7 @@ const LinkingManager = Platform.OS === 'android' ? * * ``` * componentDidMount() { - * var url = Linking.getInitialURL().then((url) => { + * Linking.getInitialURL().then((url) => { * if (url) { * console.log('Initial url is: ' + url); * } From d9ac00735c7670eb41e46c6effdb3ae4a91e5ec0 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Tue, 21 Mar 2017 18:24:58 -0700 Subject: [PATCH 128/366] Make the choice of bridge based on an optional delegate method Reviewed By: javache Differential Revision: D4679644 fbshipit-source-id: f53e554e283fdb0b59c41623e690fd1a21e03a57 --- React/Base/RCTBridge.m | 26 ++++++++++++++++- React/Base/RCTBridgeDelegate.h | 14 ++++++++++ React/CxxBridge/RCTCxxBridge.h | 17 ------------ React/CxxBridge/RCTCxxBridge.mm | 49 ++------------------------------- 4 files changed, 42 insertions(+), 64 deletions(-) delete mode 100644 React/CxxBridge/RCTCxxBridge.h diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 5fa260caf6fd4a..0fe05ca8bb4ae1 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -302,7 +302,31 @@ - (void)setUp - (void)createBatchedBridge { - self.batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self]; + // In order to facilitate switching between bridges with only build + // file changes, this uses reflection to check which bridges are + // available. This is a short-term hack until RCTBatchedBridge is + // removed. + + Class batchedBridgeClass = objc_lookUpClass("RCTBatchedBridge"); + Class cxxBridgeClass = objc_lookUpClass("RCTCxxBridge"); + + Class implClass = nil; + + if ([self.delegate respondsToSelector:@selector(shouldBridgeUseCxxBridge:)]) { + if ([self.delegate shouldBridgeUseCxxBridge:self]) { + implClass = cxxBridgeClass; + } else { + implClass = batchedBridgeClass; + } + } else if (batchedBridgeClass != nil) { + implClass = batchedBridgeClass; + } else if (cxxBridgeClass != nil) { + implClass = cxxBridgeClass; + } + + RCTAssert(implClass != nil, @"No bridge implementation is available, giving up."); + + self.batchedBridge = [[implClass alloc] initWithParentBridge:self]; } - (BOOL)isLoading diff --git a/React/Base/RCTBridgeDelegate.h b/React/Base/RCTBridgeDelegate.h index 343a2275dcf069..7639339858ca55 100644 --- a/React/Base/RCTBridgeDelegate.h +++ b/React/Base/RCTBridgeDelegate.h @@ -91,6 +91,20 @@ */ - (BOOL)shouldBridgeUseCustomJSC:(RCTBridge *)bridge; +/** + * Configure whether the legacy RCTBatchedBridge or new RCTCxxBridge + * should be used. If this method is implemented and the specified + * bridge is not linked in, startup will fail. If this method is not + * implemented, the implementation will default to RCTBatchedBridge, + * but if it is not linked in, will try RCTCxxBridge instead. If + * neither bridge is linked in, startup will fail. This order will be + * reversed in the near future, as the legacy bridge is closer to + * being removed. + * + * @experimental + */ +- (BOOL)shouldBridgeUseCxxBridge:(RCTBridge *)bridge; + /** * The bridge will automatically attempt to load the JS source code from the * location specified by the `sourceURLForBridge:` method, however, if you want diff --git a/React/CxxBridge/RCTCxxBridge.h b/React/CxxBridge/RCTCxxBridge.h deleted file mode 100644 index 89ac4d01e0ce31..00000000000000 --- a/React/CxxBridge/RCTCxxBridge.h +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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. - */ - -#import - -@interface RCTCxxBridge : RCTBridge - -+ (void)enable; -+ (void)disable; - -@end diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index e48906af72e4b9..ee84db6be1adf5 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -9,8 +9,6 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "RCTCxxBridge.h" - #include #include #include @@ -48,6 +46,9 @@ #import #endif +@interface RCTCxxBridge : RCTBridge +@end + #define RCTAssertJSThread() \ RCTAssert(self.executorClass || self->_jsThread == [NSThread currentThread], \ @"This method must be called on JS thread") @@ -92,8 +93,6 @@ static bool isRAMBundle(NSData *script) { return parseTypeFromHeader(header) == ScriptTag::RAMBundle; } -static std::atomic_bool cxxBridgeEnabled(false); - @interface RCTCxxBridge () @property (nonatomic, weak, readonly) RCTBridge *parentBridge; @@ -121,19 +120,6 @@ ExecutorToken createExecutorToken() override { void onExecutorStopped(ExecutorToken) override {} }; -@implementation RCTBridge (CxxBridge) - -- (void)CXX_createBatchedBridge -{ - if (cxxBridgeEnabled) { - self.batchedBridge = [[RCTCxxBridge alloc] initWithParentBridge:self]; - } else { - self.batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self]; - } -} - -@end - @implementation RCTCxxBridge { BOOL _wasBatchActive; @@ -172,35 +158,6 @@ + (void)initialize } } -+ (void)swizzleBridge -{ - // Swizzle RCTBridge to use this, instead of RCTBatchedBridge - static dispatch_once_t once; - dispatch_once(&once, ^{ - RCTSwapInstanceMethods([RCTBridge class], - NSSelectorFromString(@"createBatchedBridge"), - @selector(CXX_createBatchedBridge)); - }); -} - -+ (void)enable -{ - [RCTCxxBridge swizzleBridge]; -#ifdef WITH_FBSYSTRACE - [RCTFBSystrace registerCallbacks]; -#endif - cxxBridgeEnabled = true; -} - -+ (void)disable -{ - [RCTCxxBridge swizzleBridge]; -#ifdef WITH_FBSYSTRACE - [RCTFBSystrace unregisterCallbacks]; -#endif - cxxBridgeEnabled = false; -} - - (JSContext *)jsContext { return contextForGlobalContextRef((JSGlobalContextRef) self->_reactInstance->getJavaScriptContext()); From ba149d72772111758b12b76db56c60dea5b1ef26 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Tue, 21 Mar 2017 18:25:00 -0700 Subject: [PATCH 129/366] Tease apart React and RCTBatchedBridge targets Reviewed By: javache Differential Revision: D4679655 fbshipit-source-id: 8123488c2d50dd7cc2329b5131e99998fe1f1e2f --- Examples/UIExplorer/UIExplorer/maincxx.m | 28 --- .../UIExplorerCxx.xcodeproj/project.pbxproj | 202 +++++++++--------- .../xcschemes/UIExplorer.xcscheme | 31 +++ Libraries/RCTTest/RCTTestRunner.m | 18 -- React/Base/RCTBatchedBridge.m | 5 +- React/Base/RCTBridge.m | 62 +++--- .../{Executors => Base}/RCTJSCErrorHandling.h | 0 .../RCTJSCErrorHandling.mm | 0 React/CxxBridge/RCTCxxBridge.mm | 26 +-- React/CxxBridge/RCTJSCHelpers.h | 19 ++ React/CxxBridge/RCTJSCHelpers.mm | 52 +++++ React/Executors/RCTJSCExecutor.mm | 22 +- React/Profiler/RCTJSCProfiler.m | 2 +- React/Profiler/RCTPerfMonitor.m | 1 - React/Profiler/RCTProfile.m | 4 +- React/React.xcodeproj/project.pbxproj | 24 +-- React/ReactCxx.xcodeproj/project.pbxproj | 127 ++++------- 17 files changed, 309 insertions(+), 314 deletions(-) delete mode 100644 Examples/UIExplorer/UIExplorer/maincxx.m rename React/{Executors => Base}/RCTJSCErrorHandling.h (100%) rename React/{Executors => Base}/RCTJSCErrorHandling.mm (100%) create mode 100644 React/CxxBridge/RCTJSCHelpers.h create mode 100644 React/CxxBridge/RCTJSCHelpers.mm diff --git a/Examples/UIExplorer/UIExplorer/maincxx.m b/Examples/UIExplorer/UIExplorer/maincxx.m deleted file mode 100644 index 8a294046c659d6..00000000000000 --- a/Examples/UIExplorer/UIExplorer/maincxx.m +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - [RCTCxxBridge enable]; - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj index ad832206603f87..b56552e8c7331e 100644 --- a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; }; 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; }; 134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */; }; + 1380DCD41E70C44800E7C47D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1380DC991E70C0DD00E7C47D /* libReact.a */; }; 138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; }; 138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; }; 1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; }; @@ -23,10 +24,9 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */; }; 13BCE84F1C9C209600DD7AAD /* RCTComponentPropsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BCE84E1C9C209600DD7AAD /* RCTComponentPropsTests.m */; }; - 13CF8FD11E2966FF0005310D /* maincxx.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CF8FD01E2966FF0005310D /* maincxx.m */; }; - 13CF8FF31E2967D10005310D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13CF8FE01E2967C40005310D /* libReact.a */; }; 13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; }; 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; }; 13E501F11D07A84A005F35D8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13E501A31D07A502005F35D8 /* libRCTAnimation.a */; }; @@ -90,6 +90,7 @@ 2DD323DD1DA2DDBF000FE1B8 /* UpdatePropertiesExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */; }; 2DD323DE1DA2DDBF000FE1B8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 2DD323DF1DA2DDBF000FE1B8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 2DD323E01DA2DDBF000FE1B8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2DD323E11DA2DDBF000FE1B8 /* legacy_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */; }; 2DD323E21DA2DDBF000FE1B8 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB61A68108700A75B9A /* Info.plist */; }; 2DD323E31DA2DE3F000FE1B8 /* libRCTAnimation-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323B51DA2DD8B000FE1B8 /* libRCTAnimation-tvOS.a */; }; @@ -154,89 +155,89 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTGeolocation; }; - 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = { + 1380DC981E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 13CF8FDF1E2967C40005310D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = ReactCxx; + remoteInfo = React; }; - 13CF8FE11E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC9A1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28131D9B038B00D4039D; - remoteInfo = "ReactCxx-tvOS"; + remoteInfo = "React-tvOS"; }; - 13CF8FE31E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC9C1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3C059A1DE3340900C268FA; remoteInfo = yoga; }; - 13CF8FE51E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC9E1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3C06751DE3340C00C268FA; remoteInfo = "yoga-tvOS"; }; - 13CF8FE71E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA01E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; remoteInfo = cxxreact; }; - 13CF8FE91E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA21E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; remoteInfo = "cxxreact-tvOS"; }; - 13CF8FEB1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA41E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; remoteInfo = jschelpers; }; - 13CF8FED1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA61E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; remoteInfo = "jschelpers-tvOS"; }; - 13CF8FEF1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA81E70C0DD00E7C47D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; + remoteInfo = "third-party"; + }; + 1380DCAA1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 139D7E881E25C6D100323FB7; remoteInfo = "double-conversion"; }; - 13CF8FF11E2967C40005310D /* PBXContainerItemProxy */ = { + 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; - remoteInfo = "third-party"; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3C86DF461ADF2C930047B81A; + remoteInfo = RCTWebSocket; }; 13E501A21D07A502005F35D8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -391,6 +392,7 @@ 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = ""; }; 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitTests.m; sourceTree = ""; }; + 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactCxx.xcodeproj; path = ../../React/ReactCxx.xcodeproj; sourceTree = ""; }; 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = ""; }; 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = ""; }; 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = ""; }; @@ -401,11 +403,10 @@ 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = UIExplorer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = ""; }; 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTURLUtilsTests.m; sourceTree = ""; }; 13BCE84E1C9C209600DD7AAD /* RCTComponentPropsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTComponentPropsTests.m; sourceTree = ""; }; 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; - 13CF8FD01E2966FF0005310D /* maincxx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = maincxx.m; path = UIExplorer/maincxx.m; sourceTree = ""; }; - 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactCxx.xcodeproj; path = ../../React/ReactCxx.xcodeproj; sourceTree = ""; }; 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = ""; }; 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMethodArgumentTests.m; sourceTree = ""; }; 13E5019C1D07A502005F35D8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = ../../Libraries/NativeAnimation/RCTAnimation.xcodeproj; sourceTree = ""; }; @@ -486,7 +487,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 13CF8FF31E2967D10005310D /* libReact.a in Frameworks */, + 1380DCD41E70C44800E7C47D /* libReact.a in Frameworks */, 147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */, 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */, 13E501F11D07A84A005F35D8 /* libRCTAnimation.a in Frameworks */, @@ -555,7 +556,7 @@ 1316A21D1AA397F400C0188E /* Libraries */ = { isa = PBXGroup; children = ( - 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */, + 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */, 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */, 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */, 13E5019C1D07A502005F35D8 /* RCTAnimation.xcodeproj */, @@ -626,6 +627,23 @@ name = Products; sourceTree = ""; }; + 1380DC8C1E70C0DC00E7C47D /* Products */ = { + isa = PBXGroup; + children = ( + 1380DC991E70C0DD00E7C47D /* libReact.a */, + 1380DC9B1E70C0DD00E7C47D /* libReact.a */, + 1380DC9D1E70C0DD00E7C47D /* libyoga.a */, + 1380DC9F1E70C0DD00E7C47D /* libyoga.a */, + 1380DCA11E70C0DD00E7C47D /* libcxxreact.a */, + 1380DCA31E70C0DD00E7C47D /* libcxxreact.a */, + 1380DCA51E70C0DD00E7C47D /* libjschelpers.a */, + 1380DCA71E70C0DD00E7C47D /* libjschelpers.a */, + 1380DCA91E70C0DD00E7C47D /* libthird-party.a */, + 1380DCAB1E70C0DD00E7C47D /* libdouble-conversion.a */, + ); + name = Products; + sourceTree = ""; + }; 138DEE031B9EDDDB007F4EA5 /* Products */ = { isa = PBXGroup; children = ( @@ -651,29 +669,12 @@ 13B07FB01A68108700A75B9A /* AppDelegate.m */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13CF8FD01E2966FF0005310D /* maincxx.m */, + 13B07FB71A68108700A75B9A /* main.m */, 1323F18D1C04ABAC0091BED0 /* Supporting Files */, ); name = UIExplorer; sourceTree = ""; }; - 13CF8FD31E2967C40005310D /* Products */ = { - isa = PBXGroup; - children = ( - 13CF8FE01E2967C40005310D /* libReact.a */, - 13CF8FE21E2967C40005310D /* libReact.a */, - 13CF8FE41E2967C40005310D /* libyoga.a */, - 13CF8FE61E2967C40005310D /* libyoga.a */, - 13CF8FE81E2967C40005310D /* libcxxreact.a */, - 13CF8FEA1E2967C40005310D /* libcxxreact.a */, - 13CF8FEC1E2967C40005310D /* libjschelpers.a */, - 13CF8FEE1E2967C40005310D /* libjschelpers.a */, - 13CF8FF21E2967C40005310D /* libthird-party.a */, - 13CF8FF01E2967C40005310D /* libdouble-conversion.a */, - ); - name = Products; - sourceTree = ""; - }; 13E5019D1D07A502005F35D8 /* Products */ = { isa = PBXGroup; children = ( @@ -1101,8 +1102,8 @@ ProjectRef = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; }, { - ProductGroup = 13CF8FD31E2967C40005310D /* Products */; - ProjectRef = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + ProductGroup = 1380DC8C1E70C0DC00E7C47D /* Products */; + ProjectRef = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; }, ); projectRoot = ""; @@ -1154,88 +1155,88 @@ remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTCameraRoll.a; - remoteRef = 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 13CF8FE01E2967C40005310D /* libReact.a */ = { + 1380DC991E70C0DD00E7C47D /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReact.a; - remoteRef = 13CF8FDF1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC981E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE21E2967C40005310D /* libReact.a */ = { + 1380DC9B1E70C0DD00E7C47D /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReact.a; - remoteRef = 13CF8FE11E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC9A1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE41E2967C40005310D /* libyoga.a */ = { + 1380DC9D1E70C0DD00E7C47D /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; - remoteRef = 13CF8FE31E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC9C1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE61E2967C40005310D /* libyoga.a */ = { + 1380DC9F1E70C0DD00E7C47D /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; - remoteRef = 13CF8FE51E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC9E1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE81E2967C40005310D /* libcxxreact.a */ = { + 1380DCA11E70C0DD00E7C47D /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; - remoteRef = 13CF8FE71E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA01E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FEA1E2967C40005310D /* libcxxreact.a */ = { + 1380DCA31E70C0DD00E7C47D /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; - remoteRef = 13CF8FE91E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA21E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FEC1E2967C40005310D /* libjschelpers.a */ = { + 1380DCA51E70C0DD00E7C47D /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; - remoteRef = 13CF8FEB1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA41E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FEE1E2967C40005310D /* libjschelpers.a */ = { + 1380DCA71E70C0DD00E7C47D /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; - remoteRef = 13CF8FED1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA61E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FF01E2967C40005310D /* libdouble-conversion.a */ = { + 1380DCA91E70C0DD00E7C47D /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 1380DCA81E70C0DD00E7C47D /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 1380DCAB1E70C0DD00E7C47D /* libdouble-conversion.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libdouble-conversion.a"; - remoteRef = 13CF8FEF1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCAA1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FF21E2967C40005310D /* libthird-party.a */ = { + 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libthird-party.a"; - remoteRef = 13CF8FF11E2967C40005310D /* PBXContainerItemProxy */; + path = libRCTCameraRoll.a; + remoteRef = 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 13E501A31D07A502005F35D8 /* libRCTAnimation.a */ = { @@ -1480,10 +1481,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 13CF8FD11E2966FF0005310D /* maincxx.m in Sources */, 272E6B3F1BEA849E001FCF37 /* UpdatePropertiesExampleView.m in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1517,6 +1518,7 @@ files = ( 2DD323DC1DA2DDBF000FE1B8 /* FlexibleSizeExampleView.m in Sources */, 2DD323DD1DA2DDBF000FE1B8 /* UpdatePropertiesExampleView.m in Sources */, + 2DD323E01DA2DDBF000FE1B8 /* main.m in Sources */, 2DD323DE1DA2DDBF000FE1B8 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme index aa4516c991545d..f5551bb9306749 100644 --- a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme +++ b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme @@ -62,6 +62,20 @@ ReferencedContainer = "container:UIExplorerCxx.xcodeproj"> + + + + + + + + + + + + diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index df74872f348bdf..ff90ac2ea0e7f6 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -11,7 +11,6 @@ #import #import -#import #import #import #import @@ -20,7 +19,6 @@ #import "RCTTestModule.h" static const NSTimeInterval kTestTimeoutSeconds = 120; -static const NSTimeInterval kTestTeardownTimeoutSeconds = 30; @implementation RCTTestRunner { @@ -97,8 +95,6 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName configurationBlock:(void(^)(RCTRootView *rootView))configurationBlock expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock { - __weak id weakJSContext; - @autoreleasepool { __block NSString *error = nil; RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { @@ -139,12 +135,6 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; } - // Take a weak reference to the JS context, so we track its deallocation later - // (we can only do this now, since it's been lazily initialized) - id jsExecutor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"]; - if ([jsExecutor isKindOfClass:[RCTJSCExecutor class]]) { - weakJSContext = [jsExecutor valueForKey:@"_context"]; - } [rootView removeFromSuperview]; RCTSetLogFunction(RCTDefaultLogFunction); @@ -163,14 +153,6 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName } [bridge invalidate]; } - - // Wait for the executor to have shut down completely before returning - NSDate *teardownTimeout = [NSDate dateWithTimeIntervalSinceNow:kTestTeardownTimeoutSeconds]; - while (teardownTimeout.timeIntervalSinceNow > 0 && weakJSContext) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } - RCTAssert(!weakJSContext, @"JS context was not deallocated after being invalidated"); } @end diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 480df01a953518..56c5a6db05e355 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -21,10 +21,11 @@ #import "RCTLog.h" #import "RCTModuleData.h" #import "RCTPerformanceLogger.h" -#import "RCTProfile.h" -#import "RCTRedBox.h" #import "RCTUtils.h" +#import +#import + #if RCT_DEV && __has_include("RCTDevLoadingView.h") #import "RCTDevLoadingView.h" #endif diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 0fe05ca8bb4ae1..51074272337c55 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -276,31 +276,7 @@ - (void)requestReload [self reload]; } -- (void)setUp -{ - RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil); - - _performanceLogger = [RCTPerformanceLogger new]; - [_performanceLogger markStartForTag:RCTPLBridgeStartup]; - [_performanceLogger markStartForTag:RCTPLTTI]; - - // Only update bundleURL from delegate if delegate bundleURL has changed - NSURL *previousDelegateURL = _delegateBundleURL; - _delegateBundleURL = [self.delegate sourceURLForBridge:self]; - if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) { - _bundleURL = _delegateBundleURL; - } - - // Sanitize the bundle URL - _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString]; - - [self createBatchedBridge]; - [self.batchedBridge start]; - - RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); -} - -- (void)createBatchedBridge +- (Class)bridgeClass { // In order to facilitate switching between bridges with only build // file changes, this uses reflection to check which bridges are @@ -326,7 +302,41 @@ - (void)createBatchedBridge RCTAssert(implClass != nil, @"No bridge implementation is available, giving up."); - self.batchedBridge = [[implClass alloc] initWithParentBridge:self]; +#ifdef WITH_FBSYSTRACE + if (implClass == cxxBridgeClass) { + [RCTFBSystrace registerCallbacks]; + } else { + [RCTFBSystrace unregisterCallbacks]; + } +#endif + + return implClass; +} + +- (void)setUp +{ + Class bridgeClass = self.bridgeClass; + + RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil); + + _performanceLogger = [RCTPerformanceLogger new]; + [_performanceLogger markStartForTag:RCTPLBridgeStartup]; + [_performanceLogger markStartForTag:RCTPLTTI]; + + // Only update bundleURL from delegate if delegate bundleURL has changed + NSURL *previousDelegateURL = _delegateBundleURL; + _delegateBundleURL = [self.delegate sourceURLForBridge:self]; + if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) { + _bundleURL = _delegateBundleURL; + } + + // Sanitize the bundle URL + _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString]; + + self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self]; + [self.batchedBridge start]; + + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); } - (BOOL)isLoading diff --git a/React/Executors/RCTJSCErrorHandling.h b/React/Base/RCTJSCErrorHandling.h similarity index 100% rename from React/Executors/RCTJSCErrorHandling.h rename to React/Base/RCTJSCErrorHandling.h diff --git a/React/Executors/RCTJSCErrorHandling.mm b/React/Base/RCTJSCErrorHandling.mm similarity index 100% rename from React/Executors/RCTJSCErrorHandling.mm rename to React/Base/RCTJSCErrorHandling.mm diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index ee84db6be1adf5..461b01d8f39f61 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -39,6 +39,7 @@ #import #import "NSDataBigString.h" +#import "RCTJSCHelpers.h" #import "RCTMessageThread.h" #import "RCTObjcExecutor.h" @@ -67,26 +68,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { RCTBridgeFieldCallID, }; -static JSValueRef nativeLoggingHook( - JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, - const JSValueRef arguments[], JSValueRef *exception) { - RCTLogLevel level = RCTLogLevelInfo; - if (argumentCount > 1) { - level = MAX(level, (RCTLogLevel)Value(ctx, arguments[1]).asNumber()); - } - if (argumentCount > 0) { - String message = Value(ctx, arguments[0]).toString(); - _RCTLogJavaScriptInternal(level, @(message.str().c_str())); - } - return Value::makeUndefined(ctx); -} - -static JSValueRef nativePerformanceNow( - JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, - const JSValueRef arguments[], JSValueRef *exception) { - return Value::makeNumber(ctx, CACurrentMediaTime() * 1000); -} - static bool isRAMBundle(NSData *script) { BundleHeader header; [script getBytes:&header length:sizeof(header)]; @@ -151,10 +132,7 @@ @implementation RCTCxxBridge + (void)initialize { if (self == [RCTCxxBridge class]) { - ReactMarker::logMarker = [](const std::string&) {}; - PerfLogging::installNativeHooks = RCTFBQuickPerformanceLoggerConfigureHooks; - JSNativeHooks::loggingHook = nativeLoggingHook; - JSNativeHooks::nowHook = nativePerformanceNow; + RCTPrepareJSCExecutor(); } } diff --git a/React/CxxBridge/RCTJSCHelpers.h b/React/CxxBridge/RCTJSCHelpers.h new file mode 100644 index 00000000000000..3c3775b31c8acd --- /dev/null +++ b/React/CxxBridge/RCTJSCHelpers.h @@ -0,0 +1,19 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +/** + * 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. + */ + +#pragma once + +/** + * This must be invoked on iOS to set up platform dependencies before + * creating an instance of JSCExecutor. + */ + +void RCTPrepareJSCExecutor(); diff --git a/React/CxxBridge/RCTJSCHelpers.mm b/React/CxxBridge/RCTJSCHelpers.mm new file mode 100644 index 00000000000000..41651c1c7607b1 --- /dev/null +++ b/React/CxxBridge/RCTJSCHelpers.mm @@ -0,0 +1,52 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +/** + * 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. + */ + +#include "RCTJSCHelpers.h" + +#import + +#import +#import +#import +#import + +using namespace facebook::react; + +namespace { + +JSValueRef nativeLoggingHook( + JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef *exception) { + RCTLogLevel level = RCTLogLevelInfo; + if (argumentCount > 1) { + level = MAX(level, (RCTLogLevel)Value(ctx, arguments[1]).asNumber()); + } + if (argumentCount > 0) { + String message = Value(ctx, arguments[0]).toString(); + _RCTLogJavaScriptInternal(level, @(message.str().c_str())); + } + return Value::makeUndefined(ctx); +} + +JSValueRef nativePerformanceNow( + JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef *exception) { + return Value::makeNumber(ctx, CACurrentMediaTime() * 1000); +} + +} + +void RCTPrepareJSCExecutor() { + ReactMarker::logMarker = [](const std::string&) {}; + PerfLogging::installNativeHooks = RCTFBQuickPerformanceLoggerConfigureHooks; + JSNativeHooks::loggingHook = nativeLoggingHook; + JSNativeHooks::nowHook = nativePerformanceNow; +} diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 18a645a56a53ad..75be11f54b6fe9 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -19,19 +19,19 @@ #import #import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import -#import "JSCSamplingProfiler.h" -#import "RCTAssert.h" -#import "RCTBridge+Private.h" -#import "RCTDefines.h" -#import "RCTDevSettings.h" -#import "RCTJSCErrorHandling.h" #import "RCTJSCProfiler.h" -#import "RCTJavaScriptLoader.h" -#import "RCTLog.h" -#import "RCTPerformanceLogger.h" -#import "RCTProfile.h" -#import "RCTUtils.h" #if (RCT_PROFILE || RCT_DEV) && __has_include("RCTDevMenu.h") #import "RCTDevMenu.h" diff --git a/React/Profiler/RCTJSCProfiler.m b/React/Profiler/RCTJSCProfiler.m index 7ae2a2b2f5f088..4d5333b777614b 100644 --- a/React/Profiler/RCTJSCProfiler.m +++ b/React/Profiler/RCTJSCProfiler.m @@ -11,7 +11,7 @@ #import -#import "RCTLog.h" +#import #ifndef RCT_JSC_PROFILER #define RCT_JSC_PROFILER RCT_PROFILE diff --git a/React/Profiler/RCTPerfMonitor.m b/React/Profiler/RCTPerfMonitor.m index 4d6b6e1cfcc44d..52efbcf267f6ab 100644 --- a/React/Profiler/RCTPerfMonitor.m +++ b/React/Profiler/RCTPerfMonitor.m @@ -20,7 +20,6 @@ #import "RCTFPSGraph.h" #import "RCTInvalidating.h" #import "RCTJavaScriptExecutor.h" -#import "RCTJSCExecutor.h" #import "RCTPerformanceLogger.h" #import "RCTRootView.h" #import "RCTUIManager.h" diff --git a/React/Profiler/RCTProfile.m b/React/Profiler/RCTProfile.m index d2b0f479d136a7..47f5eb87431fea 100644 --- a/React/Profiler/RCTProfile.m +++ b/React/Profiler/RCTProfile.m @@ -22,7 +22,6 @@ #import "RCTBridge.h" #import "RCTComponentData.h" #import "RCTDefines.h" -#import "RCTJSCExecutor.h" #import "RCTLog.h" #import "RCTModuleData.h" #import "RCTUIManager.h" @@ -473,7 +472,8 @@ void RCTProfileInit(RCTBridge *bridge) // Set up thread ordering dispatch_async(RCTProfileGetQueue(), ^{ - NSArray *orderedThreads = @[@"JS async", @"RCTPerformanceLogger", RCTJSCThreadName, @(RCTUIManagerQueueName), @"main"]; + NSArray *orderedThreads = @[@"JS async", @"RCTPerformanceLogger", @"com.facebook.react.JavaScript", + @(RCTUIManagerQueueName), @"main"]; [orderedThreads enumerateObjectsUsingBlock:^(NSString *thread, NSUInteger idx, __unused BOOL *stop) { RCTProfileAddEvent(RCTProfileTraceEvents, @"ph": @"M", // metadata event diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index a79946fbfa4b40..b807434e77b8af 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E21AA5CF210034F82E /* RCTTabBarItem.m */; }; 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */; }; 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E61AA5CF210034F82E /* RCTTabBarManager.m */; }; + 139324FE1E70B069009FD7E0 /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 139324FC1E70B069009FD7E0 /* RCTJSCErrorHandling.h */; }; + 139324FF1E70B069009FD7E0 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 139324FD1E70B069009FD7E0 /* RCTJSCErrorHandling.mm */; }; 13A0C2891B74F71200B29F6F /* RCTDevLoadingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A0C2861B74F71200B29F6F /* RCTDevLoadingView.m */; }; 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; }; 13A6E20E1C19AA0C00845B82 /* RCTParserUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A6E20D1C19AA0C00845B82 /* RCTParserUtils.m */; }; @@ -211,7 +213,6 @@ 3D302F4E1DF828F800D6DDAE /* RCTURLRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D302F4F1DF828F800D6DDAE /* RCTUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D302F501DF828F800D6DDAE /* RCTWebSocketObserverProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D302F521DF828F800D6DDAE /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; 3D302F531DF828F800D6DDAE /* RCTJSCExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D302F541DF828F800D6DDAE /* JSCSamplingProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D302F551DF828F800D6DDAE /* RCTAccessibilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; @@ -322,7 +323,6 @@ 3D302FCA1DF8290600D6DDAE /* RCTURLRequestHandler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D302FCB1DF8290600D6DDAE /* RCTUtils.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D302FCC1DF8290600D6DDAE /* RCTWebSocketObserverProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D302FCE1DF8290600D6DDAE /* RCTJSCErrorHandling.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; 3D302FCF1DF8290600D6DDAE /* RCTJSCExecutor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D302FD01DF8290600D6DDAE /* JSCSamplingProfiler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D302FD11DF8290600D6DDAE /* RCTAccessibilityManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; @@ -414,8 +414,6 @@ 3D5AC7221E005763000F9153 /* RCTTVRemoteHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5AC71E1E005750000F9153 /* RCTTVRemoteHandler.h */; }; 3D5AC7231E005766000F9153 /* RCTTVRemoteHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5AC71F1E005750000F9153 /* RCTTVRemoteHandler.m */; }; 3D7749441DC1065C007EC8D8 /* RCTPlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7749431DC1065C007EC8D8 /* RCTPlatform.m */; }; - 3D7A27E21DE325B7002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; }; - 3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; }; 3D80D9171DF6F7A80028D040 /* JSBundleType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */; }; 3D80D9181DF6F7A80028D040 /* JSBundleType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */; }; 3D80D9191DF6F7CF0028D040 /* JSCWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27DD1DE32541002E3F95 /* JSCWrapper.cpp */; }; @@ -465,7 +463,6 @@ 3D80D9491DF6FA890028D040 /* RCTURLRequestHandler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D80D94A1DF6FA890028D040 /* RCTUtils.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D80D94B1DF6FA890028D040 /* RCTWebSocketObserverProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D80D94D1DF6FA890028D040 /* RCTJSCErrorHandling.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; 3D80D94E1DF6FA890028D040 /* RCTJSCExecutor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D80D94F1DF6FA890028D040 /* JSCSamplingProfiler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D80D9501DF6FA890028D040 /* RCTAccessibilityManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; @@ -583,7 +580,6 @@ 3D80DA431DF820620028D040 /* RCTURLRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D80DA441DF820620028D040 /* RCTUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D80DA451DF820620028D040 /* RCTWebSocketObserverProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D80DA471DF820620028D040 /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; 3D80DA481DF820620028D040 /* RCTJSCExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D80DA491DF820620028D040 /* JSCSamplingProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D80DA4A1DF820620028D040 /* RCTAccessibilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; @@ -853,7 +849,6 @@ 3D302FCA1DF8290600D6DDAE /* RCTURLRequestHandler.h in Copy Headers */, 3D302FCB1DF8290600D6DDAE /* RCTUtils.h in Copy Headers */, 3D302FCC1DF8290600D6DDAE /* RCTWebSocketObserverProtocol.h in Copy Headers */, - 3D302FCE1DF8290600D6DDAE /* RCTJSCErrorHandling.h in Copy Headers */, 3D302FCF1DF8290600D6DDAE /* RCTJSCExecutor.h in Copy Headers */, 3D302FD01DF8290600D6DDAE /* JSCSamplingProfiler.h in Copy Headers */, 3D302FD11DF8290600D6DDAE /* RCTAccessibilityManager.h in Copy Headers */, @@ -1012,7 +1007,6 @@ 3D80D9491DF6FA890028D040 /* RCTURLRequestHandler.h in Copy Headers */, 3D80D94A1DF6FA890028D040 /* RCTUtils.h in Copy Headers */, 3D80D94B1DF6FA890028D040 /* RCTWebSocketObserverProtocol.h in Copy Headers */, - 3D80D94D1DF6FA890028D040 /* RCTJSCErrorHandling.h in Copy Headers */, 3D80D94E1DF6FA890028D040 /* RCTJSCExecutor.h in Copy Headers */, 3D80D94F1DF6FA890028D040 /* JSCSamplingProfiler.h in Copy Headers */, 3D80D9501DF6FA890028D040 /* RCTAccessibilityManager.h in Copy Headers */, @@ -1171,6 +1165,8 @@ 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarItemManager.m; sourceTree = ""; }; 137327E51AA5CF210034F82E /* RCTTabBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarManager.h; sourceTree = ""; }; 137327E61AA5CF210034F82E /* RCTTabBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarManager.m; sourceTree = ""; }; + 139324FC1E70B069009FD7E0 /* RCTJSCErrorHandling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCErrorHandling.h; sourceTree = ""; }; + 139324FD1E70B069009FD7E0 /* RCTJSCErrorHandling.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCErrorHandling.mm; sourceTree = ""; }; 13A0C2851B74F71200B29F6F /* RCTDevLoadingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTDevLoadingView.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 13A0C2861B74F71200B29F6F /* RCTDevLoadingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevLoadingView.m; sourceTree = ""; }; 13A0C2871B74F71200B29F6F /* RCTDevMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTDevMenu.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -1309,9 +1305,7 @@ 3D7A27DC1DE32541002E3F95 /* JavaScriptCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JavaScriptCore.h; sourceTree = ""; }; 3D7A27DD1DE32541002E3F95 /* JSCWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCWrapper.cpp; sourceTree = ""; }; 3D7A27DE1DE32541002E3F95 /* JSCWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCWrapper.h; sourceTree = ""; }; - 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCErrorHandling.mm; sourceTree = ""; }; 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketObserverProtocol.h; sourceTree = ""; }; - 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCErrorHandling.h; sourceTree = ""; }; 3EDCA8A21D3591E700450C31 /* RCTErrorCustomizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorCustomizer.h; sourceTree = ""; }; 3EDCA8A31D3591E700450C31 /* RCTErrorInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorInfo.h; sourceTree = ""; }; 3EDCA8A41D3591E700450C31 /* RCTErrorInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTErrorInfo.m; sourceTree = ""; }; @@ -1433,8 +1427,6 @@ 134FCB381A6E7F0800051CC8 /* Executors */ = { isa = PBXGroup; children = ( - 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */, - 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */, 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */, 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */, ); @@ -1726,6 +1718,8 @@ 83CBBA491A601E3B00E9B192 /* Base */ = { isa = PBXGroup; children = ( + 139324FC1E70B069009FD7E0 /* RCTJSCErrorHandling.h */, + 139324FD1E70B069009FD7E0 /* RCTJSCErrorHandling.mm */, 83CBBA4A1A601E3B00E9B192 /* RCTAssert.h */, 83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */, 14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */, @@ -1894,7 +1888,6 @@ 3D302F4E1DF828F800D6DDAE /* RCTURLRequestHandler.h in Headers */, 3D302F4F1DF828F800D6DDAE /* RCTUtils.h in Headers */, 3D302F501DF828F800D6DDAE /* RCTWebSocketObserverProtocol.h in Headers */, - 3D302F521DF828F800D6DDAE /* RCTJSCErrorHandling.h in Headers */, 3D302F531DF828F800D6DDAE /* RCTJSCExecutor.h in Headers */, 3D302F541DF828F800D6DDAE /* JSCSamplingProfiler.h in Headers */, 3D302F551DF828F800D6DDAE /* RCTAccessibilityManager.h in Headers */, @@ -2084,7 +2077,6 @@ 3D80DA431DF820620028D040 /* RCTURLRequestHandler.h in Headers */, 3D80DA441DF820620028D040 /* RCTUtils.h in Headers */, 3D80DA451DF820620028D040 /* RCTWebSocketObserverProtocol.h in Headers */, - 3D80DA471DF820620028D040 /* RCTJSCErrorHandling.h in Headers */, 3D80DA481DF820620028D040 /* RCTJSCExecutor.h in Headers */, 3D80DA491DF820620028D040 /* JSCSamplingProfiler.h in Headers */, 3D80DA4A1DF820620028D040 /* RCTAccessibilityManager.h in Headers */, @@ -2152,6 +2144,7 @@ 3D80DA881DF820620028D040 /* RCTTabBar.h in Headers */, 3D80DA891DF820620028D040 /* RCTTabBarItem.h in Headers */, 3D80DA8A1DF820620028D040 /* RCTTabBarItemManager.h in Headers */, + 139324FE1E70B069009FD7E0 /* RCTJSCErrorHandling.h in Headers */, 3D80DA8B1DF820620028D040 /* RCTTabBarManager.h in Headers */, 3D80DA8C1DF820620028D040 /* RCTTextDecorationLineType.h in Headers */, 3D80DA8D1DF820620028D040 /* RCTView.h in Headers */, @@ -2495,7 +2488,6 @@ 594AD5D41E46D87500B07237 /* RCTScrollContentViewManager.m in Sources */, A12E9E5D1E5DF8720029001B /* RCTReloadPackagerMethod.m in Sources */, 3D5AC71A1E0056E0000F9153 /* RCTTVNavigationEventEmitter.m in Sources */, - 3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */, 2D3B5EA61D9B08CA00451313 /* RCTTouchEvent.m in Sources */, 2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */, 2D3B5EF01D9B09E300451313 /* RCTWrapperViewController.m in Sources */, @@ -2631,8 +2623,8 @@ 13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */, A2440AA31DF8D854006E7BFC /* RCTReloadCommand.m in Sources */, E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */, - 3D7A27E21DE325B7002E3F95 /* RCTJSCErrorHandling.mm in Sources */, 13A0C2891B74F71200B29F6F /* RCTDevLoadingView.m in Sources */, + 139324FF1E70B069009FD7E0 /* RCTJSCErrorHandling.mm in Sources */, 13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */, 1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */, 134FCB3D1A6E7F0800051CC8 /* RCTJSCExecutor.mm in Sources */, diff --git a/React/ReactCxx.xcodeproj/project.pbxproj b/React/ReactCxx.xcodeproj/project.pbxproj index c375179209e0dc..b1f0b1ccccc6ef 100644 --- a/React/ReactCxx.xcodeproj/project.pbxproj +++ b/React/ReactCxx.xcodeproj/project.pbxproj @@ -25,18 +25,12 @@ 130E3D891E6A082100ACE484 /* RCTDevSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = 130E3D871E6A082100ACE484 /* RCTDevSettings.mm */; }; 130E3D8A1E6A083600ACE484 /* RCTDevSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 130E3D861E6A082100ACE484 /* RCTDevSettings.h */; }; 130E3D8B1E6A083900ACE484 /* RCTDevSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = 130E3D871E6A082100ACE484 /* RCTDevSettings.mm */; }; - 13134C841E296B2A00B9F3CB /* RCTCxxBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C731E296B2A00B9F3CB /* RCTCxxBridge.h */; }; - 13134C851E296B2A00B9F3CB /* RCTCxxBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C731E296B2A00B9F3CB /* RCTCxxBridge.h */; }; 13134C861E296B2A00B9F3CB /* RCTCxxBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C741E296B2A00B9F3CB /* RCTCxxBridge.mm */; }; 13134C871E296B2A00B9F3CB /* RCTCxxBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C741E296B2A00B9F3CB /* RCTCxxBridge.mm */; }; 13134C8C1E296B2A00B9F3CB /* RCTMessageThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C771E296B2A00B9F3CB /* RCTMessageThread.h */; }; 13134C8D1E296B2A00B9F3CB /* RCTMessageThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C771E296B2A00B9F3CB /* RCTMessageThread.h */; }; 13134C8E1E296B2A00B9F3CB /* RCTMessageThread.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C781E296B2A00B9F3CB /* RCTMessageThread.mm */; }; 13134C8F1E296B2A00B9F3CB /* RCTMessageThread.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C781E296B2A00B9F3CB /* RCTMessageThread.mm */; }; - 13134C901E296B2A00B9F3CB /* RCTNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C791E296B2A00B9F3CB /* RCTNativeModule.h */; }; - 13134C911E296B2A00B9F3CB /* RCTNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C791E296B2A00B9F3CB /* RCTNativeModule.h */; }; - 13134C921E296B2A00B9F3CB /* RCTNativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C7A1E296B2A00B9F3CB /* RCTNativeModule.mm */; }; - 13134C931E296B2A00B9F3CB /* RCTNativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C7A1E296B2A00B9F3CB /* RCTNativeModule.mm */; }; 13134C941E296B2A00B9F3CB /* RCTObjcExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C7B1E296B2A00B9F3CB /* RCTObjcExecutor.h */; }; 13134C951E296B2A00B9F3CB /* RCTObjcExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 13134C7B1E296B2A00B9F3CB /* RCTObjcExecutor.h */; }; 13134C961E296B2A00B9F3CB /* RCTObjcExecutor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13134C7C1E296B2A00B9F3CB /* RCTObjcExecutor.mm */; }; @@ -61,14 +55,27 @@ 1339578B1DF76D3500EC27BE /* Yoga.h in Headers */ = {isa = PBXBuildFile; fileRef = 130A77081DF767AF001F9587 /* Yoga.h */; }; 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */; }; 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; }; - 134FCB3D1A6E7F0800051CC8 /* RCTJSCExecutor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */; }; 13513F3C1B1F43F400FCE529 /* RCTProgressViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13513F3B1B1F43F400FCE529 /* RCTProgressViewManager.m */; }; + 135A9BFB1E7B0EAE00587AEB /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 135A9BF91E7B0EAE00587AEB /* RCTJSCErrorHandling.h */; }; + 135A9BFC1E7B0EAE00587AEB /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 135A9BFA1E7B0EAE00587AEB /* RCTJSCErrorHandling.mm */; }; + 135A9BFF1E7B0EE600587AEB /* RCTJSCHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 135A9BFD1E7B0EE600587AEB /* RCTJSCHelpers.h */; }; + 135A9C001E7B0EE600587AEB /* RCTJSCHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 135A9BFE1E7B0EE600587AEB /* RCTJSCHelpers.mm */; }; + 135A9C011E7B0F4700587AEB /* systemJSCWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */; }; + 135A9C021E7B0F4800587AEB /* systemJSCWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */; }; + 135A9C031E7B0F6100587AEB /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 135A9BF91E7B0EAE00587AEB /* RCTJSCErrorHandling.h */; }; + 135A9C041E7B0F6400587AEB /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 135A9BFA1E7B0EAE00587AEB /* RCTJSCErrorHandling.mm */; }; + 135A9C051E7B0F7500587AEB /* RCTJSCHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 135A9BFE1E7B0EE600587AEB /* RCTJSCHelpers.mm */; }; + 135A9C061E7B0F7800587AEB /* RCTJSCHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 135A9BFD1E7B0EE600587AEB /* RCTJSCHelpers.h */; }; 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; }; 1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7091AB030C200659ED6 /* RCTAppState.m */; }; 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E01AA5CF210034F82E /* RCTTabBar.m */; }; 137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E21AA5CF210034F82E /* RCTTabBarItem.m */; }; 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */; }; 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E61AA5CF210034F82E /* RCTTabBarManager.m */; }; + 1384E2081E806D4E00545659 /* RCTNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 1384E2061E806D4E00545659 /* RCTNativeModule.h */; }; + 1384E2091E806D4E00545659 /* RCTNativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1384E2071E806D4E00545659 /* RCTNativeModule.mm */; }; + 1384E20A1E806D5700545659 /* RCTNativeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 1384E2061E806D4E00545659 /* RCTNativeModule.h */; }; + 1384E20B1E806D5B00545659 /* RCTNativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1384E2071E806D4E00545659 /* RCTNativeModule.mm */; }; 139D7E4C1E25C5A300323FB7 /* bignum-dtoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 139D7E3A1E25C5A300323FB7 /* bignum-dtoa.h */; }; 139D7E4E1E25C5A300323FB7 /* bignum.h in Headers */ = {isa = PBXBuildFile; fileRef = 139D7E3C1E25C5A300323FB7 /* bignum.h */; }; 139D7E501E25C5A300323FB7 /* cached-powers.h in Headers */ = {isa = PBXBuildFile; fileRef = 139D7E3E1E25C5A300323FB7 /* cached-powers.h */; }; @@ -167,7 +174,6 @@ 13F880391E296D2800C3C7A1 /* noncopyable.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B1091E0369AD0018521A /* noncopyable.h */; }; 13F8803A1E296D2800C3C7A1 /* Unicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B10B1E0369AD0018521A /* Unicode.h */; }; 13F8803B1E296D2800C3C7A1 /* Value.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D92B10D1E0369AD0018521A /* Value.h */; }; - 13F8803C1E296DF600C3C7A1 /* RCTCxxBridge.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13134C731E296B2A00B9F3CB /* RCTCxxBridge.h */; }; 13F880411E29709F00C3C7A1 /* libdouble-conversion.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139D7E881E25C6D100323FB7 /* libdouble-conversion.a */; }; 13F887581E2971D400C3C7A1 /* Demangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F887521E2971C500C3C7A1 /* Demangle.cpp */; }; 13F887591E2971D400C3C7A1 /* StringBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13F887531E2971C500C3C7A1 /* StringBase.cpp */; }; @@ -214,11 +220,9 @@ 1450FF871BCFF28A00208362 /* RCTProfileTrampoline-arm.S in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF821BCFF28A00208362 /* RCTProfileTrampoline-arm.S */; }; 1450FF881BCFF28A00208362 /* RCTProfileTrampoline-arm64.S in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF831BCFF28A00208362 /* RCTProfileTrampoline-arm64.S */; }; 1450FF8A1BCFF28A00208362 /* RCTProfileTrampoline-x86_64.S in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF851BCFF28A00208362 /* RCTProfileTrampoline-x86_64.S */; }; - 14A43DF31C20B1C900794BC8 /* RCTJSCProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 14A43DF21C20B1C900794BC8 /* RCTJSCProfiler.m */; }; 14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */; }; 14C2CA741B3AC64300E6CBB2 /* RCTModuleData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA731B3AC64300E6CBB2 /* RCTModuleData.mm */; }; 14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */; }; - 14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */; }; 14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; }; 14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; }; 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; }; @@ -283,7 +287,6 @@ 2D3B5E931D9B087300451313 /* RCTErrorInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCA8A41D3591E700450C31 /* RCTErrorInfo.m */; }; 2D3B5E941D9B087900451313 /* RCTBundleURLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 68EFE4ED1CF6EB3900A1DE13 /* RCTBundleURLProvider.m */; }; 2D3B5E951D9B087C00451313 /* RCTAssert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */; }; - 2D3B5E961D9B088500451313 /* RCTBatchedBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */; }; 2D3B5E971D9B089000451313 /* RCTBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */; }; 2D3B5E981D9B089500451313 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; }; 2D3B5E991D9B089A00451313 /* RCTDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D1E68D91CABD13900DD7465 /* RCTDisplayLink.m */; }; @@ -301,7 +304,6 @@ 2D3B5EA61D9B08CA00451313 /* RCTTouchEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 391E86A21C623EC800009732 /* RCTTouchEvent.m */; }; 2D3B5EA71D9B08CE00451313 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; }; 2D3B5EA81D9B08D300451313 /* RCTUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA501A601E3B00E9B192 /* RCTUtils.m */; }; - 2D3B5EAC1D9B08EF00451313 /* RCTJSCExecutor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */; }; 2D3B5EAE1D9B08F800451313 /* RCTEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D9FEEA1CDCCECF00158BD7 /* RCTEventEmitter.m */; }; 2D3B5EAF1D9B08FB00451313 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; 2D3B5EB01D9B08FE00451313 /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; }; @@ -317,7 +319,6 @@ 2D3B5EBC1D9B092600451313 /* RCTKeyboardObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D9FEED1CDCD93000158BD7 /* RCTKeyboardObserver.m */; }; 2D3B5EBD1D9B092A00451313 /* RCTTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEE1A69327A00A75B9A /* RCTTiming.m */; }; 2D3B5EBE1D9B092D00451313 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; }; - 2D3B5EBF1D9B093300451313 /* RCTJSCProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 14A43DF21C20B1C900794BC8 /* RCTJSCProfiler.m */; }; 2D3B5EC01D9B093600451313 /* RCTPerfMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EB1BDA3B3C003C6C10 /* RCTPerfMonitor.m */; }; 2D3B5EC11D9B093900451313 /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */; }; 2D3B5EC21D9B093B00451313 /* RCTProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 1450FF811BCFF28A00208362 /* RCTProfile.m */; }; @@ -408,8 +409,6 @@ 3D302F4E1DF828F800D6DDAE /* RCTURLRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D302F4F1DF828F800D6DDAE /* RCTUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D302F501DF828F800D6DDAE /* RCTWebSocketObserverProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D302F521DF828F800D6DDAE /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; - 3D302F531DF828F800D6DDAE /* RCTJSCExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D302F541DF828F800D6DDAE /* JSCSamplingProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D302F551DF828F800D6DDAE /* RCTAccessibilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; 3D302F561DF828F800D6DDAE /* RCTAlertManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */; }; @@ -429,7 +428,6 @@ 3D302F641DF828F800D6DDAE /* RCTTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 13B07FED1A69327A00A75B9A /* RCTTiming.h */; }; 3D302F651DF828F800D6DDAE /* RCTUIManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E067481A70F434002CDEE1 /* RCTUIManager.h */; }; 3D302F661DF828F800D6DDAE /* RCTFPSGraph.h in Headers */ = {isa = PBXBuildFile; fileRef = 14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */; }; - 3D302F671DF828F800D6DDAE /* RCTJSCProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 14A43DF11C20B1C900794BC8 /* RCTJSCProfiler.h */; }; 3D302F681DF828F800D6DDAE /* RCTMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 14BF71811C04795500C97D0C /* RCTMacros.h */; }; 3D302F691DF828F800D6DDAE /* RCTProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 1450FF801BCFF28A00208362 /* RCTProfile.h */; }; 3D302F6A1DF828F800D6DDAE /* RCTActivityIndicatorView.h in Headers */ = {isa = PBXBuildFile; fileRef = B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */; }; @@ -492,8 +490,6 @@ 3D7454801E5475AF00E74ADD /* RecoverableError.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D7454791E54757500E74ADD /* RecoverableError.h */; }; 3D7454811E5475AF00E74ADD /* RecoverableError.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D7454791E54757500E74ADD /* RecoverableError.h */; }; 3D7749441DC1065C007EC8D8 /* RCTPlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7749431DC1065C007EC8D8 /* RCTPlatform.m */; }; - 3D7A27E21DE325B7002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; }; - 3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; }; 3D7AA9C41E548CD5001955CF /* NSDataBigString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7AA9C31E548CD5001955CF /* NSDataBigString.mm */; }; 3D7AA9C51E548CDB001955CF /* NSDataBigString.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D7454B31E54786200E74ADD /* NSDataBigString.h */; }; 3D7AA9C61E548CDD001955CF /* NSDataBigString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7AA9C31E548CD5001955CF /* NSDataBigString.mm */; }; @@ -543,8 +539,6 @@ 3D80D9491DF6FA890028D040 /* RCTURLRequestHandler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D80D94A1DF6FA890028D040 /* RCTUtils.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D80D94B1DF6FA890028D040 /* RCTWebSocketObserverProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D80D94D1DF6FA890028D040 /* RCTJSCErrorHandling.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; - 3D80D94E1DF6FA890028D040 /* RCTJSCExecutor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D80D94F1DF6FA890028D040 /* JSCSamplingProfiler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D80D9501DF6FA890028D040 /* RCTAccessibilityManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; 3D80D9511DF6FA890028D040 /* RCTAlertManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */; }; @@ -564,7 +558,6 @@ 3D80D95F1DF6FA890028D040 /* RCTTiming.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B07FED1A69327A00A75B9A /* RCTTiming.h */; }; 3D80D9601DF6FA890028D040 /* RCTUIManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13E067481A70F434002CDEE1 /* RCTUIManager.h */; }; 3D80D9611DF6FA890028D040 /* RCTFPSGraph.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */; }; - 3D80D9621DF6FA890028D040 /* RCTJSCProfiler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14A43DF11C20B1C900794BC8 /* RCTJSCProfiler.h */; }; 3D80D9631DF6FA890028D040 /* RCTMacros.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14BF71811C04795500C97D0C /* RCTMacros.h */; }; 3D80D9641DF6FA890028D040 /* RCTProfile.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 1450FF801BCFF28A00208362 /* RCTProfile.h */; }; 3D80D9651DF6FA890028D040 /* RCTActivityIndicatorView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */; }; @@ -661,8 +654,6 @@ 3D80DA431DF820620028D040 /* RCTURLRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3D80DA441DF820620028D040 /* RCTUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3D80DA451DF820620028D040 /* RCTWebSocketObserverProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3D80DA471DF820620028D040 /* RCTJSCErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; - 3D80DA481DF820620028D040 /* RCTJSCExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3D80DA491DF820620028D040 /* JSCSamplingProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3D80DA4A1DF820620028D040 /* RCTAccessibilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; 3D80DA4B1DF820620028D040 /* RCTAlertManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */; }; @@ -682,7 +673,6 @@ 3D80DA591DF820620028D040 /* RCTTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 13B07FED1A69327A00A75B9A /* RCTTiming.h */; }; 3D80DA5A1DF820620028D040 /* RCTUIManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E067481A70F434002CDEE1 /* RCTUIManager.h */; }; 3D80DA5B1DF820620028D040 /* RCTFPSGraph.h in Headers */ = {isa = PBXBuildFile; fileRef = 14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */; }; - 3D80DA5C1DF820620028D040 /* RCTJSCProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 14A43DF11C20B1C900794BC8 /* RCTJSCProfiler.h */; }; 3D80DA5D1DF820620028D040 /* RCTMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 14BF71811C04795500C97D0C /* RCTMacros.h */; }; 3D80DA5E1DF820620028D040 /* RCTProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 1450FF801BCFF28A00208362 /* RCTProfile.h */; }; 3D80DA5F1DF820620028D040 /* RCTActivityIndicatorView.h in Headers */ = {isa = PBXBuildFile; fileRef = B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */; }; @@ -809,8 +799,6 @@ 3DA981E41E5B0F29004F2374 /* RCTURLRequestHandler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */; }; 3DA981E51E5B0F29004F2374 /* RCTUtils.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */; }; 3DA981E61E5B0F29004F2374 /* RCTWebSocketObserverProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */; }; - 3DA981E71E5B0F7F004F2374 /* RCTJSCErrorHandling.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */; }; - 3DA981E81E5B0F7F004F2374 /* RCTJSCExecutor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */; }; 3DA981E91E5B0F7F004F2374 /* JSCSamplingProfiler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 369123DF1DDC75850095B341 /* JSCSamplingProfiler.h */; }; 3DA981EA1E5B0F7F004F2374 /* RCTAccessibilityManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */; }; 3DA981EB1E5B0F7F004F2374 /* RCTAlertManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */; }; @@ -830,7 +818,6 @@ 3DA981F91E5B0F7F004F2374 /* RCTTiming.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13B07FED1A69327A00A75B9A /* RCTTiming.h */; }; 3DA981FA1E5B0F7F004F2374 /* RCTUIManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 13E067481A70F434002CDEE1 /* RCTUIManager.h */; }; 3DA981FB1E5B0F7F004F2374 /* RCTFPSGraph.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */; }; - 3DA981FC1E5B0F7F004F2374 /* RCTJSCProfiler.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14A43DF11C20B1C900794BC8 /* RCTJSCProfiler.h */; }; 3DA981FD1E5B0F7F004F2374 /* RCTMacros.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 14BF71811C04795500C97D0C /* RCTMacros.h */; }; 3DA981FE1E5B0F7F004F2374 /* RCTProfile.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 1450FF801BCFF28A00208362 /* RCTProfile.h */; }; 3DA981FF1E5B0F7F004F2374 /* RCTActivityIndicatorView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */; }; @@ -1058,8 +1045,6 @@ dstPath = include/React; dstSubfolderSpec = 16; files = ( - 3DA981E71E5B0F7F004F2374 /* RCTJSCErrorHandling.h in Copy Headers */, - 3DA981E81E5B0F7F004F2374 /* RCTJSCExecutor.h in Copy Headers */, 3DA981E91E5B0F7F004F2374 /* JSCSamplingProfiler.h in Copy Headers */, 3DA981EA1E5B0F7F004F2374 /* RCTAccessibilityManager.h in Copy Headers */, 3DA981EB1E5B0F7F004F2374 /* RCTAlertManager.h in Copy Headers */, @@ -1079,7 +1064,6 @@ 3DA981F91E5B0F7F004F2374 /* RCTTiming.h in Copy Headers */, 3DA981FA1E5B0F7F004F2374 /* RCTUIManager.h in Copy Headers */, 3DA981FB1E5B0F7F004F2374 /* RCTFPSGraph.h in Copy Headers */, - 3DA981FC1E5B0F7F004F2374 /* RCTJSCProfiler.h in Copy Headers */, 3DA981FD1E5B0F7F004F2374 /* RCTMacros.h in Copy Headers */, 3DA981FE1E5B0F7F004F2374 /* RCTProfile.h in Copy Headers */, 3DA981FF1E5B0F7F004F2374 /* RCTActivityIndicatorView.h in Copy Headers */, @@ -1252,7 +1236,6 @@ dstPath = include/React; dstSubfolderSpec = 16; files = ( - 13F8803C1E296DF600C3C7A1 /* RCTCxxBridge.h in Copy Headers */, 3D80D91F1DF6FA890028D040 /* RCTImageLoader.h in Copy Headers */, 3D80D9201DF6FA890028D040 /* RCTImageStoreManager.h in Copy Headers */, 3D80D9211DF6FA890028D040 /* RCTResizeMode.h in Copy Headers */, @@ -1297,8 +1280,6 @@ 3D80D9491DF6FA890028D040 /* RCTURLRequestHandler.h in Copy Headers */, 3D80D94A1DF6FA890028D040 /* RCTUtils.h in Copy Headers */, 3D80D94B1DF6FA890028D040 /* RCTWebSocketObserverProtocol.h in Copy Headers */, - 3D80D94D1DF6FA890028D040 /* RCTJSCErrorHandling.h in Copy Headers */, - 3D80D94E1DF6FA890028D040 /* RCTJSCExecutor.h in Copy Headers */, 3D80D94F1DF6FA890028D040 /* JSCSamplingProfiler.h in Copy Headers */, 3D80D9501DF6FA890028D040 /* RCTAccessibilityManager.h in Copy Headers */, 3D80D9511DF6FA890028D040 /* RCTAlertManager.h in Copy Headers */, @@ -1318,7 +1299,6 @@ 3D80D95F1DF6FA890028D040 /* RCTTiming.h in Copy Headers */, 3D80D9601DF6FA890028D040 /* RCTUIManager.h in Copy Headers */, 3D80D9611DF6FA890028D040 /* RCTFPSGraph.h in Copy Headers */, - 3D80D9621DF6FA890028D040 /* RCTJSCProfiler.h in Copy Headers */, 3D80D9631DF6FA890028D040 /* RCTMacros.h in Copy Headers */, 3D80D9641DF6FA890028D040 /* RCTProfile.h in Copy Headers */, 3D80D9651DF6FA890028D040 /* RCTActivityIndicatorView.h in Copy Headers */, @@ -1469,12 +1449,9 @@ 130A77081DF767AF001F9587 /* Yoga.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Yoga.h; sourceTree = ""; }; 130E3D861E6A082100ACE484 /* RCTDevSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevSettings.h; sourceTree = ""; }; 130E3D871E6A082100ACE484 /* RCTDevSettings.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTDevSettings.mm; sourceTree = ""; }; - 13134C731E296B2A00B9F3CB /* RCTCxxBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCxxBridge.h; sourceTree = ""; }; 13134C741E296B2A00B9F3CB /* RCTCxxBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTCxxBridge.mm; sourceTree = ""; }; 13134C771E296B2A00B9F3CB /* RCTMessageThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMessageThread.h; sourceTree = ""; }; 13134C781E296B2A00B9F3CB /* RCTMessageThread.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTMessageThread.mm; sourceTree = ""; }; - 13134C791E296B2A00B9F3CB /* RCTNativeModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNativeModule.h; sourceTree = ""; }; - 13134C7A1E296B2A00B9F3CB /* RCTNativeModule.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeModule.mm; sourceTree = ""; }; 13134C7B1E296B2A00B9F3CB /* RCTObjcExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTObjcExecutor.h; sourceTree = ""; }; 13134C7C1E296B2A00B9F3CB /* RCTObjcExecutor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTObjcExecutor.mm; sourceTree = ""; }; 13134C7E1E296B2A00B9F3CB /* RCTCxxMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCxxMethod.h; sourceTree = ""; }; @@ -1497,10 +1474,12 @@ 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CoreLocation.m"; sourceTree = ""; }; 1345A83A1B265A0E00583190 /* RCTURLRequestDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTURLRequestDelegate.h; sourceTree = ""; }; 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTURLRequestHandler.h; sourceTree = ""; }; - 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTJSCExecutor.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCExecutor.mm; sourceTree = ""; }; 13513F3A1B1F43F400FCE529 /* RCTProgressViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTProgressViewManager.h; sourceTree = ""; }; 13513F3B1B1F43F400FCE529 /* RCTProgressViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTProgressViewManager.m; sourceTree = ""; }; + 135A9BF91E7B0EAE00587AEB /* RCTJSCErrorHandling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCErrorHandling.h; sourceTree = ""; }; + 135A9BFA1E7B0EAE00587AEB /* RCTJSCErrorHandling.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCErrorHandling.mm; sourceTree = ""; }; + 135A9BFD1E7B0EE600587AEB /* RCTJSCHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCHelpers.h; sourceTree = ""; }; + 135A9BFE1E7B0EE600587AEB /* RCTJSCHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCHelpers.mm; sourceTree = ""; }; 13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStatusBarManager.h; sourceTree = ""; }; 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStatusBarManager.m; sourceTree = ""; }; 1372B7081AB030C200659ED6 /* RCTAppState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAppState.h; sourceTree = ""; }; @@ -1513,6 +1492,8 @@ 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarItemManager.m; sourceTree = ""; }; 137327E51AA5CF210034F82E /* RCTTabBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarManager.h; sourceTree = ""; }; 137327E61AA5CF210034F82E /* RCTTabBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarManager.m; sourceTree = ""; }; + 1384E2061E806D4E00545659 /* RCTNativeModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNativeModule.h; sourceTree = ""; }; + 1384E2071E806D4E00545659 /* RCTNativeModule.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeModule.mm; sourceTree = ""; }; 139D7E391E25C5A300323FB7 /* bignum-dtoa.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "bignum-dtoa.cc"; path = "double-conversion-1.1.5/src/bignum-dtoa.cc"; sourceTree = ""; }; 139D7E3A1E25C5A300323FB7 /* bignum-dtoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "bignum-dtoa.h"; path = "double-conversion-1.1.5/src/bignum-dtoa.h"; sourceTree = ""; }; 139D7E3B1E25C5A300323FB7 /* bignum.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bignum.cc; path = "double-conversion-1.1.5/src/bignum.cc"; sourceTree = ""; }; @@ -1652,8 +1633,6 @@ 1450FF851BCFF28A00208362 /* RCTProfileTrampoline-x86_64.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "RCTProfileTrampoline-x86_64.S"; sourceTree = ""; }; 1482F9E61B55B927000ADFF3 /* RCTBridgeDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeDelegate.h; sourceTree = ""; }; 14A43DB81C1F849600794BC8 /* RCTBridge+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTBridge+Private.h"; sourceTree = ""; }; - 14A43DF11C20B1C900794BC8 /* RCTJSCProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCProfiler.h; sourceTree = ""; }; - 14A43DF21C20B1C900794BC8 /* RCTJSCProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSCProfiler.m; sourceTree = ""; }; 14BF717F1C04793D00C97D0C /* RCTProfileTrampoline-i386.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "RCTProfileTrampoline-i386.S"; sourceTree = ""; }; 14BF71811C04795500C97D0C /* RCTMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMacros.h; sourceTree = ""; }; 14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleMethod.h; sourceTree = ""; }; @@ -1661,7 +1640,6 @@ 14C2CA721B3AC64300E6CBB2 /* RCTModuleData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleData.h; sourceTree = ""; }; 14C2CA731B3AC64300E6CBB2 /* RCTModuleData.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTModuleData.mm; sourceTree = ""; }; 14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFrameUpdate.m; sourceTree = ""; }; - 14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBatchedBridge.m; sourceTree = ""; }; 14F362071AABD06A001CE568 /* RCTSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitch.h; sourceTree = ""; }; 14F362081AABD06A001CE568 /* RCTSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitch.m; sourceTree = ""; }; 14F362091AABD06A001CE568 /* RCTSwitchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitchManager.h; sourceTree = ""; }; @@ -1675,7 +1653,7 @@ 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControlManager.m; sourceTree = ""; }; 191E3EBF1C29DC3800C180A6 /* RCTRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControl.h; sourceTree = ""; }; 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControl.m; sourceTree = ""; }; - 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = systemJSCWrapper.cpp; path = ../jschelpers/systemJSCWrapper.cpp; sourceTree = ""; }; + 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = systemJSCWrapper.cpp; sourceTree = ""; }; 27B958731E57587D0096647A /* JSBigString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBigString.cpp; sourceTree = ""; }; 2D2A28131D9B038B00D4039D /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; 352DCFEE1D19F4C20056D623 /* RCTI18nUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTI18nUtil.h; sourceTree = ""; }; @@ -1710,7 +1688,6 @@ 3D7A27DC1DE32541002E3F95 /* JavaScriptCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JavaScriptCore.h; sourceTree = ""; }; 3D7A27DD1DE32541002E3F95 /* JSCWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCWrapper.cpp; sourceTree = ""; }; 3D7A27DE1DE32541002E3F95 /* JSCWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCWrapper.h; sourceTree = ""; }; - 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJSCErrorHandling.mm; sourceTree = ""; }; 3D7AA9C31E548CD5001955CF /* NSDataBigString.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NSDataBigString.mm; sourceTree = ""; }; 3D92B0A71E03699D0018521A /* CxxModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CxxModule.h; sourceTree = ""; }; 3D92B0A81E03699D0018521A /* CxxNativeModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CxxNativeModule.cpp; sourceTree = ""; }; @@ -1764,7 +1741,6 @@ 3D92B10C1E0369AD0018521A /* Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Value.cpp; sourceTree = ""; }; 3D92B10D1E0369AD0018521A /* Value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Value.h; sourceTree = ""; }; 3DB910701C74B21600838BBE /* RCTWebSocketObserverProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketObserverProtocol.h; sourceTree = ""; }; - 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCErrorHandling.h; sourceTree = ""; }; 3EDCA8A21D3591E700450C31 /* RCTErrorCustomizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorCustomizer.h; sourceTree = ""; }; 3EDCA8A31D3591E700450C31 /* RCTErrorInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorInfo.h; sourceTree = ""; }; 3EDCA8A41D3591E700450C31 /* RCTErrorInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTErrorInfo.m; sourceTree = ""; }; @@ -1904,12 +1880,11 @@ children = ( 3D7454B31E54786200E74ADD /* NSDataBigString.h */, 3D7AA9C31E548CD5001955CF /* NSDataBigString.mm */, - 13134C731E296B2A00B9F3CB /* RCTCxxBridge.h */, 13134C741E296B2A00B9F3CB /* RCTCxxBridge.mm */, + 135A9BFD1E7B0EE600587AEB /* RCTJSCHelpers.h */, + 135A9BFE1E7B0EE600587AEB /* RCTJSCHelpers.mm */, 13134C771E296B2A00B9F3CB /* RCTMessageThread.h */, 13134C781E296B2A00B9F3CB /* RCTMessageThread.mm */, - 13134C791E296B2A00B9F3CB /* RCTNativeModule.h */, - 13134C7A1E296B2A00B9F3CB /* RCTNativeModule.mm */, 13134C7B1E296B2A00B9F3CB /* RCTObjcExecutor.h */, 13134C7C1E296B2A00B9F3CB /* RCTObjcExecutor.mm */, ); @@ -1919,6 +1894,8 @@ 13134C7D1E296B2A00B9F3CB /* CxxModule */ = { isa = PBXGroup; children = ( + 1384E2061E806D4E00545659 /* RCTNativeModule.h */, + 1384E2071E806D4E00545659 /* RCTNativeModule.mm */, 13134C7E1E296B2A00B9F3CB /* RCTCxxMethod.h */, 13134C7F1E296B2A00B9F3CB /* RCTCxxMethod.mm */, 13134C801E296B2A00B9F3CB /* RCTCxxModule.h */, @@ -1929,17 +1906,6 @@ path = CxxModule; sourceTree = ""; }; - 134FCB381A6E7F0800051CC8 /* Executors */ = { - isa = PBXGroup; - children = ( - 3DC724301D8BF99A00808C32 /* RCTJSCErrorHandling.h */, - 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */, - 134FCB391A6E7F0800051CC8 /* RCTJSCExecutor.h */, - 134FCB3A1A6E7F0800051CC8 /* RCTJSCExecutor.mm */, - ); - path = Executors; - sourceTree = ""; - }; 139D7E381E25C55B00323FB7 /* double-conversion */ = { isa = PBXGroup; children = ( @@ -2183,8 +2149,6 @@ children = ( 14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */, 14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */, - 14A43DF11C20B1C900794BC8 /* RCTJSCProfiler.h */, - 14A43DF21C20B1C900794BC8 /* RCTJSCProfiler.m */, 14BF71811C04795500C97D0C /* RCTMacros.h */, 14F7A0EB1BDA3B3C003C6C10 /* RCTPerfMonitor.m */, 1450FF801BCFF28A00208362 /* RCTProfile.h */, @@ -2258,6 +2222,7 @@ 3D4A621D1DDD3985001F41B4 /* jschelpers */ = { isa = PBXGroup; children = ( + 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */, 3D7A27DC1DE32541002E3F95 /* JavaScriptCore.h */, 3D92B1071E0369AD0018521A /* JSCHelpers.cpp */, 3D92B1081E0369AD0018521A /* JSCHelpers.h */, @@ -2321,7 +2286,6 @@ 13134C721E296B2A00B9F3CB /* CxxBridge */, 13134C7D1E296B2A00B9F3CB /* CxxModule */, 83CBBA491A601E3B00E9B192 /* Base */, - 134FCB381A6E7F0800051CC8 /* Executors */, 13B07FE01A69315300A75B9A /* Modules */, 1450FF7F1BCFF28A00208362 /* Profiler */, 13B07FF31A6947C200A75B9A /* Views */, @@ -2332,9 +2296,10 @@ 83CBBA491A601E3B00E9B192 /* Base */ = { isa = PBXGroup; children = ( + 135A9BF91E7B0EAE00587AEB /* RCTJSCErrorHandling.h */, + 135A9BFA1E7B0EAE00587AEB /* RCTJSCErrorHandling.mm */, 83CBBA4A1A601E3B00E9B192 /* RCTAssert.h */, 83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */, - 14C2CA771B3ACB0400E6CBB2 /* RCTBatchedBridge.m */, 83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */, 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */, 14A43DB81C1F849600794BC8 /* RCTBridge+Private.h */, @@ -2455,7 +2420,6 @@ 3D7454791E54757500E74ADD /* RecoverableError.h */, 3D92B0D31E03699D0018521A /* SampleCxxModule.cpp */, 3D92B0D41E03699D0018521A /* SampleCxxModule.h */, - 19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */, 3D92B0D51E03699D0018521A /* SystraceSection.h */, ); path = cxxreact; @@ -2524,6 +2488,7 @@ 3D302F3C1DF828F800D6DDAE /* RCTJavaScriptLoader.h in Headers */, 3D302F3D1DF828F800D6DDAE /* RCTJSStackFrame.h in Headers */, 3D302F3E1DF828F800D6DDAE /* RCTKeyCommands.h in Headers */, + 135A9C031E7B0F6100587AEB /* RCTJSCErrorHandling.h in Headers */, 3D302F3F1DF828F800D6DDAE /* RCTLog.h in Headers */, 3D302F401DF828F800D6DDAE /* RCTModuleData.h in Headers */, 3D302F411DF828F800D6DDAE /* RCTModuleMethod.h in Headers */, @@ -2541,8 +2506,6 @@ 3D302F4E1DF828F800D6DDAE /* RCTURLRequestHandler.h in Headers */, 3D302F4F1DF828F800D6DDAE /* RCTUtils.h in Headers */, 3D302F501DF828F800D6DDAE /* RCTWebSocketObserverProtocol.h in Headers */, - 3D302F521DF828F800D6DDAE /* RCTJSCErrorHandling.h in Headers */, - 3D302F531DF828F800D6DDAE /* RCTJSCExecutor.h in Headers */, 3D302F541DF828F800D6DDAE /* JSCSamplingProfiler.h in Headers */, 3D302F551DF828F800D6DDAE /* RCTAccessibilityManager.h in Headers */, 3D302F561DF828F800D6DDAE /* RCTAlertManager.h in Headers */, @@ -2562,7 +2525,6 @@ 3D302F641DF828F800D6DDAE /* RCTTiming.h in Headers */, 3D302F651DF828F800D6DDAE /* RCTUIManager.h in Headers */, 3D302F661DF828F800D6DDAE /* RCTFPSGraph.h in Headers */, - 3D302F671DF828F800D6DDAE /* RCTJSCProfiler.h in Headers */, 3D302F681DF828F800D6DDAE /* RCTMacros.h in Headers */, 3D302F691DF828F800D6DDAE /* RCTProfile.h in Headers */, 3D302F6A1DF828F800D6DDAE /* RCTActivityIndicatorView.h in Headers */, @@ -2576,6 +2538,7 @@ 3D302F721DF828F800D6DDAE /* RCTConvert+CoreLocation.h in Headers */, 3D302F761DF828F800D6DDAE /* RCTFont.h in Headers */, 3D302F7B1DF828F800D6DDAE /* RCTModalHostView.h in Headers */, + 1384E20A1E806D5700545659 /* RCTNativeModule.h in Headers */, 3D302F7C1DF828F800D6DDAE /* RCTModalHostViewController.h in Headers */, 3D302F7D1DF828F800D6DDAE /* RCTModalHostViewManager.h in Headers */, 3D302F7E1DF828F800D6DDAE /* RCTNavigator.h in Headers */, @@ -2583,6 +2546,7 @@ 130443DD1E401AF500D93A67 /* RCTConvert+Transform.h in Headers */, 3D302F801DF828F800D6DDAE /* RCTNavItem.h in Headers */, 3D302F811DF828F800D6DDAE /* RCTNavItemManager.h in Headers */, + 135A9C061E7B0F7800587AEB /* RCTJSCHelpers.h in Headers */, 3D302F841DF828F800D6DDAE /* RCTPointerEvents.h in Headers */, 3D302F851DF828F800D6DDAE /* RCTProgressViewManager.h in Headers */, 3D302F861DF828F800D6DDAE /* RCTRefreshControl.h in Headers */, @@ -2610,9 +2574,7 @@ 3D302F9A1DF828F800D6DDAE /* RCTViewManager.h in Headers */, 3D302F9D1DF828F800D6DDAE /* RCTWrapperViewController.h in Headers */, 3D302F9F1DF828F800D6DDAE /* UIView+React.h in Headers */, - 13134C911E296B2A00B9F3CB /* RCTNativeModule.h in Headers */, 13134CA11E296B2A00B9F3CB /* RCTCxxUtils.h in Headers */, - 13134C851E296B2A00B9F3CB /* RCTCxxBridge.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2743,7 +2705,6 @@ 3D80DA191DF820620028D040 /* RCTImageLoader.h in Headers */, 13134C941E296B2A00B9F3CB /* RCTObjcExecutor.h in Headers */, 3D80DA1A1DF820620028D040 /* RCTImageStoreManager.h in Headers */, - 13134C841E296B2A00B9F3CB /* RCTCxxBridge.h in Headers */, 130443A11E3FEAA900D93A67 /* RCTFollyConvert.h in Headers */, 59FBEFB41E46D91C0095D885 /* RCTScrollContentViewManager.h in Headers */, 3D80DA1B1DF820620028D040 /* RCTResizeMode.h in Headers */, @@ -2767,6 +2728,7 @@ 139D7E541E25C5A300323FB7 /* double-conversion.h in Headers */, 3D80DA2A1DF820620028D040 /* RCTErrorCustomizer.h in Headers */, 3D80DA2B1DF820620028D040 /* RCTErrorInfo.h in Headers */, + 1384E2081E806D4E00545659 /* RCTNativeModule.h in Headers */, 3D80DA2C1DF820620028D040 /* RCTEventDispatcher.h in Headers */, 3D80DA2D1DF820620028D040 /* RCTFrameUpdate.h in Headers */, 3D80DA2E1DF820620028D040 /* RCTImageSource.h in Headers */, @@ -2774,6 +2736,7 @@ 3D80DA2F1DF820620028D040 /* RCTInvalidating.h in Headers */, 139D7E5C1E25C5A300323FB7 /* utils.h in Headers */, 3D80DA301DF820620028D040 /* RCTJavaScriptExecutor.h in Headers */, + 135A9BFF1E7B0EE600587AEB /* RCTJSCHelpers.h in Headers */, 3D80DA311DF820620028D040 /* RCTJavaScriptLoader.h in Headers */, 130E3D881E6A082100ACE484 /* RCTDevSettings.h in Headers */, 3D80DA321DF820620028D040 /* RCTJSStackFrame.h in Headers */, @@ -2791,6 +2754,7 @@ 3D80DA3B1DF820620028D040 /* RCTPerformanceLogger.h in Headers */, 3D80DA3C1DF820620028D040 /* RCTPlatform.h in Headers */, 3D80DA3D1DF820620028D040 /* RCTRootView.h in Headers */, + 135A9BFB1E7B0EAE00587AEB /* RCTJSCErrorHandling.h in Headers */, 3D80DA3E1DF820620028D040 /* RCTRootViewDelegate.h in Headers */, 3D80DA3F1DF820620028D040 /* RCTRootViewInternal.h in Headers */, 3D80DA401DF820620028D040 /* RCTTouchEvent.h in Headers */, @@ -2798,13 +2762,10 @@ 13F880391E296D2800C3C7A1 /* noncopyable.h in Headers */, 13134C8C1E296B2A00B9F3CB /* RCTMessageThread.h in Headers */, 3D80DA421DF820620028D040 /* RCTURLRequestDelegate.h in Headers */, - 13134C901E296B2A00B9F3CB /* RCTNativeModule.h in Headers */, 3D80DA431DF820620028D040 /* RCTURLRequestHandler.h in Headers */, 3D80DA441DF820620028D040 /* RCTUtils.h in Headers */, 3D80DA451DF820620028D040 /* RCTWebSocketObserverProtocol.h in Headers */, 139D7EEA1E25DBDC00323FB7 /* symbolize.h in Headers */, - 3D80DA471DF820620028D040 /* RCTJSCErrorHandling.h in Headers */, - 3D80DA481DF820620028D040 /* RCTJSCExecutor.h in Headers */, 13134C981E296B2A00B9F3CB /* RCTCxxMethod.h in Headers */, 3D80DA491DF820620028D040 /* JSCSamplingProfiler.h in Headers */, 3D80DA4A1DF820620028D040 /* RCTAccessibilityManager.h in Headers */, @@ -2827,7 +2788,6 @@ 3D80DA591DF820620028D040 /* RCTTiming.h in Headers */, 3D80DA5A1DF820620028D040 /* RCTUIManager.h in Headers */, 3D80DA5B1DF820620028D040 /* RCTFPSGraph.h in Headers */, - 3D80DA5C1DF820620028D040 /* RCTJSCProfiler.h in Headers */, 3D80DA5D1DF820620028D040 /* RCTMacros.h in Headers */, 3D80DA5E1DF820620028D040 /* RCTProfile.h in Headers */, 3D80DA5F1DF820620028D040 /* RCTActivityIndicatorView.h in Headers */, @@ -3267,7 +3227,6 @@ 2DD0EFE11DA84F2800B0C975 /* RCTStatusBarManager.m in Sources */, 2D3B5EC91D9B095C00451313 /* RCTBorderDrawing.m in Sources */, 2D3B5E991D9B089A00451313 /* RCTDisplayLink.m in Sources */, - 2D3B5EBF1D9B093300451313 /* RCTJSCProfiler.m in Sources */, 2D3B5EA11D9B08B600451313 /* RCTModuleData.mm in Sources */, 2D3B5EEA1D9B09CD00451313 /* RCTTabBar.m in Sources */, 2D3B5EAE1D9B08F800451313 /* RCTEventEmitter.m in Sources */, @@ -3304,16 +3263,14 @@ 2D3B5E951D9B087C00451313 /* RCTAssert.m in Sources */, 2D3B5EB61D9B091400451313 /* RCTExceptionsManager.m in Sources */, 2D3B5EEB1D9B09D000451313 /* RCTTabBarItem.m in Sources */, - 2D3B5E961D9B088500451313 /* RCTBatchedBridge.m in Sources */, 2D3B5ED41D9B097D00451313 /* RCTModalHostView.m in Sources */, 2D3B5E9F1D9B08AF00451313 /* RCTKeyCommands.m in Sources */, 2D3B5EA51D9B08C700451313 /* RCTRootView.m in Sources */, - 2D3B5EAC1D9B08EF00451313 /* RCTJSCExecutor.mm in Sources */, 13134C871E296B2A00B9F3CB /* RCTCxxBridge.mm in Sources */, CF2731C31E7B8DF30044CA4F /* RCTDeviceInfo.m in Sources */, 2D3B5EB11D9B090100451313 /* RCTAppState.m in Sources */, + 1384E20B1E806D5B00545659 /* RCTNativeModule.mm in Sources */, 2D3B5EC21D9B093B00451313 /* RCTProfile.m in Sources */, - 13134C931E296B2A00B9F3CB /* RCTNativeModule.mm in Sources */, 2D3B5ECB1D9B096200451313 /* RCTConvert+CoreLocation.m in Sources */, 2D3B5EEE1D9B09DA00451313 /* RCTView.m in Sources */, 2D3B5E981D9B089500451313 /* RCTConvert.m in Sources */, @@ -3327,12 +3284,12 @@ 2D3B5EB81D9B091B00451313 /* RCTSourceCode.m in Sources */, 2D3B5EB51D9B091100451313 /* RCTDevMenu.m in Sources */, 2D3B5EBD1D9B092A00451313 /* RCTTiming.m in Sources */, + 135A9C041E7B0F6400587AEB /* RCTJSCErrorHandling.mm in Sources */, 2D3B5EA81D9B08D300451313 /* RCTUtils.m in Sources */, 2D3B5EC81D9B095800451313 /* RCTActivityIndicatorViewManager.m in Sources */, 3DCD185D1DF978E7007FE5A1 /* RCTReloadCommand.m in Sources */, 130443DB1E401ADD00D93A67 /* RCTConvert+Transform.m in Sources */, 2D3B5EC61D9B095000451313 /* RCTProfileTrampoline-x86_64.S in Sources */, - 3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */, 2D3B5EA61D9B08CA00451313 /* RCTTouchEvent.m in Sources */, 2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */, 2D3B5EF01D9B09E300451313 /* RCTWrapperViewController.m in Sources */, @@ -3356,6 +3313,7 @@ 2D3B5E9A1D9B089D00451313 /* RCTEventDispatcher.m in Sources */, 2D3B5ED61D9B098400451313 /* RCTModalHostViewManager.m in Sources */, 2D3B5EE51D9B09BE00451313 /* RCTShadowView.m in Sources */, + 135A9C051E7B0F7500587AEB /* RCTJSCHelpers.mm in Sources */, 2D3B5EC71D9B095600451313 /* RCTActivityIndicatorView.m in Sources */, 2D3B5EB21D9B090300451313 /* RCTAsyncLocalStorage.m in Sources */, 2D3B5EC01D9B093600451313 /* RCTPerfMonitor.m in Sources */, @@ -3387,6 +3345,7 @@ 13EBC6711E2870DE00880AC5 /* JSCWrapper.cpp in Sources */, 13EBC6731E2870DE00880AC5 /* Value.cpp in Sources */, 13EBC67D1E28725900880AC5 /* JSCHelpers.cpp in Sources */, + 135A9C021E7B0F4800587AEB /* systemJSCWrapper.cpp in Sources */, 13EBC67B1E28723000880AC5 /* Unicode.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3398,6 +3357,7 @@ 13EBC6791E2870E400880AC5 /* Unicode.cpp in Sources */, 13EBC6781E2870E400880AC5 /* JSCWrapper.cpp in Sources */, 13EBC6771E2870E400880AC5 /* JSCHelpers.cpp in Sources */, + 135A9C011E7B0F4700587AEB /* systemJSCWrapper.cpp in Sources */, 13EBC67A1E2870E400880AC5 /* Value.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3460,7 +3420,6 @@ 59FBEFB61E46D91C0095D885 /* RCTScrollContentViewManager.m in Sources */, 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */, 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */, - 14A43DF31C20B1C900794BC8 /* RCTJSCProfiler.m in Sources */, 001BFCD01D8381DE008E587E /* RCTMultipartStreamReader.m in Sources */, 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */, 14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */, @@ -3510,13 +3469,10 @@ 13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */, A2440AA31DF8D854006E7BFC /* RCTReloadCommand.m in Sources */, E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */, - 3D7A27E21DE325B7002E3F95 /* RCTJSCErrorHandling.mm in Sources */, 13A0C2891B74F71200B29F6F /* RCTDevLoadingView.m in Sources */, 13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */, 1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */, 59A7B9FE1E577DBF0068EDBF /* RCTRootContentView.m in Sources */, - 134FCB3D1A6E7F0800051CC8 /* RCTJSCExecutor.mm in Sources */, - 14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */, 13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */, 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */, CF2731C11E7B8DE40044CA4F /* RCTDeviceInfo.m in Sources */, @@ -3532,6 +3488,7 @@ 13E067571A70F44B002CDEE1 /* RCTView.m in Sources */, 3D7749441DC1065C007EC8D8 /* RCTPlatform.m in Sources */, 13D9FEEE1CDCD93000158BD7 /* RCTKeyboardObserver.m in Sources */, + 135A9C001E7B0EE600587AEB /* RCTJSCHelpers.mm in Sources */, B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */, 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */, 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */, @@ -3543,7 +3500,6 @@ 83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */, 13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */, 139D7EE71E25DBDC00323FB7 /* signalhandler.cc in Sources */, - 13134C921E296B2A00B9F3CB /* RCTNativeModule.mm in Sources */, 58114A161AAE854800E7D092 /* RCTPicker.m in Sources */, 137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */, 83A1FE8C1B62640A00BE0E65 /* RCTModalHostView.m in Sources */, @@ -3558,12 +3514,13 @@ 13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */, 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */, 13F17A851B8493E5007D4C75 /* RCTRedBox.m in Sources */, + 135A9BFC1E7B0EAE00587AEB /* RCTJSCErrorHandling.mm in Sources */, 83392EB31B6634E10013B15F /* RCTModalHostViewController.m in Sources */, 13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */, - 19DED2291E77E29200F089BB /* systemJSCWrapper.cpp in Sources */, 83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */, 83A1FE8F1B62643A00BE0E65 /* RCTModalHostViewManager.m in Sources */, 13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */, + 1384E2091E806D4E00545659 /* RCTNativeModule.mm in Sources */, 391E86A41C623EC800009732 /* RCTTouchEvent.m in Sources */, 1450FF861BCFF28A00208362 /* RCTProfile.m in Sources */, 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */, From 462352e609102d137df1ce45220cd5b54d9b3bd4 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 21 Mar 2017 22:18:57 -0700 Subject: [PATCH 130/366] add jest snapshot tests Reviewed By: yungsters Differential Revision: D4726519 fbshipit-source-id: 1ae98743cdb89acb2708d84073527015dbeee906 --- .../Lists/__tests__/FlatList-test.js | 64 ++++ .../Lists/__tests__/SectionList-test.js | 67 +++++ .../__snapshots__/FlatList-test.js.snap | 247 +++++++++++++++ .../__snapshots__/SectionList-test.js.snap | 281 ++++++++++++++++++ 4 files changed, 659 insertions(+) create mode 100644 Libraries/CustomComponents/Lists/__tests__/FlatList-test.js create mode 100644 Libraries/CustomComponents/Lists/__tests__/SectionList-test.js create mode 100644 Libraries/CustomComponents/Lists/__tests__/__snapshots__/FlatList-test.js.snap create mode 100644 Libraries/CustomComponents/Lists/__tests__/__snapshots__/SectionList-test.js.snap diff --git a/Libraries/CustomComponents/Lists/__tests__/FlatList-test.js b/Libraries/CustomComponents/Lists/__tests__/FlatList-test.js new file mode 100644 index 00000000000000..945e5f64e64361 --- /dev/null +++ b/Libraries/CustomComponents/Lists/__tests__/FlatList-test.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2013-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. + * + */ +'use strict'; + +jest.disableAutomock(); + +const React = require('React'); +const ReactTestRenderer = require('react-test-renderer'); + +const FlatList = require('FlatList'); + +describe('FlatList', () => { + it('renders simple list', () => { + const component = ReactTestRenderer.create( + } + /> + ); + expect(component).toMatchSnapshot(); + }); + it('renders empty list', () => { + const component = ReactTestRenderer.create( + } + /> + ); + expect(component).toMatchSnapshot(); + }); + it('renders null list', () => { + const component = ReactTestRenderer.create( + } + /> + ); + expect(component).toMatchSnapshot(); + }); + it('renders all the bells and whistles', () => { + const component = ReactTestRenderer.create( + } + ListFooterComponent={() =>