From c8d922b4670127c417d744a0a1412cc6ad961fef Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 16 Mar 2017 07:40:54 -0700 Subject: [PATCH 001/763] 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 002/763] 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 003/763] 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 004/763] 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 005/763] 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 006/763] 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 007/763] 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 008/763] 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 009/763] 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 010/763] 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 011/763] 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 012/763] 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 013/763] 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 014/763] 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 015/763] 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 016/763] 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 017/763] 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 018/763] 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 019/763] 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 020/763] 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 021/763] 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 022/763] 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 023/763] 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 024/763] 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 025/763] 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 026/763] 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 027/763] 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 028/763] 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 029/763] 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 030/763] 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 031/763] 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 032/763] 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 033/763] 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 034/763] 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 035/763] 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 036/763] 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 037/763] 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 038/763] 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 039/763] 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 040/763] 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 041/763] 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 042/763] 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 043/763] 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 044/763] 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 045/763] 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 046/763] 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 047/763] 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 048/763] 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 049/763] 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 050/763] 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 051/763] 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 052/763] 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 053/763] 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 054/763] 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 055/763] 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 056/763] 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 057/763] 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 058/763] 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 059/763] 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 060/763] 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 061/763] 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 062/763] 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 063/763] 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 064/763] 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 065/763] 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 066/763] 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 067/763] 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 068/763] 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 069/763] 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 070/763] 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 071/763] 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 072/763] 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 073/763] 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 074/763] 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 075/763] 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 076/763] 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 077/763] 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 078/763] 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 079/763] 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={() =>