From 3a24c63f73b84a1f6d5331cc76ce4dad0d363616 Mon Sep 17 00:00:00 2001 From: Vesa Laakso Date: Wed, 6 Jul 2016 11:49:49 -0700 Subject: [PATCH 001/241] Documentation: Fix LayoutWithFlexbox.md broken example Summary: This fixes the [Align Items example](http://facebook.github.io/react-native/releases/next/docs/flexbox.html#align-items) in Layout with Flexbox -documentation page by fixing a broken example. screen shot 2016-07-06 at 20 59 08 EDIT: Seems like 34adde9e96d8949fe3a20144616aaa5e073d5e94 tried to cover all of these but missed one case :) Closes https://github.com/facebook/react-native/pull/8608 Differential Revision: D3523445 Pulled By: JoelMarcey fbshipit-source-id: ffc8be2b2e85d3b821a37e387e9385e57820a200 --- docs/LayoutWithFlexbox.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/LayoutWithFlexbox.md b/docs/LayoutWithFlexbox.md index 39d7730845b2e5..762084ac3da30e 100644 --- a/docs/LayoutWithFlexbox.md +++ b/docs/LayoutWithFlexbox.md @@ -76,7 +76,7 @@ Adding `alignItems` to a component's style determines the **alignment** of child import React, { Component } from 'react'; import { AppRegistry, View } from 'react-native'; -class AlignItemsBasics { +class AlignItemsBasics extends Component { render() { return ( // Try setting `alignItems` to 'flex-start' From ba3c7ef08c8b0ff2457b0efd8f1a2998a3eb44f7 Mon Sep 17 00:00:00 2001 From: Steven Scaffidi Date: Wed, 6 Jul 2016 11:53:05 -0700 Subject: [PATCH 002/241] Fix the getRowData method of SwipeableListViewDataSource in SwipeableListView Summary: This is simple fix to SwipeableListView. According to SwipeableListViewDataSource, the only method available is cloneWithRowsAndSections. If you try to get the data from a SwipeableListView, without this fix it won't work. This change fixes the problem so that SwipeableListView can work correctly. Closes https://github.com/facebook/react-native/pull/8493 Differential Revision: D3523384 fbshipit-source-id: 359ff274fabcab676ed6dee24f0652c4146cde69 --- Libraries/Experimental/SwipeableRow/SwipeableListView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Experimental/SwipeableRow/SwipeableListView.js b/Libraries/Experimental/SwipeableRow/SwipeableListView.js index f18d240b12fcc0..206f4e54d24ce2 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableListView.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableListView.js @@ -52,7 +52,7 @@ const SwipeableListView = React.createClass({ statics: { getNewDataSource(): Object { return new SwipeableListViewDataSource({ - getRowData: (data, sectionID, rowID) => data[rowID], + getRowData: (data, sectionID, rowID) => data[sectionID][rowID], getSectionHeaderData: (data, sectionID) => data[sectionID], sectionHeaderHasChanged: (s1, s2) => s1 !== s2, rowHasChanged: (row1, row2) => row1 !== row2, From 7795918eb4396efeb1e6d6e589e1aa5b3ca8e1fc Mon Sep 17 00:00:00 2001 From: Jeff Morrison Date: Wed, 6 Jul 2016 12:49:08 -0700 Subject: [PATCH 003/241] Unrevert D3518381 Reviewed By: gabelevi Differential Revision: D3522895 fbshipit-source-id: 52f28c7f3142566a07d8bc845be882aeda098809 --- .flowconfig | 20 ++++++++++++++++--- ...gationTransitioner-AnimatedView-example.js | 2 +- Examples/UIExplorer/WebSocketExample.js | 9 ++++++++- Libraries/BugReporting/dumpReactTree.js | 1 + Libraries/CameraRoll/CameraRoll.js | 1 + .../NavigationCardStackStyleInterpolator.js | 12 +++++------ .../NavigationHeaderStyleInterpolator.js | 8 ++++---- .../NavigationPagerStyleInterpolator.js | 2 +- Libraries/Experimental/WindowedListView.js | 2 ++ Libraries/StyleSheet/StyleSheetTypes.js | 2 +- package.json | 2 +- 11 files changed, 43 insertions(+), 18 deletions(-) diff --git a/.flowconfig b/.flowconfig index 0bbb11213636bd..fd80d1db240c70 100644 --- a/.flowconfig +++ b/.flowconfig @@ -9,6 +9,20 @@ # Ignore malformed json .*/node_modules/y18n/test/.*\.json +# Ignore the website subdir +/website/.* + +# Ignore BUCK generated dirs +/\.buckd/ + +# Ignore unexpected extra @providesModule +.*/node_modules/commoner/test/source/widget/share.js + +# Ignore duplicate module providers +/Libraries/react-native/React.js +/Libraries/react-native/ReactNative.js +/node_modules/jest-runtime/build/__tests__/.* + [include] [libs] @@ -32,9 +46,9 @@ suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-7]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-7]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-8]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-8]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy [version] -^0.27.0 +^0.28.0 diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js b/Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js index 31cafcb91bbcad..db1b088f5e43cd 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js @@ -222,7 +222,7 @@ class ExampleScene extends Component { const width = layout.initWidth; const translateX = position.interpolate({ inputRange, - outputRange: [width, 0, -10], + outputRange: ([width, 0, -10]: Array), }); return { diff --git a/Examples/UIExplorer/WebSocketExample.js b/Examples/UIExplorer/WebSocketExample.js index c43079162a31ee..d3400fdbcd1150 100644 --- a/Examples/UIExplorer/WebSocketExample.js +++ b/Examples/UIExplorer/WebSocketExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -81,7 +88,7 @@ function showValue(value) { if (typeof ArrayBuffer !== 'undefined' && typeof Uint8Array !== 'undefined' && value instanceof ArrayBuffer) { - return `ArrayBuffer {${Array.from(new Uint8Array(value))}}`; + return `ArrayBuffer {${String(Array.from(new Uint8Array(value)))}}`; } return value; } diff --git a/Libraries/BugReporting/dumpReactTree.js b/Libraries/BugReporting/dumpReactTree.js index 89a7d874c53939..06c734e3a62042 100644 --- a/Libraries/BugReporting/dumpReactTree.js +++ b/Libraries/BugReporting/dumpReactTree.js @@ -101,6 +101,7 @@ function convertObject(object: Object, depth: number) { if (!first) { output += ', '; } + // $FlowFixMe(>=0.28.0) output += `${key}: ${convertValue(object[key], depth + 1)}`; first = false; } diff --git a/Libraries/CameraRoll/CameraRoll.js b/Libraries/CameraRoll/CameraRoll.js index 7ef5962759aed0..3d4a8b376979ca 100644 --- a/Libraries/CameraRoll/CameraRoll.js +++ b/Libraries/CameraRoll/CameraRoll.js @@ -143,6 +143,7 @@ class CameraRoll { invariant( type === 'photo' || type === 'video' || type === undefined, + // $FlowFixMe(>=0.28.0) `The second argument to saveToCameraRoll must be 'photo' or 'video'. You passed ${type}` ); diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js index fb5a7f4dd01dec..77fa010e5d654e 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js @@ -90,18 +90,18 @@ function forHorizontal(props: NavigationSceneRendererProps): Object { const opacity = position.interpolate({ inputRange, - outputRange: [1, 1, 0.3], + outputRange: ([1, 1, 0.3]: Array), }); const scale = position.interpolate({ inputRange, - outputRange: [1, 1, 0.95], + outputRange: ([1, 1, 0.95]: Array), }); const translateY = 0; const translateX = position.interpolate({ inputRange, - outputRange: [width, 0, -10], + outputRange: ([width, 0, -10]: Array), }); return { @@ -131,18 +131,18 @@ function forVertical(props: NavigationSceneRendererProps): Object { const opacity = position.interpolate({ inputRange, - outputRange: [1, 1, 0.3], + outputRange: ([1, 1, 0.3]: Array), }); const scale = position.interpolate({ inputRange, - outputRange: [1, 1, 0.95], + outputRange: ([1, 1, 0.95]: Array), }); const translateX = 0; const translateY = position.interpolate({ inputRange, - outputRange: [height, 0, -10], + outputRange: ([height, 0, -10]: Array), }); return { diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js index f6a0dbf51662de..fcc37c7a5ab568 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js @@ -54,7 +54,7 @@ function forLeft(props: NavigationSceneRendererProps): Object { return { opacity: position.interpolate({ inputRange: [ index - 1, index, index + 1 ], - outputRange: [ 0, 1, 0 ], + outputRange: ([ 0, 1, 0 ]: Array), }), }; } @@ -65,13 +65,13 @@ function forCenter(props: NavigationSceneRendererProps): Object { return { opacity:position.interpolate({ inputRange: [ index - 1, index, index + 1 ], - outputRange: [ 0, 1, 0 ], + outputRange: ([ 0, 1, 0 ]: Array), }), transform: [ { translateX: position.interpolate({ inputRange: [ index - 1, index + 1 ], - outputRange: [ 200, -200 ], + outputRange: ([ 200, -200 ]: Array), }), } ], @@ -84,7 +84,7 @@ function forRight(props: NavigationSceneRendererProps): Object { return { opacity: position.interpolate({ inputRange: [ index - 1, index, index + 1 ], - outputRange: [ 0, 1, 0 ], + outputRange: ([ 0, 1, 0 ]: Array), }), }; } diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js index 78a9987cab5f86..87a211b1a723d5 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js @@ -91,7 +91,7 @@ function forHorizontal(props: NavigationSceneRendererProps): Object { const width = layout.initWidth; const translateX = position.interpolate({ inputRange, - outputRange: [width, 0, -width], + outputRange: ([width, 0, -width]: Array), }); return { diff --git a/Libraries/Experimental/WindowedListView.js b/Libraries/Experimental/WindowedListView.js index bec56ecbe4983b..2cef3471ce04c1 100644 --- a/Libraries/Experimental/WindowedListView.js +++ b/Libraries/Experimental/WindowedListView.js @@ -626,6 +626,7 @@ class CellRenderer extends React.Component { if (this.props.asyncRowPerfEventName) { this._perfUpdateID = g_perf_update_id++; this._asyncCookie = Systrace.beginAsyncEvent(this.props.asyncRowPerfEventName + this._perfUpdateID); + // $FlowFixMe(>=0.28.0) infoLog(`perf_asynctest_${this.props.asyncRowPerfEventName}_start ${this._perfUpdateID} ${Date.now()}`); } if (this.props.includeInLayout) { @@ -662,6 +663,7 @@ class CellRenderer extends React.Component { // Note this doesn't include the native render time but is more accurate than also including the JS render // time of anything that has been queued up. Systrace.endAsyncEvent(this.props.asyncRowPerfEventName + this._perfUpdateID, this._asyncCookie); + // $FlowFixMe(>=0.28.0) infoLog(`perf_asynctest_${this.props.asyncRowPerfEventName}_end ${this._perfUpdateID} ${Date.now()}`); } } diff --git a/Libraries/StyleSheet/StyleSheetTypes.js b/Libraries/StyleSheet/StyleSheetTypes.js index 779e64772307ba..515d3e6145ea77 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.js +++ b/Libraries/StyleSheet/StyleSheetTypes.js @@ -12,4 +12,4 @@ 'use strict'; type Atom = number | bool | Object | Array; -export type StyleObj = Atom | Array; +export type StyleObj = Atom; diff --git a/package.json b/package.json index 2961e17c47c98d..59f40476e31fe2 100644 --- a/package.json +++ b/package.json @@ -196,7 +196,7 @@ "eslint-plugin-babel": "^3.2.0", "eslint-plugin-flow-vars": "^0.2.1", "eslint-plugin-react": "^4.2.1", - "flow-bin": "^0.27.0", + "flow-bin": "^0.28.0", "jest": "^13.1.0", "jest-repl": "^13.1.0", "jest-runtime": "^13.1.0", From 7f790dc0de1ecc9f232e304abcc5e38b9fae98f0 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Wed, 6 Jul 2016 12:57:38 -0700 Subject: [PATCH 004/241] Pull an updated version of fbjni into RN OSS Differential Revision: D3521227 fbshipit-source-id: 57db97ea2af2b2c9e55f380ce05d9e78a5f9d48c --- ReactAndroid/build.gradle | 2 +- .../src/main/java/com/facebook/jni/BUCK | 4 +- .../main/java/com/facebook/jni/Countable.java | 19 +- .../java/com/facebook/jni/CppException.java | 20 - .../java/com/facebook/jni/HybridData.java | 23 +- .../java/com/facebook/jni/IteratorHelper.java | 56 +++ .../com/facebook/jni/MapIteratorHelper.java | 53 +++ ...rrorException.java => NativeRunnable.java} | 21 +- .../java/com/facebook/jni/Prerequisites.java | 65 --- .../com/facebook/jni/ThreadScopeSupport.java | 22 + .../com/facebook/jni/UnknownCppException.java | 25 -- .../src/main/java/com/facebook/jni/fbjni.pro | 11 + .../src/main/jni/first-party/fb/Android.mk | 2 +- .../first-party/fb/include/fb/Environment.h | 57 +-- .../first-party/fb/include/fb/fbjni/Common.h | 20 + .../first-party/fb/include/fb/fbjni/Context.h | 4 + .../fb/include/fb/fbjni/CoreClasses-inl.h | 7 + .../fb/include/fb/fbjni/CoreClasses.h | 4 +- .../fb/include/fb/fbjni/Exceptions.h | 54 +-- .../first-party/fb/include/fb/fbjni/JThread.h | 39 ++ .../fb/include/fb/fbjni/Meta-inl.h | 28 +- .../fb/include/fb/fbjni/NativeRunnable.h | 47 +++ .../fb/fbjni/ReferenceAllocators-inl.h | 2 - .../first-party/fb/include/jni/LocalString.h | 4 +- .../jni/first-party/fb/jni/Environment.cpp | 47 ++- .../jni/first-party/fb/jni/Exceptions.cpp | 384 ++++++------------ .../jni/first-party/fb/jni/LocalString.cpp | 4 +- .../main/jni/first-party/fb/jni/OnLoad.cpp | 3 + .../src/main/jni/first-party/fb/jni/fbjni.cpp | 7 +- .../src/main/jni/first-party/fb/lyra/lyra.cpp | 20 + .../src/main/jni/first-party/fb/onload.cpp | 6 + 31 files changed, 578 insertions(+), 482 deletions(-) delete mode 100644 ReactAndroid/src/main/java/com/facebook/jni/CppException.java create mode 100644 ReactAndroid/src/main/java/com/facebook/jni/IteratorHelper.java create mode 100644 ReactAndroid/src/main/java/com/facebook/jni/MapIteratorHelper.java rename ReactAndroid/src/main/java/com/facebook/jni/{CppSystemErrorException.java => NativeRunnable.java} (57%) delete mode 100644 ReactAndroid/src/main/java/com/facebook/jni/Prerequisites.java create mode 100644 ReactAndroid/src/main/java/com/facebook/jni/ThreadScopeSupport.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java create mode 100644 ReactAndroid/src/main/java/com/facebook/jni/fbjni.pro create mode 100644 ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/JThread.h create mode 100644 ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/NativeRunnable.h diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 50d7219cab64f6..407316d02102bf 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -240,7 +240,7 @@ android { jniLibs.srcDir "$buildDir/react-ndk/exported" res.srcDirs = ['src/main/res/devsupport', 'src/main/res/shell', 'src/main/res/views/modal'] java { - srcDirs = ['src/main/java', 'src/main/libraries/soloader/java'] + srcDirs = ['src/main/java', 'src/main/libraries/soloader/java', 'src/main/jni/first-party/fb/jni/java'] exclude 'com/facebook/react/processing' } } diff --git a/ReactAndroid/src/main/java/com/facebook/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/jni/BUCK index 220371dbaae689..a5eb40afc3db91 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/jni/BUCK @@ -3,13 +3,15 @@ include_defs('//ReactAndroid/DEFS') android_library( name = 'jni', srcs = glob(['**/*.java']), + proguard_config = 'fbjni.pro', deps = [ react_native_dep('java/com/facebook/proguard/annotations:annotations'), react_native_dep('libraries/soloader/java/com/facebook/soloader:soloader'), + react_native_dep('third-party/java/jsr-305:jsr-305'), ], visibility = [ 'PUBLIC', - ], + ] ) project_config( diff --git a/ReactAndroid/src/main/java/com/facebook/jni/Countable.java b/ReactAndroid/src/main/java/com/facebook/jni/Countable.java index 75892f48ca638c..a319e187aac35f 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/Countable.java +++ b/ReactAndroid/src/main/java/com/facebook/jni/Countable.java @@ -1,15 +1,9 @@ -/** - * 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. - */ +// Copyright 2004-present Facebook. All Rights Reserved. package com.facebook.jni; import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.soloader.SoLoader; /** * A Java Object that has native memory allocated corresponding to this instance. @@ -23,14 +17,15 @@ */ @DoNotStrip public class Countable { + + static { + SoLoader.loadLibrary("fb"); + } + // Private C++ instance @DoNotStrip private long mInstance = 0; - public Countable() { - Prerequisites.ensure(); - } - public native void dispose(); protected void finalize() throws Throwable { diff --git a/ReactAndroid/src/main/java/com/facebook/jni/CppException.java b/ReactAndroid/src/main/java/com/facebook/jni/CppException.java deleted file mode 100644 index a0c845dd62878a..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/jni/CppException.java +++ /dev/null @@ -1,20 +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. - */ - -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -@DoNotStrip -public class CppException extends RuntimeException { - @DoNotStrip - public CppException(String message) { - super(message); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java b/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java index fcb4ca33623fdd..e0b864b08d1d38 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java +++ b/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java @@ -1,15 +1,9 @@ -/** - * 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. - */ +// Copyright 2004-present Facebook. All Rights Reserved. package com.facebook.jni; import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.soloader.SoLoader; /** * This object holds a native C++ member for hybrid Java/C++ objects. @@ -24,14 +18,15 @@ */ @DoNotStrip public class HybridData { + + static { + SoLoader.loadLibrary("fb"); + } + // Private C++ instance @DoNotStrip private long mNativePointer = 0; - public HybridData() { - Prerequisites.ensure(); - } - /** * To explicitly delete the instance, call resetNative(). If the C++ * instance is referenced after this is called, a NullPointerException will @@ -47,4 +42,8 @@ protected void finalize() throws Throwable { resetNative(); super.finalize(); } + + public boolean isValid() { + return mNativePointer != 0; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/jni/IteratorHelper.java b/ReactAndroid/src/main/java/com/facebook/jni/IteratorHelper.java new file mode 100644 index 00000000000000..aca1cb50fb04e9 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/jni/IteratorHelper.java @@ -0,0 +1,56 @@ +/** + * 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.jni; + +import com.facebook.proguard.annotations.DoNotStrip; + +import javax.annotation.Nullable; + +import java.util.Iterator; + +/** + * To iterate over an Iterator from C++ requires two calls per entry: hasNext() + * and next(). This helper reduces it to one call and one field get per entry. + * It does not use a generic argument, since in C++, the types will be erased, + * anyway. This is *not* a {@link java.util.Iterator}. + */ +@DoNotStrip +public class IteratorHelper { + private final Iterator mIterator; + + // This is private, but accessed via JNI. + @DoNotStrip + private @Nullable Object mElement; + + @DoNotStrip + public IteratorHelper(Iterator iterator) { + mIterator = iterator; + } + + @DoNotStrip + public IteratorHelper(Iterable iterable) { + mIterator = iterable.iterator(); + } + + /** + * Moves the helper to the next entry in the map, if any. Returns true iff + * there is an entry to read. + */ + @DoNotStrip + boolean hasNext() { + if (mIterator.hasNext()) { + mElement = mIterator.next(); + return true; + } else { + mElement = null; + return false; + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/jni/MapIteratorHelper.java b/ReactAndroid/src/main/java/com/facebook/jni/MapIteratorHelper.java new file mode 100644 index 00000000000000..aa9283ed2f898a --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/jni/MapIteratorHelper.java @@ -0,0 +1,53 @@ +/** + * 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.jni; + +import javax.annotation.Nullable; + +import java.util.Iterator; +import java.util.Map; + +import com.facebook.proguard.annotations.DoNotStrip; + +/** + * To iterate over a Map from C++ requires four calls per entry: hasNext(), + * next(), getKey(), getValue(). This helper reduces it to one call and two + * field gets per entry. It does not use a generic argument, since in C++, the + * types will be erased, anyway. This is *not* a {@link java.util.Iterator}. + */ +@DoNotStrip +public class MapIteratorHelper { + @DoNotStrip private final Iterator mIterator; + @DoNotStrip private @Nullable Object mKey; + @DoNotStrip private @Nullable Object mValue; + + @DoNotStrip + public MapIteratorHelper(Map map) { + mIterator = map.entrySet().iterator(); + } + + /** + * Moves the helper to the next entry in the map, if any. Returns true iff + * there is an entry to read. + */ + @DoNotStrip + boolean hasNext() { + if (mIterator.hasNext()) { + Map.Entry entry = mIterator.next(); + mKey = entry.getKey(); + mValue = entry.getValue(); + return true; + } else { + mKey = null; + mValue = null; + return false; + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java b/ReactAndroid/src/main/java/com/facebook/jni/NativeRunnable.java similarity index 57% rename from ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java rename to ReactAndroid/src/main/java/com/facebook/jni/NativeRunnable.java index 13090a18ce30a3..151cc8ad80f0a3 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java +++ b/ReactAndroid/src/main/java/com/facebook/jni/NativeRunnable.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * @@ -9,19 +9,20 @@ package com.facebook.jni; +import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; +/** + * A Runnable that has a native run implementation. + */ @DoNotStrip -public class CppSystemErrorException extends CppException { - int errorCode; +public class NativeRunnable implements Runnable { - @DoNotStrip - public CppSystemErrorException(String message, int errorCode) { - super(message); - this.errorCode = errorCode; - } + private final HybridData mHybridData; - public int getErrorCode() { - return errorCode; + private NativeRunnable(HybridData hybridData) { + mHybridData = hybridData; } + + public native void run(); } diff --git a/ReactAndroid/src/main/java/com/facebook/jni/Prerequisites.java b/ReactAndroid/src/main/java/com/facebook/jni/Prerequisites.java deleted file mode 100644 index 5f279d0b2a6c7d..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/jni/Prerequisites.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.facebook.jni; - - -import com.facebook.soloader.SoLoader; - - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; - -public class Prerequisites { - private static final int EGL_OPENGL_ES2_BIT = 0x0004; - - public static void ensure() { - SoLoader.loadLibrary("fb"); - } - - // Code is simplified version of getDetectedVersion() - // from cts/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java - static public boolean supportsOpenGL20() { - EGL10 egl = (EGL10) EGLContext.getEGL(); - EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - int[] numConfigs = new int[1]; - - if (egl.eglInitialize(display, null)) { - try { - if (egl.eglGetConfigs(display, null, 0, numConfigs)) { - EGLConfig[] configs = new EGLConfig[numConfigs[0]]; - if (egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) { - int[] value = new int[1]; - for (int i = 0; i < numConfigs[0]; i++) { - if (egl.eglGetConfigAttrib(display, configs[i], - EGL10.EGL_RENDERABLE_TYPE, value)) { - if ((value[0] & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT) { - return true; - } - } - } - } - } - } finally { - egl.eglTerminate(display); - } - } - return false; - } -} - diff --git a/ReactAndroid/src/main/java/com/facebook/jni/ThreadScopeSupport.java b/ReactAndroid/src/main/java/com/facebook/jni/ThreadScopeSupport.java new file mode 100644 index 00000000000000..89610c43798d66 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/jni/ThreadScopeSupport.java @@ -0,0 +1,22 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +package com.facebook.jni; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.soloader.SoLoader; + +@DoNotStrip +public class ThreadScopeSupport { + static { + SoLoader.loadLibrary("fb"); + } + + // This is just used for ThreadScope::withClassLoader to have a java function + // in the stack so that jni has access to the correct classloader. + @DoNotStrip + private static void runStdFunction(long ptr) { + runStdFunctionImpl(ptr); + } + + private static native void runStdFunctionImpl(long ptr); +} diff --git a/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java b/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java deleted file mode 100644 index 45e9bfe0cee0dd..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java +++ /dev/null @@ -1,25 +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. - */ - -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -@DoNotStrip -public class UnknownCppException extends CppException { - @DoNotStrip - public UnknownCppException() { - super("Unknown"); - } - - @DoNotStrip - public UnknownCppException(String message) { - super(message); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/jni/fbjni.pro b/ReactAndroid/src/main/java/com/facebook/jni/fbjni.pro new file mode 100644 index 00000000000000..5b5b6454d32adf --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/jni/fbjni.pro @@ -0,0 +1,11 @@ +# For common use cases for the hybrid pattern, keep symbols which may +# be referenced only from C++. + +-keepclassmembers class * { + com.facebook.jni.HybridData *; + (com.facebook.jni.HybridData); +} + +-keepclasseswithmembers class * { + com.facebook.jni.HybridData *; +} diff --git a/ReactAndroid/src/main/jni/first-party/fb/Android.mk b/ReactAndroid/src/main/jni/first-party/fb/Android.mk index 6eb6eeab9d7dd8..aaf198bce73ff1 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/Android.mk +++ b/ReactAndroid/src/main/jni/first-party/fb/Android.mk @@ -22,7 +22,7 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_CFLAGS := -DLOG_TAG=\"libfb\" -DDISABLE_XPLAT -fexceptions -frtti +LOCAL_CFLAGS := -DLOG_TAG=\"libfb\" -DDISABLE_CPUCAP -DDISABLE_XPLAT -fexceptions -frtti LOCAL_CFLAGS += -Wall -Werror # include/utils/threads.h has unused parameters LOCAL_CFLAGS += -Wno-unused-parameter diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/Environment.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/Environment.h index bf6c36162afc80..64f9937a65776c 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/Environment.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/Environment.h @@ -8,6 +8,7 @@ */ #pragma once +#include #include #include @@ -21,44 +22,35 @@ struct Environment { // May be null if this thread isn't attached to the JVM FBEXPORT static JNIEnv* current(); static void initialize(JavaVM* vm); + + // There are subtle issues with calling the next functions directly. It is + // much better to always use a ThreadScope to manage attaching/detaching for + // you. FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached(); FBEXPORT static void detachCurrentThread(); }; /** - * RAII Object that attaches a thread to the JVM. Failing to detach from a - * thread before it - * exits will cause a crash, as will calling Detach an extra time, and this - * guard class helps - * keep that straight. In addition, it remembers whether it performed the attach - * or not, so it - * is safe to nest it with itself or with non-fbjni code that manages the - * attachment correctly. + * RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it + * exits will cause a crash, as will calling Detach an extra time, and this guard class helps + * keep that straight. In addition, it remembers whether it performed the attach or not, so it + * is safe to nest it with itself or with non-fbjni code that manages the attachment correctly. * * Potential concerns: - * - Attaching to the JVM is fast (~100us on MotoG), but ideally you would - * attach while the + * - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the * app is not busy. - * - Having a thread detach at arbitrary points is not safe in Dalvik; you need - * to be sure that - * there is no Java code on the current stack or you run the risk of a crash - * like: + * - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that + * there is no Java code on the current stack or you run the risk of a crash like: * ERROR: detaching thread with interp frames (count=18) - * (More detail at - * https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo) - * ThreadScope won't do a detach if the thread was already attached before - * the guard is + * (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo) + * ThreadScope won't do a detach if the thread was already attached before the guard is * instantiated, but there's probably some usage that could trip this up. - * - Newly attached C++ threads only get the bootstrap class loader -- i.e. - * java language - * classes, not any of our application's classes. This will be different - * behavior than threads - * that were initiated on the Java side. A workaround is to pass a global - * reference for a - * class or instance to the new thread; this bypasses the need for the class - * loader. - * (See - * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread) + * - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language + * classes, not any of our application's classes. This will be different behavior than threads + * that were initiated on the Java side. A workaround is to pass a global reference for a + * class or instance to the new thread; this bypasses the need for the class loader. + * (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread) + * If you need access to the application's classes, you can use ThreadScope::WithClassLoader. */ class FBEXPORT ThreadScope { public: @@ -69,6 +61,15 @@ class FBEXPORT ThreadScope { ThreadScope& operator=(ThreadScope&&) = delete; ~ThreadScope(); + /** + * This runs the closure in a scope with fbjni's classloader. This should be + * the same classloader as the rest of the application and thus anything + * running in the closure will have access to the same classes as in a normal + * java-create thread. + */ + static void WithClassLoader(std::function&& runnable); + + static void OnLoad(); private: bool attachedWithThisScope_; }; diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Common.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Common.h index 5db5293044a68e..9da51c406e989f 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Common.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Common.h @@ -29,11 +29,31 @@ # endif #endif +// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as +// a C++ exception. +#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \ + ::facebook::jni::throwPendingJniExceptionAsCppException() + +// If the condition is true, throws a JniException object, which wraps the pending JNI Java +// exception if any. If no pending exception is found, throws a JniException object that wraps a +// RuntimeException throwable.  +#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \ + ::facebook::jni::throwCppExceptionIf(CONDITION) + /// @cond INTERNAL namespace facebook { namespace jni { +FBEXPORT void throwPendingJniExceptionAsCppException(); +FBEXPORT void throwCppExceptionIf(bool condition); + +[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable); +[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg); +template +[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args); + + /** * This needs to be called at library load time, typically in your JNI_OnLoad method. * diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Context.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Context.h index 136ca82f1917f4..8630aa6bf49d80 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Context.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Context.h @@ -25,6 +25,10 @@ class AContext : public JavaClass { return method(self()); } + local_ref getFilesDir() { + static auto method = getClass()->getMethod("getFilesDir"); + return method(self()); + } }; } diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses-inl.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses-inl.h index f5f861ba3c6d11..40fb94b7b5a0d7 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses-inl.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses-inl.h @@ -341,6 +341,13 @@ struct Convert { }; } +// jthrowable ////////////////////////////////////////////////////////////////////////////////////// + +inline local_ref JThrowable::initCause(alias_ref cause) { + static auto meth = javaClassStatic()->getMethod("initCause"); + return meth(self(), cause.get()); +} + // jtypeArray ////////////////////////////////////////////////////////////////////////////////////// namespace detail { diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h index 62b8bb7fe810bf..9e0bb87641f1fe 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h @@ -212,7 +212,7 @@ class FBEXPORT JavaClass : public Base { /// the Java class actually has (i.e. with static create() functions). template static local_ref newInstance(Args... args) { - return detail::newInstance(args...); + return detail::newInstance(args...); } javaobject self() const noexcept; @@ -344,6 +344,8 @@ FBEXPORT local_ref make_jstring(const std::string& modifiedUtf8); class FBEXPORT JThrowable : public JavaClass { public: static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;"; + + local_ref initCause(alias_ref cause); }; namespace detail { diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Exceptions.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Exceptions.h index 8edb871c2d3c12..ce2cc70bdc3ffd 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Exceptions.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Exceptions.h @@ -29,30 +29,26 @@ #include #include "Common.h" - -// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as -// a C++ exception. -#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \ - ::facebook::jni::throwPendingJniExceptionAsCppException() - -// If the condition is true, throws a JniException object, which wraps the pending JNI Java -// exception if any. If no pending exception is found, throws a JniException object that wraps a -// RuntimeException throwable.  -#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \ - ::facebook::jni::throwCppExceptionIf(CONDITION) +#include "References.h" +#include "CoreClasses.h" namespace facebook { namespace jni { -namespace internal { - void initExceptionHelpers(); -} +class JThrowable; -/** - * Before using any of the state initialized above, call this. It - * will assert if initialization has not yet occurred. - */ -FBEXPORT void assertIfExceptionsNotInitialized(); +class JCppException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;"; + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } + + static local_ref create(const std::exception& ex) { + return newInstance(make_jstring(ex.what())); + } +}; // JniException //////////////////////////////////////////////////////////////////////////////////// @@ -67,23 +63,22 @@ FBEXPORT void assertIfExceptionsNotInitialized(); class FBEXPORT JniException : public std::exception { public: JniException(); + ~JniException(); - explicit JniException(jthrowable throwable); + explicit JniException(alias_ref throwable); JniException(JniException &&rhs); JniException(const JniException &other); - ~JniException() noexcept; - - jthrowable getThrowable() const noexcept; + local_ref getThrowable() const noexcept; virtual const char* what() const noexcept; void setJavaException() const noexcept; private: - jthrowable throwableGlobalRef_; + global_ref throwable_; mutable std::string what_; mutable bool isMessageExtracted_; const static std::string kExceptionMessageFailure_; @@ -95,16 +90,8 @@ class FBEXPORT JniException : public std::exception { // Functions that throw C++ exceptions -FBEXPORT void throwPendingJniExceptionAsCppException(); - -FBEXPORT void throwCppExceptionIf(bool condition); - static const int kMaxExceptionMessageBufferSize = 512; -[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable); - -[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg); - // These methods are the preferred way to throw a Java exception from // a C++ function. They create and throw a C++ exception which wraps // a Java exception, so the C++ flow is interrupted. Then, when @@ -113,7 +100,6 @@ static const int kMaxExceptionMessageBufferSize = 512; // thrown to the java caller. template [[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) { - assertIfExceptionsNotInitialized(); int msgSize = snprintf(nullptr, 0, fmt, args...); char *msg = (char*) alloca(msgSize + 1); @@ -123,7 +109,7 @@ template // Identifies any pending C++ exception and throws it as a Java exception. If the exception can't // be thrown, it aborts the program. This is a noexcept function at C++ level. -void translatePendingCppExceptionToJavaException() noexcept; +FBEXPORT void translatePendingCppExceptionToJavaException() noexcept; // For convenience, some exception names in java.lang are available here. diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/JThread.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/JThread.h new file mode 100644 index 00000000000000..60b3cac20ab56b --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/JThread.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#pragma once + +#include "CoreClasses.h" +#include "NativeRunnable.h" + +namespace facebook { +namespace jni { + +class JThread : public JavaClass { + public: + static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;"; + + void start() { + static auto method = javaClassStatic()->getMethod("start"); + method(self()); + } + + void join() { + static auto method = javaClassStatic()->getMethod("join"); + method(self()); + } + + static local_ref create(std::function&& runnable) { + auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable)); + return newInstance(static_ref_cast(jrunnable)); + } +}; + +} +} diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Meta-inl.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Meta-inl.h index dda35925dbc07c..45cf2d1bd80260 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Meta-inl.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Meta-inl.h @@ -66,7 +66,7 @@ local_ref::javaobject> makeArgsArray(Args... args) { } -bool needsSlowPath(alias_ref obj) { +inline bool needsSlowPath(alias_ref obj) { #if defined(__ANDROID__) // On Android 6.0, art crashes when attempting to call a function on a Proxy. // So, when we detect that case we must use the safe, slow workaround. That is, @@ -88,19 +88,6 @@ bool needsSlowPath(alias_ref obj) { } -template -local_ref slowCall(jmethodID method_id, alias_ref self, Args... args) { - static auto invoke = findClassStatic("java/lang/reflect/Method") - ->getMethod::javaobject)>("invoke"); - // TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod. - auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE)); - FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); - if (!reflected) throw JniException(); - auto argsArray = makeArgsArray(args...); - // No need to check for exceptions since invoke is itself a JMethod that will do that for us. - return invoke(reflected, self.get(), argsArray.get()); -} - template inline void JMethod::operator()(alias_ref self, Args... args) { const auto env = Environment::current(); @@ -285,6 +272,19 @@ class JNonvirtualMethod : public JMethodBase { friend class JClass; }; +template +local_ref slowCall(jmethodID method_id, alias_ref self, Args... args) { + static auto invoke = findClassStatic("java/lang/reflect/Method") + ->getMethod::javaobject)>("invoke"); + // TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod. + auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE)); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + if (!reflected) throw std::runtime_error("Unable to get reflected java.lang.reflect.Method"); + auto argsArray = makeArgsArray(args...); + // No need to check for exceptions since invoke is itself a JMethod that will do that for us. + return invoke(reflected, self.get(), argsArray.get()); +} + // JField /////////////////////////////////////////////////////////////////////////////////////// diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/NativeRunnable.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/NativeRunnable.h new file mode 100644 index 00000000000000..68da6a25fb4c7d --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/NativeRunnable.h @@ -0,0 +1,47 @@ +/* + * 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 + +#include "CoreClasses.h" +#include "Hybrid.h" +#include "Registration.h" + +#include + +namespace facebook { +namespace jni { + +struct JRunnable : public JavaClass { + static auto constexpr kJavaDescriptor = "Ljava/lang/Runnable;"; +}; + +struct JNativeRunnable : public HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/NativeRunnable;"; + + JNativeRunnable(std::function&& runnable) : runnable_(std::move(runnable)) {} + + static void OnLoad() { + registerHybrid({ + makeNativeMethod("run", JNativeRunnable::run), + }); + } + + void run() { + runnable_(); + } + + private: + std::function runnable_; +}; + + +} // namespace jni +} // namespace facebook diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/ReferenceAllocators-inl.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/ReferenceAllocators-inl.h index 5f69a3b41503f6..9106dd2532a229 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/ReferenceAllocators-inl.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/ReferenceAllocators-inl.h @@ -13,8 +13,6 @@ #include #include -#include "Exceptions.h" - namespace facebook { namespace jni { diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/jni/LocalString.h b/ReactAndroid/src/main/jni/first-party/fb/include/jni/LocalString.h index 2763e62be8d708..7278fc848f8dd4 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/jni/LocalString.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/jni/LocalString.h @@ -23,8 +23,8 @@ namespace detail { void utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, size_t modifiedLength); size_t modifiedLength(const std::string& str); size_t modifiedLength(const uint8_t* str, size_t* length); -std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len); -std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len); +std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept; +std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept; } diff --git a/ReactAndroid/src/main/jni/first-party/fb/jni/Environment.cpp b/ReactAndroid/src/main/jni/first-party/fb/jni/Environment.cpp index 60f63ef6bf7259..e8ea51dc06877e 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/jni/Environment.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/jni/Environment.cpp @@ -12,12 +12,40 @@ #include #include #include +#include +#include + +#include namespace facebook { namespace jni { -static StaticInitialized> g_env; -static JavaVM* g_vm = nullptr; +namespace { +StaticInitialized> g_env; +JavaVM* g_vm = nullptr; + +struct JThreadScopeSupport : JavaClass { + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/ThreadScopeSupport;"; + + // These reinterpret_casts are a totally dangerous pattern. Don't use them. Use HybridData instead. + static void runStdFunction(std::function&& func) { + static auto method = javaClassStatic()->getStaticMethod("runStdFunction"); + method(javaClassStatic(), reinterpret_cast(&func)); + } + + static void runStdFunctionImpl(alias_ref, jlong ptr) { + (*reinterpret_cast*>(ptr))(); + } + + static void OnLoad() { + // We need the javaClassStatic so that the class lookup is cached and that + // runStdFunction can be called from a ThreadScope-attached thread. + javaClassStatic()->registerNatives({ + makeNativeMethod("runStdFunctionImpl", runStdFunctionImpl), + }); + } +}; +} /* static */ JNIEnv* Environment::current() { @@ -25,6 +53,7 @@ JNIEnv* Environment::current() { if ((env == nullptr) && (g_vm != nullptr)) { if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) { FBLOGE("Error retrieving JNI Environment, thread is probably not attached to JVM"); + // TODO(cjhopman): This should throw an exception. env = nullptr; } else { g_env->reset(env); @@ -85,5 +114,19 @@ ThreadScope::~ThreadScope() { } } +/* static */ +void ThreadScope::OnLoad() { + // These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading. + JThreadScopeSupport::OnLoad(); +} + +/* static */ +void ThreadScope::WithClassLoader(std::function&& runnable) { + // TODO(cjhopman): If the classloader is already available in this scope, we + // shouldn't have to jump through java. + ThreadScope ts; + JThreadScopeSupport::runStdFunction(std::move(runnable)); +} + } } diff --git a/ReactAndroid/src/main/jni/first-party/fb/jni/Exceptions.cpp b/ReactAndroid/src/main/jni/first-party/fb/jni/Exceptions.cpp index e7d38054cfd5a1..bfc99214a904a6 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/jni/Exceptions.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/jni/Exceptions.cpp @@ -7,9 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "fb/fbjni.h" +#include #include +#include #include #include @@ -21,234 +22,113 @@ #include + namespace facebook { namespace jni { -// CommonJniExceptions ///////////////////////////////////////////////////////////////////////////// - -class FBEXPORT CommonJniExceptions { +namespace { +class JRuntimeException : public JavaClass { public: - static void init(); + static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;"; - static jclass getThrowableClass() { - return throwableClass_; + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); } - static jclass getUnknownCppExceptionClass() { - return unknownCppExceptionClass_; + static local_ref create() { + return newInstance(); } +}; + +class JIOException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Ljava/io/IOException;"; - static jthrowable getUnknownCppExceptionObject() { - return unknownCppExceptionObject_; + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); } +}; + +class JOutOfMemoryError : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;"; - static jthrowable getRuntimeExceptionObject() { - return runtimeExceptionObject_; + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); } +}; + +class JArrayIndexOutOfBoundsException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;"; - private: - static jclass throwableClass_; - static jclass unknownCppExceptionClass_; - static jthrowable unknownCppExceptionObject_; - static jthrowable runtimeExceptionObject_; + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } }; -// The variables in this class are all JNI global references and are intentionally leaked because -// we assume this library cannot be unloaded. These global references are created manually instead -// of using global_ref from References.h to avoid circular dependency. -jclass CommonJniExceptions::throwableClass_ = nullptr; -jclass CommonJniExceptions::unknownCppExceptionClass_ = nullptr; -jthrowable CommonJniExceptions::unknownCppExceptionObject_ = nullptr; -jthrowable CommonJniExceptions::runtimeExceptionObject_ = nullptr; - - -// Variable to guarantee that fallback exceptions have been initialized early. We don't want to -// do pure dynamic initialization -- we want to warn programmers early that they need to run the -// helpers at library load time instead of lazily getting them when the exception helpers are -// first used. -static std::atomic gIsInitialized(false); - -void CommonJniExceptions::init() { - JNIEnv* env = internal::getEnv(); - FBASSERTMSGF(env, "Could not get JNI Environment"); - - // Throwable class - jclass localThrowableClass = env->FindClass("java/lang/Throwable"); - FBASSERT(localThrowableClass); - throwableClass_ = static_cast(env->NewGlobalRef(localThrowableClass)); - FBASSERT(throwableClass_); - env->DeleteLocalRef(localThrowableClass); - - // UnknownCppException class - jclass localUnknownCppExceptionClass = env->FindClass("com/facebook/jni/UnknownCppException"); - FBASSERT(localUnknownCppExceptionClass); - jmethodID unknownCppExceptionConstructorMID = env->GetMethodID( - localUnknownCppExceptionClass, - "", - "()V"); - FBASSERT(unknownCppExceptionConstructorMID); - unknownCppExceptionClass_ = static_cast(env->NewGlobalRef(localUnknownCppExceptionClass)); - FBASSERT(unknownCppExceptionClass_); - env->DeleteLocalRef(localUnknownCppExceptionClass); - - // UnknownCppException object - jthrowable localUnknownCppExceptionObject = static_cast(env->NewObject( - unknownCppExceptionClass_, - unknownCppExceptionConstructorMID)); - FBASSERT(localUnknownCppExceptionObject); - unknownCppExceptionObject_ = static_cast(env->NewGlobalRef( - localUnknownCppExceptionObject)); - FBASSERT(unknownCppExceptionObject_); - env->DeleteLocalRef(localUnknownCppExceptionObject); - - // RuntimeException object - jclass localRuntimeExceptionClass = env->FindClass("java/lang/RuntimeException"); - FBASSERT(localRuntimeExceptionClass); - - jmethodID runtimeExceptionConstructorMID = env->GetMethodID( - localRuntimeExceptionClass, - "", - "()V"); - FBASSERT(runtimeExceptionConstructorMID); - jthrowable localRuntimeExceptionObject = static_cast(env->NewObject( - localRuntimeExceptionClass, - runtimeExceptionConstructorMID)); - FBASSERT(localRuntimeExceptionObject); - runtimeExceptionObject_ = static_cast(env->NewGlobalRef(localRuntimeExceptionObject)); - FBASSERT(runtimeExceptionObject_); - - env->DeleteLocalRef(localRuntimeExceptionClass); - env->DeleteLocalRef(localRuntimeExceptionObject); -} +class JUnknownCppException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;"; + static local_ref create() { + return newInstance(); + } -// initExceptionHelpers() ////////////////////////////////////////////////////////////////////////// + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } +}; -void internal::initExceptionHelpers() { - CommonJniExceptions::init(); - gIsInitialized.store(true, std::memory_order_seq_cst); -} +class JCppSystemErrorException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;"; -void assertIfExceptionsNotInitialized() { - // Use relaxed memory order because we don't need memory barriers. - // The real init-once enforcement is done by the compiler for the - // "static" in initExceptionHelpers. - FBASSERTMSGF(gIsInitialized.load(std::memory_order_relaxed), - "initExceptionHelpers was never called!"); -} + static local_ref create(const std::system_error& e) { + return newInstance(make_jstring(e.what()), e.code().value()); + } +}; // Exception throwing & translating functions ////////////////////////////////////////////////////// // Functions that throw Java exceptions -namespace { - -void setJavaExceptionAndAbortOnFailure(jthrowable throwable) noexcept { - assertIfExceptionsNotInitialized(); - JNIEnv* env = internal::getEnv(); +void setJavaExceptionAndAbortOnFailure(alias_ref throwable) { + auto env = Environment::current(); if (throwable) { - env->Throw(throwable); + env->Throw(throwable.get()); } if (env->ExceptionCheck() != JNI_TRUE) { std::abort(); } } -void setDefaultException() noexcept { - assertIfExceptionsNotInitialized(); - setJavaExceptionAndAbortOnFailure(CommonJniExceptions::getRuntimeExceptionObject()); -} - -void setCppSystemErrorExceptionInJava(const std::system_error& ex) noexcept { - assertIfExceptionsNotInitialized(); - JNIEnv* env = internal::getEnv(); - jclass cppSystemErrorExceptionClass = env->FindClass( - "com/facebook/jni/CppSystemErrorException"); - if (!cppSystemErrorExceptionClass) { - setDefaultException(); - return; - } - jmethodID constructorMID = env->GetMethodID( - cppSystemErrorExceptionClass, - "", - "(Ljava/lang/String;I)V"); - if (!constructorMID) { - setDefaultException(); - return; - } - jthrowable cppSystemErrorExceptionObject = static_cast(env->NewObject( - cppSystemErrorExceptionClass, - constructorMID, - env->NewStringUTF(ex.what()), - ex.code().value())); - setJavaExceptionAndAbortOnFailure(cppSystemErrorExceptionObject); -} - -template -void setNewJavaException(jclass exceptionClass, const char* fmt, ARGS... args) { - assertIfExceptionsNotInitialized(); - int msgSize = snprintf(nullptr, 0, fmt, args...); - JNIEnv* env = internal::getEnv(); - - try { - char *msg = (char*) alloca(msgSize + 1); - snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...); - env->ThrowNew(exceptionClass, msg); - } catch (...) { - env->ThrowNew(exceptionClass, ""); - } - - if (env->ExceptionCheck() != JNI_TRUE) { - setDefaultException(); - } -} - -void setNewJavaException(jclass exceptionClass, const char* msg) { - assertIfExceptionsNotInitialized(); - setNewJavaException(exceptionClass, "%s", msg); -} - -template -void setNewJavaException(const char* className, const char* fmt, ARGS... args) { - assertIfExceptionsNotInitialized(); - JNIEnv* env = internal::getEnv(); - jclass exceptionClass = env->FindClass(className); - if (env->ExceptionCheck() != JNI_TRUE && !exceptionClass) { - // If FindClass() has failed but no exception has been thrown, throw a default exception. - setDefaultException(); - return; - } - setNewJavaException(exceptionClass, fmt, args...); -} - } // Functions that throw C++ exceptions // TODO(T6618159) Take a stack dump here to save context if it results in a crash when propagated -FBEXPORT void throwPendingJniExceptionAsCppException() { - assertIfExceptionsNotInitialized(); - JNIEnv* env = internal::getEnv(); +void throwPendingJniExceptionAsCppException() { + JNIEnv* env = Environment::current(); if (env->ExceptionCheck() == JNI_FALSE) { return; } - jthrowable throwable = env->ExceptionOccurred(); + auto throwable = adopt_local(env->ExceptionOccurred()); if (!throwable) { throw std::runtime_error("Unable to get pending JNI exception."); } - env->ExceptionClear(); + throw JniException(throwable); } void throwCppExceptionIf(bool condition) { - assertIfExceptionsNotInitialized(); if (!condition) { return; } - JNIEnv* env = internal::getEnv(); + auto env = Environment::current(); if (env->ExceptionCheck() == JNI_TRUE) { throwPendingJniExceptionAsCppException(); return; @@ -257,13 +137,11 @@ void throwCppExceptionIf(bool condition) { throw JniException(); } -FBEXPORT void throwNewJavaException(jthrowable throwable) { - throw JniException(throwable); +void throwNewJavaException(jthrowable throwable) { + throw JniException(wrap_alias(throwable)); } -FBEXPORT void throwNewJavaException( - const char* throwableName, - const char* msg) { +void throwNewJavaException(const char* throwableName, const char* msg) { // If anything of the fbjni calls fail, an exception of a suitable // form will be thrown, which is what we want. auto throwableClass = findClassLocal(throwableName); @@ -275,34 +153,80 @@ FBEXPORT void throwNewJavaException( // Translate C++ to Java Exception -FBEXPORT void translatePendingCppExceptionToJavaException() noexcept { - assertIfExceptionsNotInitialized(); +namespace { + +// The implementation std::rethrow_if_nested uses a dynamic_cast to determine +// if the exception is a nested_exception. If the exception is from a library +// built with -fno-rtti, then that will crash. This avoids that. +void rethrow_if_nested() { + try { + throw; + } catch (const std::nested_exception& e) { + e.rethrow_nested(); + } catch (...) { + } +} + +// For each exception in the chain of the currently handled exception, func +// will be called with that exception as the currently handled exception (in +// reverse order, i.e. innermost first). +void denest(std::function func) { try { + throw; + } catch (const std::exception& e) { + try { + rethrow_if_nested(); + } catch (...) { + denest(func); + } + func(); + } catch (...) { + func(); + } +} +} + +void translatePendingCppExceptionToJavaException() noexcept { + local_ref previous; + auto func = [&previous] () { + local_ref current; try { throw; } catch(const JniException& ex) { - ex.setJavaException(); + current = ex.getThrowable(); } catch(const std::ios_base::failure& ex) { - setNewJavaException("java/io/IOException", ex.what()); + current = JIOException::create(ex.what()); } catch(const std::bad_alloc& ex) { - setNewJavaException("java/lang/OutOfMemoryError", ex.what()); + current = JOutOfMemoryError::create(ex.what()); } catch(const std::out_of_range& ex) { - setNewJavaException("java/lang/ArrayIndexOutOfBoundsException", ex.what()); + current = JArrayIndexOutOfBoundsException::create(ex.what()); } catch(const std::system_error& ex) { - setCppSystemErrorExceptionInJava(ex); + current = JCppSystemErrorException::create(ex); } catch(const std::runtime_error& ex) { - setNewJavaException("java/lang/RuntimeException", ex.what()); + current = JRuntimeException::create(ex.what()); } catch(const std::exception& ex) { - setNewJavaException("com/facebook/jni/CppException", ex.what()); + current = JCppException::create(ex.what()); } catch(const char* msg) { - setNewJavaException(CommonJniExceptions::getUnknownCppExceptionClass(), msg); + current = JUnknownCppException::create(msg); } catch(...) { - setJavaExceptionAndAbortOnFailure(CommonJniExceptions::getUnknownCppExceptionObject()); + current = JUnknownCppException::create(); } - } catch(...) { - // This block aborts the program, if something bad happens when handling exceptions, thus - // keeping this function noexcept. - std::abort(); + if (previous) { + current->initCause(previous); + } + previous = current; + }; + + try { + denest(func); + setJavaExceptionAndAbortOnFailure(previous); + } catch (std::exception& e) { + FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException: %s", e.what()); + // rethrow the exception and let the noexcept handling abort. + throw; + } catch (...) { + FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException"); + throw; } } @@ -310,79 +234,41 @@ FBEXPORT void translatePendingCppExceptionToJavaException() noexcept { const std::string JniException::kExceptionMessageFailure_ = "Unable to get exception message."; -JniException::JniException() : JniException(CommonJniExceptions::getRuntimeExceptionObject()) { } +JniException::JniException() : JniException(JRuntimeException::create()) { } -JniException::JniException(jthrowable throwable) : isMessageExtracted_(false) { - assertIfExceptionsNotInitialized(); - throwableGlobalRef_ = static_cast(internal::getEnv()->NewGlobalRef(throwable)); - if (!throwableGlobalRef_) { - throw std::bad_alloc(); - } +JniException::JniException(alias_ref throwable) : isMessageExtracted_(false) { + throwable_ = make_global(throwable); } JniException::JniException(JniException &&rhs) - : throwableGlobalRef_(std::move(rhs.throwableGlobalRef_)), + : throwable_(std::move(rhs.throwable_)), what_(std::move(rhs.what_)), isMessageExtracted_(rhs.isMessageExtracted_) { - rhs.throwableGlobalRef_ = nullptr; } JniException::JniException(const JniException &rhs) : what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) { - JNIEnv* env = internal::getEnv(); - if (rhs.getThrowable()) { - throwableGlobalRef_ = static_cast(env->NewGlobalRef(rhs.getThrowable())); - if (!throwableGlobalRef_) { - throw std::bad_alloc(); - } - } else { - throwableGlobalRef_ = nullptr; - } + throwable_ = make_global(rhs.throwable_); } -JniException::~JniException() noexcept { - if (throwableGlobalRef_) { - internal::getEnv()->DeleteGlobalRef(throwableGlobalRef_); - } +JniException::~JniException() { + ThreadScope ts; + throwable_.reset(); } -jthrowable JniException::getThrowable() const noexcept { - return throwableGlobalRef_; +local_ref JniException::getThrowable() const noexcept { + return make_local(throwable_); } // TODO 6900503: consider making this thread-safe. void JniException::populateWhat() const noexcept { - JNIEnv* env = internal::getEnv(); - - jmethodID toStringMID = env->GetMethodID( - CommonJniExceptions::getThrowableClass(), - "toString", - "()Ljava/lang/String;"); - jstring messageJString = (jstring) env->CallObjectMethod( - throwableGlobalRef_, - toStringMID); - - isMessageExtracted_ = true; - - if (env->ExceptionCheck()) { - env->ExceptionClear(); - what_ = kExceptionMessageFailure_; - return; - } - - const char* chars = env->GetStringUTFChars(messageJString, nullptr); - if (!chars) { - what_ = kExceptionMessageFailure_; - return; - } - + ThreadScope ts; try { - what_ = std::string(chars); + what_ = throwable_->toString(); + isMessageExtracted_ = true; } catch(...) { what_ = kExceptionMessageFailure_; } - - env->ReleaseStringUTFChars(messageJString, chars); } const char* JniException::what() const noexcept { @@ -393,7 +279,7 @@ const char* JniException::what() const noexcept { } void JniException::setJavaException() const noexcept { - setJavaExceptionAndAbortOnFailure(throwableGlobalRef_); + setJavaExceptionAndAbortOnFailure(throwable_); } }} diff --git a/ReactAndroid/src/main/jni/first-party/fb/jni/LocalString.cpp b/ReactAndroid/src/main/jni/first-party/fb/jni/LocalString.cpp index 1a0e8d703a1236..2e197f07598397 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/jni/LocalString.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/jni/LocalString.cpp @@ -156,7 +156,7 @@ void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size modified[j++] = '\0'; } -std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) { +std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept { // Converting from modified utf8 to utf8 will always shrink, so this will always be sufficient std::string utf8(len, 0); size_t j = 0; @@ -230,7 +230,7 @@ size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) { return utf8StringLen; } -std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) { +std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept { if (!utf16String || utf16StringLen <= 0) { return ""; } diff --git a/ReactAndroid/src/main/jni/first-party/fb/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/first-party/fb/jni/OnLoad.cpp index d5041613378886..0334729c8e20b6 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/jni/OnLoad.cpp @@ -10,10 +10,13 @@ #include #include #include +#include using namespace facebook::jni; void initialize_fbjni() { CountableOnLoad(Environment::current()); HybridDataOnLoad(); + JNativeRunnable::OnLoad(); + ThreadScope::OnLoad(); } diff --git a/ReactAndroid/src/main/jni/first-party/fb/jni/fbjni.cpp b/ReactAndroid/src/main/jni/first-party/fb/jni/fbjni.cpp index e2bd795fab64e4..de9423c2019204 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/jni/fbjni.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/jni/fbjni.cpp @@ -26,7 +26,6 @@ jint initialize(JavaVM* vm, std::function&& init_fn) noexcept { std::call_once(flag, [vm] { try { Environment::initialize(vm); - internal::initExceptionHelpers(); } catch (std::exception& ex) { error_occured = true; try { @@ -58,6 +57,9 @@ jint initialize(JavaVM* vm, std::function&& init_fn) noexcept { alias_ref findClassStatic(const char* name) { const auto env = internal::getEnv(); + if (!env) { + throw std::runtime_error("Unable to retrieve JNIEnv*."); + } auto cls = env->FindClass(name); FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls); auto leaking_ref = (jclass)env->NewGlobalRef(cls); @@ -67,6 +69,9 @@ alias_ref findClassStatic(const char* name) { local_ref findClassLocal(const char* name) { const auto env = internal::getEnv(); + if (!env) { + throw std::runtime_error("Unable to retrieve JNIEnv*."); + } auto cls = env->FindClass(name); FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls); return adopt_local(cls); diff --git a/ReactAndroid/src/main/jni/first-party/fb/lyra/lyra.cpp b/ReactAndroid/src/main/jni/first-party/fb/lyra/lyra.cpp index c3d25df1ac5952..f915ecef29d606 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/lyra/lyra.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/lyra/lyra.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -15,6 +16,21 @@ namespace lyra { namespace { +class IosFlagsSaver { + ios_base& ios_; + ios_base::fmtflags flags_; + + public: + IosFlagsSaver(ios_base& ios) + : ios_(ios), + flags_(ios.flags()) + {} + + ~IosFlagsSaver() { + ios_.flags(flags_); + } +}; + struct BacktraceState { size_t skip; vector& stackTrace; @@ -71,6 +87,8 @@ void getStackTraceSymbols(vector& symbols, } ostream& operator<<(ostream& out, const StackTraceElement& elm) { + IosFlagsSaver flags{out}; + // TODO(t10748683): Add build id to the output out << "{dso=" << elm.libraryName() << " offset=" << hex << showbase << elm.libraryOffset(); @@ -88,6 +106,8 @@ ostream& operator<<(ostream& out, const StackTraceElement& elm) { // TODO(t10737667): The implement a tool that parse the stack trace and // symbolicate it ostream& operator<<(ostream& out, const vector& trace) { + IosFlagsSaver flags{out}; + auto i = 0; out << "Backtrace:\n"; for (auto& elm : trace) { diff --git a/ReactAndroid/src/main/jni/first-party/fb/onload.cpp b/ReactAndroid/src/main/jni/first-party/fb/onload.cpp index 9357940a4ff151..2caebc37a4eb38 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/onload.cpp +++ b/ReactAndroid/src/main/jni/first-party/fb/onload.cpp @@ -8,6 +8,9 @@ */ #include +#ifndef DISABLE_CPUCAP +#include +#endif #include using namespace facebook::jni; @@ -20,6 +23,9 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { initialize_fbjni(); #ifndef DISABLE_XPLAT initialize_xplatinit(); +#endif +#ifndef DISABLE_CPUCAP + initialize_cpucapabilities(); #endif }); } From d49ea020e19914cec1cb230d2320b8131da65657 Mon Sep 17 00:00:00 2001 From: Kevin Lacker Date: Wed, 6 Jul 2016 12:58:58 -0700 Subject: [PATCH 005/241] update blog with misc minor requests Summary: A couple changes people requested. None of the author links were doing anything, so now they link to Twitter profiles, and also the references to react-native-web have a standardized style w\ react-native-web-player. Closes https://github.com/facebook/react-native/pull/8617 Differential Revision: D3524125 Pulled By: JoelMarcey fbshipit-source-id: 68afa1bafeceed98f82fa4ced766da143790b5ba --- blog/2016-03-24-introducing-hot-reloading.md | 1 + blog/2016-07-06-toward-better-documentation.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/blog/2016-03-24-introducing-hot-reloading.md b/blog/2016-03-24-introducing-hot-reloading.md index fe59f919e348b1..f9121d48f6aa31 100644 --- a/blog/2016-03-24-introducing-hot-reloading.md +++ b/blog/2016-03-24-introducing-hot-reloading.md @@ -1,6 +1,7 @@ --- title: Introducing Hot Reloading author: Martín Bigio +authorURL: https://twitter.com/martinbigio --- React Native's goal is to give you the best possible developer experience. A big part of it is the time it takes between you save a file and be able to see the changes. Our goal is to get this feedback loop to be under 1 second, even as your app grows. diff --git a/blog/2016-07-06-toward-better-documentation.md b/blog/2016-07-06-toward-better-documentation.md index fd3e9879c44690..80dc604c5ef794 100644 --- a/blog/2016-07-06-toward-better-documentation.md +++ b/blog/2016-07-06-toward-better-documentation.md @@ -1,6 +1,7 @@ --- title: Toward Better Documentation author: Kevin Lacker +authorURL: https://twitter.com/lacker --- Part of having a great developer experience is having great documentation. A lot goes into creating good docs - the ideal documentation is concise, helpful, accurate, complete, and delightful. Recently we've been working hard to make the docs better based on your feedback, and we wanted to share some of the improvements we've made. @@ -33,7 +34,7 @@ AppRegistry.registerComponent('ScratchPad', () => ScratchPad); We think these inline examples, using the [`react-native-web-player`](https://github.com/dabbott/react-native-web-player) module with help from [Devin Abbott](https://twitter.com/devinaabbott), are a great way to learn the basics of React Native, and we have updated our [tutorial for new React Native developers](/react-native/docs/tutorial.html) to use these wherever possible. Check it out - if you have ever been curious to see what would happen if you modified just one little bit of sample code, this is a really nice way to poke around. Also, if you're building developer tools and you want to show a live React Native sample on your own site, [`react-native-web-player`](https://github.com/dabbott/react-native-web-player) can make that straightforward. -The core simulation engine is provided by the [react-native-web](https://github.com/necolas/react-native-web) library, which provides a way to display React Native components like `Text` and `View` on the web. Check out [react-native-web](https://github.com/necolas/react-native-web) if you're interested in building mobile and web experiences that share a large chunk of the codebase. +The core simulation engine is provided by [Nicolas Gallagher](https://twitter.com/necolas)'s [`react-native-web`](https://github.com/necolas/react-native-web) project, which provides a way to display React Native components like `Text` and `View` on the web. Check out [`react-native-web`](https://github.com/necolas/react-native-web) if you're interested in building mobile and web experiences that share a large chunk of the codebase. ## Better Guides From c4fc504094229a579d51d413140709ab328e2018 Mon Sep 17 00:00:00 2001 From: tychota Date: Wed, 6 Jul 2016 13:03:39 -0700 Subject: [PATCH 006/241] Fix the yeoman-environment version Summary: This closes #8610. The release of 1.6.2 of yeoman-environment break react-native (since there is no lib folder) Yeoman will be removed from RN as #8197 anyway. Thanks cpsubrian, RobTS **Test plan (required)** I installed the 1.2.7 version and copied it in react-native nodes_modules. Then I ran react-native upgrade screen shot 2016-07-06 at 21 09 33 Closes https://github.com/facebook/react-native/pull/8614 Differential Revision: D3524043 fbshipit-source-id: 1def4854ca0fd881b8a935f37c86eb373dfd97c5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59f40476e31fe2..4a326705c024b9 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "xcode": "^0.8.2", "xmldoc": "^0.4.0", "yargs": "^3.24.0", - "yeoman-environment": "^1.2.7", + "yeoman-environment": "~1.2.7", "yeoman-generator": "^0.20.3" }, "devDependencies": { From a7d032b7071d7477a624836ec36cf23603774c3f Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Wed, 6 Jul 2016 16:01:17 -0700 Subject: [PATCH 007/241] Display JS exceptions and stacks in a red box Reviewed By: astreet Differential Revision: D3510875 fbshipit-source-id: a7042434b68cb849f5b0c4ef782befff6a27ef5c --- .../devsupport/DevSupportManagerImpl.java | 9 ++++- .../react/devsupport/JSException.java | 37 +++++++++++++++++++ .../jni/xreact/jni/JMessageQueueThread.cpp | 30 ++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/devsupport/JSException.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java index dd823f84ada663..8bb5423e9c471a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java @@ -79,6 +79,7 @@ public class DevSupportManagerImpl implements DevSupportManager { private static final int JAVA_ERROR_COOKIE = -1; + private static final int JSEXCEPTION_ERROR_COOKIE = -1; private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js"; private static enum ErrorType { JS, @@ -194,7 +195,13 @@ public void onReceive(Context context, Intent intent) { public void handleException(Exception e) { if (mIsDevSupportEnabled) { FLog.e(ReactConstants.TAG, "Exception in native call from JS", e); - showNewJavaError(e.getMessage(), e); + if (e instanceof JSException) { + // TODO #11638796: convert the stack into something useful + showNewError(e.getMessage() + "\n\n" + ((JSException) e).getStack(), new StackFrame[] {}, + JSEXCEPTION_ERROR_COOKIE, ErrorType.JS); + } else { + showNewJavaError(e.getMessage(), e); + } } else { mDefaultNativeModuleCallExceptionHandler.handleException(e); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/JSException.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/JSException.java new file mode 100644 index 00000000000000..ca83f57bb7b7da --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/JSException.java @@ -0,0 +1,37 @@ +/** + * 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.devsupport; + +import com.facebook.proguard.annotations.DoNotStrip; + +/** + * This represents an error evaluating JavaScript. It includes the usual + * message, and the raw JS stack where the error occurred (which may be empty). + */ + +@DoNotStrip +public class JSException extends Exception { + private final String mStack; + + @DoNotStrip + public JSException(String message, String stack, Throwable cause) { + super(message, cause); + mStack = stack; + } + + public JSException(String message, String stack) { + super(message); + mStack = stack; + } + + public String getStack() { + return mStack; + } +} diff --git a/ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.cpp b/ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.cpp index 6cba3235cd2f83..2510c2841bed0f 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.cpp @@ -9,11 +9,37 @@ #include #include +#include + #include "JNativeRunnable.h" namespace facebook { namespace react { +namespace { + +struct JavaJSException : jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/JSException;"; + + static local_ref create(const char* message, const char* stack, + const std::exception& ex) { + local_ref cause = jni::JCppException::create(ex); + return newInstance(make_jstring(message), make_jstring(stack), cause.get()); + } +}; + +std::function wrapRunnable(std::function&& runnable) { + return [runnable=std::move(runnable)] { + try { + runnable(); + } catch (const JSException& ex) { + throwNewJavaException(JavaJSException::create(ex.what(), ex.getStack().c_str(), ex).get()); + } + }; +} + +} + JMessageQueueThread::JMessageQueueThread(alias_ref jobj) : m_jobj(make_global(jobj)) { } @@ -21,7 +47,7 @@ JMessageQueueThread::JMessageQueueThread(alias_ref&& runnable) { static auto method = JavaMessageQueueThread::javaClassStatic()-> getMethod("runOnQueue"); - method(m_jobj, JNativeRunnable::newObjectCxxArgs(runnable).get()); + method(m_jobj, JNativeRunnable::newObjectCxxArgs(wrapRunnable(std::move(runnable))).get()); } void JMessageQueueThread::runOnQueueSync(std::function&& runnable) { @@ -29,7 +55,7 @@ void JMessageQueueThread::runOnQueueSync(std::function&& runnable) { getMethod("isOnThread"); if (jIsOnThread(m_jobj)) { - runnable(); + wrapRunnable(std::move(runnable))(); } else { std::mutex signalMutex; std::condition_variable signalCv; From e209f2f1e93b12d64be8197bbe8eba7e28355ded Mon Sep 17 00:00:00 2001 From: Mengjue Wang Date: Wed, 6 Jul 2016 16:26:59 -0700 Subject: [PATCH 008/241] Move Bridge functions to OSS -- RTL experiment Summary: Delete the bridge functions(isRTL, allowRTL()) in internal module and move to OSS. Create bridge for RCTI18nUtil Reviewed By: fkgozali Differential Revision: D3519224 fbshipit-source-id: 3853edcfcc78777d957874448117de72ae0700b5 --- React/Modules/RCTI18nManager.h | 19 ++++++++++++++++++ React/Modules/RCTI18nManager.m | 29 +++++++++++++++++++++++++++ React/React.xcodeproj/project.pbxproj | 6 ++++++ 3 files changed, 54 insertions(+) create mode 100644 React/Modules/RCTI18nManager.h create mode 100644 React/Modules/RCTI18nManager.m diff --git a/React/Modules/RCTI18nManager.h b/React/Modules/RCTI18nManager.h new file mode 100644 index 00000000000000..5a36d0f2c79a2c --- /dev/null +++ b/React/Modules/RCTI18nManager.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTBridgeModule.h" + +/** + * @experimental + * This is a experimental module for RTL support + * This module bridges the i18n utility from RCTI18nUtil + */ +@interface RCTI18nManager : NSObject + +@end diff --git a/React/Modules/RCTI18nManager.m b/React/Modules/RCTI18nManager.m new file mode 100644 index 00000000000000..32371f229ae7f1 --- /dev/null +++ b/React/Modules/RCTI18nManager.m @@ -0,0 +1,29 @@ +/** + * 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 "RCTI18nManager.h" +#import "RCTI18nUtil.h" + +@implementation RCTI18nManager + +RCT_EXPORT_MODULE() + +RCT_EXPORT_METHOD(allowRTL:(BOOL)value) +{ + [[RCTI18nUtil sharedInstance] setAllowRTL:value]; +} + +- (NSDictionary *)constantsToExport +{ + return @{ + @"isRTL": @([[RCTI18nUtil sharedInstance] isRTL]) + }; +} + +@end diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 08c7d0ef7cf494..3873f67f1c8d9d 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -96,6 +96,7 @@ 83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; }; 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; }; 85C199EE1CD2407900DAD810 /* RCTJSCWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 85C199ED1CD2407900DAD810 /* RCTJSCWrapper.mm */; }; + B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */; }; B95154321D1B34B200FE7B80 /* RCTActivityIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */; }; E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; /* End PBXBuildFile section */ @@ -310,6 +311,8 @@ 85C199EC1CD2407900DAD810 /* RCTJSCWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSCWrapper.h; sourceTree = ""; }; 85C199ED1CD2407900DAD810 /* RCTJSCWrapper.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = RCTJSCWrapper.mm; sourceTree = ""; }; ACDD3FDA1BC7430D00E7DE33 /* RCTBorderStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderStyle.h; sourceTree = ""; }; + B233E6E81D2D843200BC68BA /* RCTI18nManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTI18nManager.h; sourceTree = ""; }; + B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTI18nManager.m; sourceTree = ""; }; 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 = ""; }; E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTextDecorationLineType.h; sourceTree = ""; }; @@ -377,6 +380,8 @@ 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */, 352DCFEE1D19F4C20056D623 /* RCTI18nUtil.h */, 352DCFEF1D19F4C20056D623 /* RCTI18nUtil.m */, + B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */, + B233E6E81D2D843200BC68BA /* RCTI18nManager.h */, 13D9FEEC1CDCD93000158BD7 /* RCTKeyboardObserver.h */, 13D9FEED1CDCD93000158BD7 /* RCTKeyboardObserver.m */, 13B07FED1A69327A00A75B9A /* RCTTiming.h */, @@ -749,6 +754,7 @@ 13E067571A70F44B002CDEE1 /* RCTView.m in Sources */, 13AFBCA01C07247D00BBAEAA /* RCTMapOverlay.m in Sources */, 13D9FEEE1CDCD93000158BD7 /* RCTKeyboardObserver.m in Sources */, + B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */, 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */, 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */, 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */, From 201433f05b6820e807ef0eade764528afe5e16ea Mon Sep 17 00:00:00 2001 From: Konstantin Raev Date: Thu, 7 Jul 2016 03:04:38 -0700 Subject: [PATCH 009/241] Fixed flowconfig for user apps Reviewed By: davidaurelio Differential Revision: D3528031 fbshipit-source-id: baf74d68127038659f88fd65b8eb3fe41444d9aa --- .flowconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.flowconfig b/.flowconfig index fd80d1db240c70..6eff3cb815acbb 100644 --- a/.flowconfig +++ b/.flowconfig @@ -19,9 +19,10 @@ .*/node_modules/commoner/test/source/widget/share.js # Ignore duplicate module providers -/Libraries/react-native/React.js -/Libraries/react-native/ReactNative.js -/node_modules/jest-runtime/build/__tests__/.* +# For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root +.*/Libraries/react-native/React.js +.*/Libraries/react-native/ReactNative.js +.*/node_modules/jest-runtime/build/__tests__/.* [include] From 10d41b50c2f3475cfbb6b7e6f8d76da5c07b16c8 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 7 Jul 2016 04:36:00 -0700 Subject: [PATCH 010/241] Add getType/getPrivate to Value Summary: Adds: - getType so you can switch on type - getPrivate Reviewed By: lexs Differential Revision: D3515510 fbshipit-source-id: d574b04f563ac650bacec3751b50be6345e8439a --- ReactCommon/cxxreact/Value.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ReactCommon/cxxreact/Value.h b/ReactCommon/cxxreact/Value.h index c43a523a7fce41..1aa965e9c31415 100644 --- a/ReactCommon/cxxreact/Value.h +++ b/ReactCommon/cxxreact/Value.h @@ -191,6 +191,11 @@ class Object : public noncopyable { } } + template + ReturnType* getPrivate() const { + return static_cast(JSObjectGetPrivate(m_obj)); + } + JSContextRef context() const { return m_context; } @@ -223,6 +228,10 @@ class Value : public noncopyable { return m_value; } + JSType getType() const { + return JSValueGetType(m_context, m_value); + } + bool isBoolean() const { return JSValueIsBoolean(context(), m_value); } From f5345601d9c16e5b70c2534acdb567e0e1b28134 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 7 Jul 2016 04:48:45 -0700 Subject: [PATCH 011/241] Add JSCHelper function to easily install native hooks outside of JSCExecutor Summary: JSCExecutor has something like this, but for methods on JSCExecutor itself. Open to better ideas around how to share code Reviewed By: lexs Differential Revision: D3516314 fbshipit-source-id: 4b1265916c52d582bb0b9348e9b4a099f566d6c9 --- ReactCommon/cxxreact/JSCExecutor.cpp | 7 +------ ReactCommon/cxxreact/JSCHelpers.cpp | 9 +++++++++ ReactCommon/cxxreact/JSCHelpers.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index 5bd5ba63ea0ae9..0b624da12ee5cb 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -66,12 +66,7 @@ inline JSObjectCallAsFunctionCallback exceptionWrapMethod() { auto executor = static_cast(JSObjectGetPrivate(globalObj)); return (executor->*method)(argumentCount, arguments); } catch (...) { - try { - auto functionName = Object(ctx, function).getProperty("name").toString().str(); - *exception = translatePendingCppExceptionToJSError(ctx, functionName.c_str()); - } catch (...) { - *exception = makeJSError(ctx, "Failed to get function name while handling exception"); - } + *exception = translatePendingCppExceptionToJSError(ctx, function); return JSValueMakeUndefined(ctx); } } diff --git a/ReactCommon/cxxreact/JSCHelpers.cpp b/ReactCommon/cxxreact/JSCHelpers.cpp index d0aa0a0eda59ef..40554219ddf4ae 100644 --- a/ReactCommon/cxxreact/JSCHelpers.cpp +++ b/ReactCommon/cxxreact/JSCHelpers.cpp @@ -113,4 +113,13 @@ JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *e } } +JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause) { + try { + auto functionName = Object(ctx, jsFunctionCause).getProperty("name").toString().str(); + return translatePendingCppExceptionToJSError(ctx, functionName.c_str()); + } catch (...) { + return makeJSError(ctx, "Failed to get function name while handling exception"); + } +} + } } diff --git a/ReactCommon/cxxreact/JSCHelpers.h b/ReactCommon/cxxreact/JSCHelpers.h index 0d2609a754bcdc..3be708164ae028 100644 --- a/ReactCommon/cxxreact/JSCHelpers.h +++ b/ReactCommon/cxxreact/JSCHelpers.h @@ -52,5 +52,33 @@ JSValueRef evaluateScript( JSValueRef makeJSError(JSContextRef ctx, const char *error); JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation); +JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause); + +template +inline JSObjectCallAsFunctionCallback exceptionWrapMethod() { + struct funcWrapper { + static JSValueRef call( + JSContextRef ctx, + JSObjectRef function, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef *exception) { + try { + return (*method)(ctx, function, thisObject, argumentCount, arguments, exception); + } catch (...) { + *exception = translatePendingCppExceptionToJSError(ctx, function); + return JSValueMakeUndefined(ctx); + } + } + }; + + return &funcWrapper::call; +} } } From 4f5c2b48fe9e0e9d2f5024838ee16006f35ba326 Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Thu, 7 Jul 2016 05:50:58 -0700 Subject: [PATCH 012/241] Fix timestamps on android touch events to use milliseconds, to be consistent with iOS Summary: So `PanReponder.onPanResponderRelease/onPanResponderTerminate` receive a `gestureState` object containing a `onPanResponderTerminate.vx/vy` property. On Android and iOS, they appear to be orders of magnitude different, which appear to be due to the different scale of timestamps that are used when generating touch events. This pull request fixes the timestamps to be milliseconds on both platforms (since I assume iOS is the more authoritative one, and is the one that `react-native-viewpager`'s vx thresholds written written to compare against.) As far as I can tell, the RN code doesn't use the `vx/vy` properties, so they should be okay. And looks like the RN code only cares about relative values of `startTimestamp/currentTimestamp/previousTimestamp` though, so should be fine too. it's quite possible there will be downstream android breakage with this change, particularly for those who are already compensating for the RN discrepancy. Closes https://github.com/facebook/react-native/pull/8199 Differential Revision: D3528215 Pulled By: dmmiller fbshipit-source-id: cbd25bb7e7bb87fa77b661a057643a6ea97bc3f1 --- .../facebook/react/tests/TextInputTestCase.java | 12 ++++++------ .../com/facebook/react/common/SystemClock.java | 4 ++++ .../com/facebook/react/modules/core/Timing.java | 2 +- .../react/uimanager/JSTouchDispatcher.java | 12 ++++++------ .../facebook/react/uimanager/OnLayoutEvent.java | 2 +- .../views/drawer/ReactDrawerLayoutManager.java | 8 ++++---- .../facebook/react/views/image/ReactImageView.java | 10 +++++----- .../react/views/modal/ReactModalHostManager.java | 4 ++-- .../react/views/picker/ReactPickerManager.java | 2 +- .../recyclerview/RecyclerViewBackedScrollView.java | 4 ++-- .../react/views/scroll/ReactScrollViewHelper.java | 2 +- .../react/views/slider/ReactSliderManager.java | 4 ++-- .../swiperefresh/SwipeRefreshLayoutManager.java | 2 +- .../react/views/switchview/ReactSwitchManager.java | 2 +- .../views/textinput/ReactTextInputManager.java | 14 +++++++------- .../react/views/toolbar/ReactToolbarManager.java | 4 ++-- .../react/views/viewpager/ReactViewPager.java | 6 +++--- .../react/views/webview/ReactWebViewManager.java | 8 ++++---- .../test/java/com/facebook/react/RootViewTest.java | 6 +++--- .../react/modules/timing/TimingModuleTest.java | 2 +- 20 files changed, 57 insertions(+), 53 deletions(-) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java index 9558a5d912af60..44c2321b2aadf3 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java @@ -116,7 +116,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -125,7 +125,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), "", start, @@ -150,7 +150,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -159,7 +159,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), moreText, "", start, @@ -184,7 +184,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -193,7 +193,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), moreText, "", start, diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java b/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java index 29c31b416c5f75..1af4b55368bfb9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java @@ -22,4 +22,8 @@ public static long currentTimeMillis() { public static long nanoTime() { return System.nanoTime(); } + + public static long elapsedRealtime() { + return android.os.SystemClock.elapsedRealtime(); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java index d0ee51cfcd191f..394d025fa87006 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java @@ -244,7 +244,7 @@ public void createTimer( return; } - long initialTargetTime = SystemClock.nanoTime() / 1000000 + adjustedDuration; + long initialTargetTime = SystemClock.elapsedRealtime() + adjustedDuration; Timer timer = new Timer(executorToken, callbackID, initialTargetTime, duration, repeat); synchronized (mTimerGuard) { mTimers.add(timer); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java index bbe5fc99530861..a31179baaca7d5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java @@ -82,7 +82,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.START, ev, mTargetCoordinates[0], @@ -105,7 +105,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.END, ev, mTargetCoordinates[0], @@ -117,7 +117,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.MOVE, ev, mTargetCoordinates[0], @@ -128,7 +128,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.START, ev, mTargetCoordinates[0], @@ -139,7 +139,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.END, ev, mTargetCoordinates[0], @@ -180,7 +180,7 @@ private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher event Assertions.assertNotNull(eventDispatcher).dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.CANCEL, androidEvent, mTargetCoordinates[0], diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java index c2b5c8a6e9d8b9..19bf172aec52b4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java @@ -45,7 +45,7 @@ private OnLayoutEvent() { } protected void init(int viewTag, int x, int y, int width, int height) { - super.init(viewTag, SystemClock.nanoTime()); + super.init(viewTag, SystemClock.elapsedRealtime()); mX = x; mY = y; mWidth = width; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java index 0595d0fd07627c..8c78b2c346efba 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java @@ -188,25 +188,25 @@ public DrawerEventEmitter(DrawerLayout drawerLayout, EventDispatcher eventDispat @Override public void onDrawerSlide(View view, float v) { mEventDispatcher.dispatchEvent( - new DrawerSlideEvent(mDrawerLayout.getId(), SystemClock.nanoTime(), v)); + new DrawerSlideEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime(), v)); } @Override public void onDrawerOpened(View view) { mEventDispatcher.dispatchEvent( - new DrawerOpenedEvent(mDrawerLayout.getId(), SystemClock.nanoTime())); + new DrawerOpenedEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime())); } @Override public void onDrawerClosed(View view) { mEventDispatcher.dispatchEvent( - new DrawerClosedEvent(mDrawerLayout.getId(), SystemClock.nanoTime())); + new DrawerClosedEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime())); } @Override public void onDrawerStateChanged(int i) { mEventDispatcher.dispatchEvent( - new DrawerStateChangedEvent(mDrawerLayout.getId(), SystemClock.nanoTime(), i)); + new DrawerStateChangedEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime(), i)); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java index a864b140c81379..42e262e2ef3bc6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java @@ -192,7 +192,7 @@ public void setShouldNotifyLoadEvents(boolean shouldNotify) { @Override public void onSubmit(String id, Object callerContext) { mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD_START)); + new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD_START)); } @Override @@ -202,18 +202,18 @@ public void onFinalImageSet( @Nullable Animatable animatable) { if (imageInfo != null) { mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD)); + new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD)); mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD_END)); + new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD_END)); } } @Override public void onFailure(String id, Throwable throwable) { mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_ERROR)); + new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_ERROR)); mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD_END)); + new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD_END)); } }; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index 2fb42c594f6b27..c7b2635966e99d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -85,14 +85,14 @@ protected void addEventEmitters( new ReactModalHostView.OnRequestCloseListener() { @Override public void onRequestClose(DialogInterface dialog) { - dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.nanoTime())); + dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.elapsedRealtime())); } }); view.setOnShowListener( new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { - dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.nanoTime())); + dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.elapsedRealtime())); } }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java index 11ea4c839fc974..e0a348d529e728 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java @@ -157,7 +157,7 @@ public PickerEventEmitter(ReactPicker reactPicker, EventDispatcher eventDispatch @Override public void onItemSelected(int position) { mEventDispatcher.dispatchEvent( new PickerItemSelectEvent( - mReactPicker.getId(), SystemClock.nanoTime(), position)); + mReactPicker.getId(), SystemClock.elapsedRealtime(), position)); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java index df3f2c57542ab3..657a227f1d3dae 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java @@ -344,7 +344,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(ScrollEvent.obtain( getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), ScrollEventType.SCROLL, 0, /* offsetX = 0, horizontal scrolling only */ calculateAbsoluteOffset(), @@ -359,7 +359,7 @@ private void onTotalChildrenHeightChange(int newTotalChildrenHeight) { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(new ContentSizeChangeEvent( getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), getWidth(), newTotalChildrenHeight)); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index fd46f677b7954b..5443bc33fc21a8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -57,7 +57,7 @@ private static void emitScrollEvent(ViewGroup scrollView, ScrollEventType scroll reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( ScrollEvent.obtain( scrollView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), scrollEventType, scrollView.getScrollX(), scrollView.getScrollY(), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java index ec3f66d8073c02..a38bf2496e0512 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java @@ -81,7 +81,7 @@ public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSliderEvent( seekbar.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), ((ReactSlider)seekbar).toRealProgress(progress), fromUser)); } @@ -96,7 +96,7 @@ public void onStopTrackingTouch(SeekBar seekbar) { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSlidingCompleteEvent( seekbar.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), ((ReactSlider)seekbar).toRealProgress(seekbar.getProgress()))); } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index 1ae2bacf5a36b9..1ff9d0edb3d0b4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -89,7 +89,7 @@ protected void addEventEmitters( @Override public void onRefresh() { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher() - .dispatchEvent(new RefreshEvent(view.getId(), SystemClock.nanoTime())); + .dispatchEvent(new RefreshEvent(view.getId(), SystemClock.elapsedRealtime())); } }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java index 1db17fc222d11c..c3945d3f2f7f3e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java @@ -78,7 +78,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSwitchEvent( buttonView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), isChecked)); } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index ebb82644490040..3ca02b999be06e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -567,7 +567,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { mEventDispatcher.dispatchEvent( new ReactTextChangedEvent( mEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), s.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -576,7 +576,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { mEventDispatcher.dispatchEvent( new ReactTextInputEvent( mEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText, oldText, start, @@ -602,17 +602,17 @@ public void onFocusChange(View v, boolean hasFocus) { eventDispatcher.dispatchEvent( new ReactTextInputFocusEvent( editText.getId(), - SystemClock.nanoTime())); + SystemClock.elapsedRealtime())); } else { eventDispatcher.dispatchEvent( new ReactTextInputBlurEvent( editText.getId(), - SystemClock.nanoTime())); + SystemClock.elapsedRealtime())); eventDispatcher.dispatchEvent( new ReactTextInputEndEditingEvent( editText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), editText.getText().toString())); } } @@ -630,7 +630,7 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent keyEvent) { eventDispatcher.dispatchEvent( new ReactTextInputSubmitEditingEvent( editText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), editText.getText().toString())); } if (actionId == EditorInfo.IME_ACTION_NEXT || @@ -667,7 +667,7 @@ public void onSelectionChanged(int start, int end) { mEventDispatcher.dispatchEvent( new ReactTextInputSelectionEvent( mReactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), start, end ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java index 9f162f9f0b4145..eb8981870cce57 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java @@ -131,7 +131,7 @@ protected void addEventEmitters(final ThemedReactContext reactContext, final Rea @Override public void onClick(View v) { mEventDispatcher.dispatchEvent( - new ToolbarClickEvent(view.getId(), SystemClock.nanoTime(), -1)); + new ToolbarClickEvent(view.getId(), SystemClock.elapsedRealtime(), -1)); } }); @@ -142,7 +142,7 @@ public boolean onMenuItemClick(MenuItem menuItem) { mEventDispatcher.dispatchEvent( new ToolbarClickEvent( view.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), menuItem.getOrder())); return true; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java index 8b86beb76f6721..8ffb06faee6171 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java @@ -91,14 +91,14 @@ private class PageChangeListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mEventDispatcher.dispatchEvent( - new PageScrollEvent(getId(), SystemClock.nanoTime(), position, positionOffset)); + new PageScrollEvent(getId(), SystemClock.elapsedRealtime(), position, positionOffset)); } @Override public void onPageSelected(int position) { if (!mIsCurrentItemFromJs) { mEventDispatcher.dispatchEvent( - new PageSelectedEvent(getId(), SystemClock.nanoTime(), position)); + new PageSelectedEvent(getId(), SystemClock.elapsedRealtime(), position)); } } @@ -119,7 +119,7 @@ public void onPageScrollStateChanged(int state) { throw new IllegalStateException("Unsupported pageScrollState"); } mEventDispatcher.dispatchEvent( - new PageScrollStateChangedEvent(getId(), SystemClock.nanoTime(), pageScrollState)); + new PageScrollStateChangedEvent(getId(), SystemClock.elapsedRealtime(), pageScrollState)); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 11350d16e24639..6e88217e4d6a2a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -107,7 +107,7 @@ public void onPageStarted(WebView webView, String url, Bitmap favicon) { webView, new TopLoadingStartEvent( webView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), createWebViewEvent(webView, url))); } @@ -130,7 +130,7 @@ public void onReceivedError( dispatchEvent( webView, - new TopLoadingErrorEvent(webView.getId(), SystemClock.nanoTime(), eventData)); + new TopLoadingErrorEvent(webView.getId(), SystemClock.elapsedRealtime(), eventData)); } @Override @@ -141,7 +141,7 @@ public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload webView, new TopLoadingStartEvent( webView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), createWebViewEvent(webView, url))); } @@ -150,7 +150,7 @@ private void emitFinishEvent(WebView webView, String url) { webView, new TopLoadingFinishEvent( webView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), createWebViewEvent(webView, url))); } diff --git a/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java b/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java index 2eb7f5ed87d8ae..72074ebbef37b9 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java @@ -65,7 +65,7 @@ public class RootViewTest { @Before public void setUp() { - final long ts = SystemClock.nanoTime(); + final long ts = SystemClock.elapsedRealtime(); PowerMockito.mockStatic(Arguments.class); PowerMockito.when(Arguments.createArray()).thenAnswer(new Answer() { @Override @@ -80,7 +80,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } }); PowerMockito.mockStatic(SystemClock.class); - PowerMockito.when(SystemClock.nanoTime()).thenAnswer(new Answer() { + PowerMockito.when(SystemClock.elapsedRealtime()).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return ts; @@ -116,7 +116,7 @@ public void testTouchEmitter() { rootView.startReactApplication(instanceManager, ""); rootView.simulateAttachForTesting(); - long ts = SystemClock.nanoTime(); + long ts = SystemClock.elapsedRealtime(); // Test ACTION_DOWN event rootView.onTouchEvent( diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java index 7eebda7f1ac62b..7391974e47f6cc 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java @@ -74,7 +74,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { PowerMockito.mockStatic(SystemClock.class); when(SystemClock.currentTimeMillis()).thenReturn(mCurrentTimeNs / 1000000); - when(SystemClock.nanoTime()).thenReturn(mCurrentTimeNs); + when(SystemClock.elapsedRealtime()).thenReturn(mCurrentTimeNs / 1000000); mChoreographerMock = mock(ReactChoreographer.class); PowerMockito.mockStatic(ReactChoreographer.class); From 97299260b61a092f131af575571d836d8c1f3754 Mon Sep 17 00:00:00 2001 From: Alexey Lang Date: Thu, 7 Jul 2016 07:20:03 -0700 Subject: [PATCH 013/241] Refactor RCTPerformanceLogger to avoid having global state Reviewed By: javache Differential Revision: D3509004 fbshipit-source-id: c4ab12b3f1defa32c2b1c211e775f6782ede4b7f --- React/Base/RCTBatchedBridge.m | 55 ++++++----- React/Base/RCTBridge+Private.h | 5 + React/Base/RCTBridge.h | 6 ++ React/Base/RCTBridge.m | 34 ++++--- React/Base/RCTBridgeDelegate.h | 2 +- React/Base/RCTJavaScriptLoader.m | 16 ++- React/Base/RCTPerformanceLogger.h | 49 +++++++--- React/Base/RCTPerformanceLogger.m | 155 ++++++++++++------------------ React/Base/RCTRootView.m | 2 +- React/Executors/RCTJSCExecutor.mm | 36 ++++--- React/Executors/RCTJSCWrapper.mm | 5 - React/Profiler/RCTPerfMonitor.m | 5 +- 12 files changed, 200 insertions(+), 170 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 98d0615db4cb76..4ea36012a2237e 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -97,7 +97,7 @@ - (void)start dispatch_group_enter(initModulesAndLoadSource); __weak RCTBatchedBridge *weakSelf = self; __block NSData *sourceCode; - [self loadSource:^(NSError *error, NSData *source) { + [self loadSource:^(NSError *error, NSData *source, int64_t sourceLength) { if (error) { dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf stopLoadingWithError:error]; @@ -111,6 +111,7 @@ - (void)start // Synchronously initialize all native modules that cannot be loaded lazily [self initModulesWithDispatchGroup:initModulesAndLoadSource]; + RCTPerformanceLogger *performanceLogger = self->_performanceLogger; __block NSString *config; dispatch_group_enter(initModulesAndLoadSource); dispatch_async(bridgeQueue, ^{ @@ -118,17 +119,17 @@ - (void)start // Asynchronously initialize the JS executor dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{ - RCTPerformanceLoggerStart(RCTPLJSCExecutorSetup); + [performanceLogger markStartForTag:RCTPLJSCExecutorSetup]; [weakSelf setUpExecutor]; - RCTPerformanceLoggerEnd(RCTPLJSCExecutorSetup); + [performanceLogger markStopForTag:RCTPLJSCExecutorSetup]; }); // Asynchronously gather the module config dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{ if (weakSelf.valid) { - RCTPerformanceLoggerStart(RCTPLNativeModulePrepareConfig); + [performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig]; config = [weakSelf moduleConfig]; - RCTPerformanceLoggerEnd(RCTPLNativeModulePrepareConfig); + [performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig]; } }); @@ -136,9 +137,9 @@ - (void)start // We're not waiting for this to complete to leave dispatch group, since // injectJSONConfiguration and executeSourceCode will schedule operations // on the same queue anyway. - RCTPerformanceLoggerStart(RCTPLNativeModuleInjectConfig); + [performanceLogger markStartForTag:RCTPLNativeModuleInjectConfig]; [weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) { - RCTPerformanceLoggerEnd(RCTPLNativeModuleInjectConfig); + [performanceLogger markStopForTag:RCTPLNativeModuleInjectConfig]; if (error) { dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf stopLoadingWithError:error]; @@ -159,18 +160,19 @@ - (void)start - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad { - RCTPerformanceLoggerStart(RCTPLScriptDownload); + [_performanceLogger markStartForTag:RCTPLScriptDownload]; - RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSData *source) { - RCTPerformanceLoggerEnd(RCTPLScriptDownload); - - _onSourceLoad(error, source); + RCTPerformanceLogger *performanceLogger = _performanceLogger; + RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSData *source, int64_t sourceLength) { + [performanceLogger markStopForTag:RCTPLScriptDownload]; + [performanceLogger setValue:sourceLength forTag:RCTPLBundleSize]; + _onSourceLoad(error, source, sourceLength); }; if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) { [self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad]; } else if (self.bundleURL) { - [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:^(NSError *error, NSData *source) { + [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:^(NSError *error, NSData *source, int64_t sourceLength) { if (error && [self.delegate respondsToSelector:@selector(fallbackSourceURLForBridge:)]) { NSURL *fallbackURL = [self.delegate fallbackSourceURLForBridge:_parentBridge]; if (fallbackURL && ![fallbackURL isEqual:self.bundleURL]) { @@ -180,7 +182,7 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad return; } } - onSourceLoad(error, source); + onSourceLoad(error, source, sourceLength); }]; } else { // Allow testing without a script @@ -190,7 +192,7 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad postNotificationName:RCTJavaScriptDidLoadNotification object:_parentBridge userInfo:@{@"bridge": self}]; }); - onSourceLoad(nil, nil); + onSourceLoad(nil, nil, 0); } } @@ -235,7 +237,7 @@ - (NSArray *)configForModuleName:(NSString *)moduleName - (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup { - RCTPerformanceLoggerStart(RCTPLNativeModuleInit); + [_performanceLogger markStartForTag:RCTPLNativeModuleInit]; NSArray> *extraModules = nil; if (self.delegate) { @@ -382,7 +384,7 @@ - (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup _moduleSetupComplete = YES; // Set up modules that require main thread init or constants export - RCTPerformanceLoggerSet(RCTPLNativeModuleMainThread, 0); + [_performanceLogger setValue:0 forTag:RCTPLNativeModuleMainThread]; NSUInteger modulesOnMainQueueCount = 0; for (RCTModuleData *moduleData in _moduleDataByID) { __weak RCTBatchedBridge *weakSelf = self; @@ -393,19 +395,22 @@ - (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup // modules on the main thread in parallel with loading the JS code, so // they will already be available before they are ever required. dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), ^{ - if (weakSelf.valid) { - RCTPerformanceLoggerAppendStart(RCTPLNativeModuleMainThread); - (void)[moduleData instance]; - [moduleData gatherConstants]; - RCTPerformanceLoggerAppendEnd(RCTPLNativeModuleMainThread); + RCTBatchedBridge *strongSelf = weakSelf; + if (!strongSelf.valid) { + return; } + + [strongSelf->_performanceLogger appendStartForTag:RCTPLNativeModuleMainThread]; + (void)[moduleData instance]; + [moduleData gatherConstants]; + [strongSelf->_performanceLogger appendStopForTag:RCTPLNativeModuleMainThread]; }); modulesOnMainQueueCount++; } } - RCTPerformanceLoggerEnd(RCTPLNativeModuleInit); - RCTPerformanceLoggerSet(RCTPLNativeModuleMainThreadUsesCount, modulesOnMainQueueCount); + [_performanceLogger markStopForTag:RCTPLNativeModuleInit]; + [_performanceLogger setValue:modulesOnMainQueueCount forTag:RCTPLNativeModuleMainThreadUsesCount]; } - (void)setUpExecutor @@ -497,7 +502,7 @@ - (void)executeSourceCode:(NSData *)sourceCode - (void)didFinishLoading { - RCTPerformanceLoggerEnd(RCTPLBridgeStartup); + [_performanceLogger markStopForTag:RCTPLBridgeStartup]; _loading = NO; [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ for (dispatch_block_t call in _pendingCalls) { diff --git a/React/Base/RCTBridge+Private.h b/React/Base/RCTBridge+Private.h index fd0dd9a38ffd5b..08dd70557b03a4 100644 --- a/React/Base/RCTBridge+Private.h +++ b/React/Base/RCTBridge+Private.h @@ -10,8 +10,13 @@ #import "RCTBridge.h" @class RCTModuleData; +@class RCTPerformanceLogger; @interface RCTBridge () +{ +@public + RCTPerformanceLogger *_performanceLogger; +} // Used for the profiler flow events between JS and native @property (nonatomic, assign) int64_t flowID; diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 18cf73876ee54e..956244ca67c56d 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -18,6 +18,7 @@ @class RCTBridge; @class RCTEventDispatcher; +@class RCTPerformanceLogger; /** * This notification triggers a reload of all bridges currently running. @@ -160,6 +161,11 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); */ @property (nonatomic, readonly, getter=isValid) BOOL valid; +/** + * Link to the Performance Logger that logs React Native perf events. + */ +@property (nonatomic, readonly, strong) RCTPerformanceLogger *performanceLogger; + /** * Reload the bundle and reset executor & modules. Safe to call from any thread. */ diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 5721bc0f069c33..775ddff9b64062 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -110,26 +110,36 @@ + (void)setCurrentBridge:(RCTBridge *)currentBridge - (instancetype)initWithDelegate:(id)delegate launchOptions:(NSDictionary *)launchOptions { - if ((self = [super init])) { - RCTPerformanceLoggerStart(RCTPLBridgeStartup); - RCTPerformanceLoggerStart(RCTPLTTI); - - _delegate = delegate; - _launchOptions = [launchOptions copy]; - [self setUp]; - RCTExecuteOnMainQueue(^{ [self bindKeys]; }); - } - return self; + return [self initWithDelegate:delegate + bundleURL:nil + moduleProvider:nil + launchOptions:launchOptions]; } - (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleProvider:(RCTBridgeModuleProviderBlock)block launchOptions:(NSDictionary *)launchOptions +{ + return [self initWithDelegate:nil + bundleURL:bundleURL + moduleProvider:block + launchOptions:launchOptions]; +} + +/** + * Private designated initializer + */ +- (instancetype)initWithDelegate:(id)delegate + bundleURL:(NSURL *)bundleURL + moduleProvider:(RCTBridgeModuleProviderBlock)block + launchOptions:(NSDictionary *)launchOptions { if ((self = [super init])) { - RCTPerformanceLoggerStart(RCTPLBridgeStartup); - RCTPerformanceLoggerStart(RCTPLTTI); + _performanceLogger = [RCTPerformanceLogger new]; + [_performanceLogger markStartForTag:RCTPLBridgeStartup]; + [_performanceLogger markStartForTag:RCTPLTTI]; + _delegate = delegate; _bundleURL = bundleURL; _moduleProvider = block; _launchOptions = [launchOptions copy]; diff --git a/React/Base/RCTBridgeDelegate.h b/React/Base/RCTBridgeDelegate.h index 8d85f78b7a862b..baa3f5db1c8fb8 100644 --- a/React/Base/RCTBridgeDelegate.h +++ b/React/Base/RCTBridgeDelegate.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source); +typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source, int64_t sourceLength); @class RCTBridge; diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index f677db64e23517..8b6b0b750e4d1e 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -35,7 +35,7 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{ NSLocalizedDescriptionKey: errorDescription }]; - onComplete(error, nil); + onComplete(error, nil, 0); return; } @@ -51,14 +51,14 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp // modules into JSC as they're required. FILE *bundle = fopen(scriptURL.path.UTF8String, "r"); if (!bundle) { - onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), source); + onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), source, 0); return; } uint32_t magicNumber; if (fread(&magicNumber, sizeof(magicNumber), 1, bundle) != 1) { fclose(bundle); - onComplete(RCTErrorWithMessage(@"Error reading bundle"), source); + onComplete(RCTErrorWithMessage(@"Error reading bundle"), source, 0); return; } @@ -81,9 +81,8 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp sourceLength = source.length; } - RCTPerformanceLoggerSet(RCTPLBundleSize, sourceLength); fclose(bundle); - onComplete(error, source); + onComplete(error, source, sourceLength); }); return; } @@ -105,7 +104,7 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp code:error.code userInfo:userInfo]; } - onComplete(error, nil); + onComplete(error, nil, 0); return; } @@ -143,11 +142,10 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp code:((NSHTTPURLResponse *)response).statusCode userInfo:userInfo]; - onComplete(error, nil); + onComplete(error, nil, 0); return; } - RCTPerformanceLoggerSet(RCTPLBundleSize, data.length); - onComplete(nil, data); + onComplete(nil, data, data.length); }]; [task resume]; diff --git a/React/Base/RCTPerformanceLogger.h b/React/Base/RCTPerformanceLogger.h index 3cfd62b0f68947..9477a0ec679d83 100644 --- a/React/Base/RCTPerformanceLogger.h +++ b/React/Base/RCTPerformanceLogger.h @@ -9,8 +9,6 @@ #import -#import "RCTDefines.h" - typedef NS_ENUM(NSUInteger, RCTPLTag) { RCTPLScriptDownload = 0, RCTPLScriptExecution, @@ -25,7 +23,6 @@ typedef NS_ENUM(NSUInteger, RCTPLTag) { RCTPLNativeModuleInjectConfig, RCTPLNativeModuleMainThreadUsesCount, RCTPLJSCWrapperOpenLibrary, - RCTPLJSCWrapperLoadFunctions, RCTPLJSCExecutorSetup, RCTPLBridgeStartup, RCTPLTTI, @@ -33,44 +30,74 @@ typedef NS_ENUM(NSUInteger, RCTPLTag) { RCTPLSize }; +@interface RCTPerformanceLogger : NSObject + /** * Starts measuring a metric with the given tag. * Overrides previous value if the measurement has been already started. * If RCTProfile is enabled it also begins appropriate async event. + * All work is scheduled on the background queue so this doesn't block current thread. */ -RCT_EXTERN void RCTPerformanceLoggerStart(RCTPLTag tag); +- (void)markStartForTag:(RCTPLTag)tag; /** * Stops measuring a metric with given tag. * Checks if RCTPerformanceLoggerStart() has been called before * and doesn't do anything and log a message if it hasn't. * If RCTProfile is enabled it also ends appropriate async event. + * All work is scheduled on the background queue so this doesn't block current thread. */ -RCT_EXTERN void RCTPerformanceLoggerEnd(RCTPLTag tag); +- (void)markStopForTag:(RCTPLTag)tag; /** * Sets given value for a metric with given tag. + * All work is scheduled on the background queue so this doesn't block current thread. */ -RCT_EXTERN void RCTPerformanceLoggerSet(RCTPLTag tag, int64_t value); +- (void)setValue:(int64_t)value forTag:(RCTPLTag)tag; /** * Adds given value to the current value for a metric with given tag. + * All work is scheduled on the background queue so this doesn't block current thread. */ -RCT_EXTERN void RCTPerformanceLoggerAdd(RCTPLTag tag, int64_t value); +- (void)addValue:(int64_t)value forTag:(RCTPLTag)tag; /** * Starts an additional measurement for a metric with given tag. * It doesn't override previous measurement, instead it'll append a new value * to the old one. + * All work is scheduled on the background queue so this doesn't block current thread. */ -RCT_EXTERN void RCTPerformanceLoggerAppendStart(RCTPLTag tag); +- (void)appendStartForTag:(RCTPLTag)tag; /** * Stops measurement and appends the result to the metric with given tag. * Checks if RCTPerformanceLoggerAppendStart() has been called before * and doesn't do anything and log a message if it hasn't. + * All work is scheduled on the background queue so this doesn't block current thread. + */ +- (void)appendStopForTag:(RCTPLTag)tag; + +/** + * Returns an array with values for all tags. + * Use RCTPLTag to go over the array, there's a pair of values + * for each tag: start and stop (with indexes 2 * tag and 2 * tag + 1). + */ +- (NSArray *)valuesForTags; + +/** + * Returns a duration (stop_time - start_time) for given RCTPLTag. + */ +- (int64_t)durationForTag:(RCTPLTag)tag; + +/** + * Returns a value for given RCTPLTag. + */ +- (int64_t)valueForTag:(RCTPLTag)tag; + +/** + * Returns an array with values for all tags. + * Use RCTPLTag to go over the array. */ -RCT_EXTERN void RCTPerformanceLoggerAppendEnd(RCTPLTag tag); +- (NSArray *)labelsForTags; -RCT_EXTERN NSArray *RCTPerformanceLoggerOutput(void); -RCT_EXTERN NSArray *RCTPerformanceLoggerLabels(void); +@end diff --git a/React/Base/RCTPerformanceLogger.m b/React/Base/RCTPerformanceLogger.m index 68e38f434fbab1..5046990214ef87 100644 --- a/React/Base/RCTPerformanceLogger.m +++ b/React/Base/RCTPerformanceLogger.m @@ -14,77 +14,22 @@ #import "RCTLog.h" #import "RCTProfile.h" -static int64_t RCTPLData[RCTPLSize][2] = {}; -static NSUInteger RCTPLCookies[RCTPLSize] = {}; - -void RCTPerformanceLoggerStart(RCTPLTag tag) -{ - if (RCTProfileIsProfiling()) { - NSString *label = RCTPerformanceLoggerLabels()[tag]; - RCTPLCookies[tag] = RCTProfileBeginAsyncEvent(RCTProfileTagAlways, label, nil); - } - - RCTPLData[tag][0] = CACurrentMediaTime() * 1000; - RCTPLData[tag][1] = 0; -} - -void RCTPerformanceLoggerEnd(RCTPLTag tag) -{ - if (RCTPLData[tag][0] != 0 && RCTPLData[tag][1] == 0) { - RCTPLData[tag][1] = CACurrentMediaTime() * 1000; - - if (RCTProfileIsProfiling()) { - NSString *label = RCTPerformanceLoggerLabels()[tag]; - RCTProfileEndAsyncEvent(RCTProfileTagAlways, @"native", RCTPLCookies[tag], label, @"RCTPerformanceLogger", nil); - } - } else { - RCTLogInfo(@"Unbalanced calls start/end for tag %li", (unsigned long)tag); - } -} - -void RCTPerformanceLoggerSet(RCTPLTag tag, int64_t value) +@interface RCTPerformanceLogger () { - RCTPLData[tag][0] = 0; - RCTPLData[tag][1] = value; + int64_t _data[RCTPLSize][2]; + NSUInteger _cookies[RCTPLSize]; } -void RCTPerformanceLoggerAdd(RCTPLTag tag, int64_t value) -{ - RCTPLData[tag][0] = 0; - RCTPLData[tag][1] += value; -} +@property (nonatomic, copy) NSArray *labelsForTags; -void RCTPerformanceLoggerAppendStart(RCTPLTag tag) -{ - RCTPLData[tag][0] = CACurrentMediaTime() * 1000; -} - -void RCTPerformanceLoggerAppendEnd(RCTPLTag tag) -{ - if (RCTPLData[tag][0] != 0) { - RCTPLData[tag][1] += CACurrentMediaTime() * 1000 - RCTPLData[tag][0]; - RCTPLData[tag][0] = 0; - } else { - RCTLogInfo(@"Unbalanced calls start/end for tag %li", (unsigned long)tag); - } -} +@end -NSArray *RCTPerformanceLoggerOutput(void) -{ - NSMutableArray *result = [NSMutableArray array]; - for (NSUInteger index = 0; index < RCTPLSize; index++) { - [result addObject:@(RCTPLData[index][0])]; - [result addObject:@(RCTPLData[index][1])]; - } - return result; -} +@implementation RCTPerformanceLogger -NSArray *RCTPerformanceLoggerLabels(void) +- (instancetype)init { - static NSArray *labels; - static dispatch_once_t token; - dispatch_once(&token, ^{ - labels = @[ + if (self = [super init]) { + _labelsForTags = @[ @"ScriptDownload", @"ScriptExecution", @"RAMBundleLoad", @@ -98,56 +43,84 @@ void RCTPerformanceLoggerAppendEnd(RCTPLTag tag) @"NativeModuleInjectConfig", @"NativeModuleMainThreadUsesCount", @"JSCWrapperOpenLibrary", - @"JSCWrapperLoadFunctions", @"JSCExecutorSetup", @"BridgeStartup", @"RootViewTTI", @"BundleSize", ]; - }); - return labels; + } + return self; } -@interface RCTPerformanceLogger : NSObject - -@end +- (void)markStartForTag:(RCTPLTag)tag +{ + if (RCTProfileIsProfiling()) { + NSString *label = _labelsForTags[tag]; + _cookies[tag] = RCTProfileBeginAsyncEvent(RCTProfileTagAlways, label, nil); + } + _data[tag][0] = CACurrentMediaTime() * 1000; + _data[tag][1] = 0; +} -@implementation RCTPerformanceLogger -RCT_EXPORT_MODULE() +- (void)markStopForTag:(RCTPLTag)tag +{ + if (RCTProfileIsProfiling()) { + NSString *label =_labelsForTags[tag]; + RCTProfileEndAsyncEvent(RCTProfileTagAlways, @"native", _cookies[tag], label, @"RCTPerformanceLogger", nil); + } + if (_data[tag][0] != 0 && _data[tag][1] == 0) { + _data[tag][1] = CACurrentMediaTime() * 1000; + } else { + RCTLogInfo(@"Unbalanced calls start/end for tag %li", (unsigned long)tag); + } +} -@synthesize bridge = _bridge; +- (void)setValue:(int64_t)value forTag:(RCTPLTag)tag +{ + _data[tag][0] = 0; + _data[tag][1] = value; +} -- (instancetype)init +- (void)addValue:(int64_t)value forTag:(RCTPLTag)tag { - // We're only overriding this to ensure the module gets created at startup - // TODO (t11106126): Remove once we have more declarative control over module setup. - return [super init]; + _data[tag][0] = 0; + _data[tag][1] += value; } -- (void)setBridge:(RCTBridge *)bridge +- (void)appendStartForTag:(RCTPLTag)tag { - _bridge = bridge; + _data[tag][0] = CACurrentMediaTime() * 1000; +} - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(sendTimespans) - name:RCTContentDidAppearNotification - object:nil]; +- (void)appendStopForTag:(RCTPLTag)tag +{ + if (_data[tag][0] != 0) { + _data[tag][1] += CACurrentMediaTime() * 1000 - _data[tag][0]; + _data[tag][0] = 0; + } else { + RCTLogInfo(@"Unbalanced calls start/end for tag %li", (unsigned long)tag); + } } -- (void)dealloc +- (NSArray *)valuesForTags { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + NSMutableArray *result = [NSMutableArray array]; + for (NSUInteger index = 0; index < RCTPLSize; index++) { + [result addObject:@(_data[index][0])]; + [result addObject:@(_data[index][1])]; + } + return result; } -- (void)sendTimespans +- (int64_t)durationForTag:(RCTPLTag)tag { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + return _data[tag][1] - _data[tag][0]; +} - [_bridge enqueueJSCall:@"PerformanceLogger.addTimespans" args:@[ - RCTPerformanceLoggerOutput(), - RCTPerformanceLoggerLabels(), - ]]; +- (int64_t)valueForTag:(RCTPLTag)tag; +{ + return _data[tag][1]; } @end diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 78896f5fe482e1..d335aac834ce55 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -340,7 +340,7 @@ - (instancetype)initWithFrame:(CGRect)frame - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; - RCTPerformanceLoggerEnd(RCTPLTTI); + [_bridge->_performanceLogger markStopForTag:RCTPLTTI]; dispatch_async(dispatch_get_main_queue(), ^{ if (!_contentHasAppeared) { _contentHasAppeared = YES; diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 4da25b0303e499..506b5119b82cb4 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -136,6 +136,8 @@ @implementation RCTJSCExecutor RCTJSCWrapper *_jscWrapper; BOOL _useCustomJSCLibrary; + + RCTPerformanceLogger *_performanceLogger; } @synthesize valid = _valid; @@ -263,6 +265,12 @@ + (void)runRunLoopThread } } +- (void)setBridge:(RCTBridge *)bridge +{ + _bridge = bridge; + _performanceLogger = [bridge performanceLogger]; +} + - (instancetype)init { return [self initWithUseCustomJSCLibrary:NO]; @@ -328,7 +336,9 @@ - (void)setUp return; } + [strongSelf->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary]; strongSelf->_jscWrapper = RCTJSCWrapperCreate(strongSelf->_useCustomJSCLibrary); + [strongSelf->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; }]; @@ -660,14 +670,13 @@ - (void)executeApplicationScript:(NSData *)script } __weak RCTJSCExecutor *weakSelf = self; - [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf || !strongSelf.isValid) { return; } - RCTPerformanceLoggerStart(RCTPLScriptExecution); + [strongSelf->_performanceLogger markStartForTag:RCTPLScriptExecution]; JSValueRef jsError = NULL; RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; @@ -676,7 +685,8 @@ - (void)executeApplicationScript:(NSData *)script JSValueRef result = jscWrapper->JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, bundleURL, 0, &jsError); jscWrapper->JSStringRelease(bundleURL); jscWrapper->JSStringRelease(execJSString); - RCTPerformanceLoggerEnd(RCTPLScriptExecution); + + [strongSelf->_performanceLogger markStopForTag:RCTPLScriptExecution]; if (onComplete) { NSError *error; @@ -783,9 +793,9 @@ static void executeRandomAccessModule(RCTJSCExecutor *executor, uint32_t moduleI - (void)registerNativeRequire { - RCTPerformanceLoggerSet(RCTPLRAMNativeRequires, 0); - RCTPerformanceLoggerSet(RCTPLRAMNativeRequiresCount, 0); - RCTPerformanceLoggerSet(RCTPLRAMNativeRequiresSize, 0); + [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires]; + [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount]; + [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; __weak RCTJSCExecutor *weakSelf = self; [self addSynchronousHookWithName:@"nativeRequire" usingBlock:^(NSNumber *moduleID) { @@ -794,8 +804,8 @@ - (void)registerNativeRequire return; } - RCTPerformanceLoggerAdd(RCTPLRAMNativeRequiresCount, 1); - RCTPerformanceLoggerAppendStart(RCTPLRAMNativeRequires); + [strongSelf->_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; + [strongSelf->_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [@"nativeRequire_" stringByAppendingFormat:@"%@", moduleID], nil); @@ -810,12 +820,12 @@ - (void)registerNativeRequire return; } - RCTPerformanceLoggerAdd(RCTPLRAMNativeRequiresSize, size); + [strongSelf->_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; executeRandomAccessModule(strongSelf, ID, NSSwapLittleIntToHost(moduleData->offset), size); } RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); - RCTPerformanceLoggerAppendEnd(RCTPLRAMNativeRequires); + [strongSelf->_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; }]; } @@ -858,7 +868,7 @@ static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccess - (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error { - RCTPerformanceLoggerStart(RCTPLRAMBundleLoad); + [_performanceLogger markStartForTag:RCTPLRAMBundleLoad]; file_ptr bundle(fopen(sourceURL.path.UTF8String, "r"), fclose); if (!bundle) { if (error) { @@ -878,8 +888,8 @@ - (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error return nil; } - RCTPerformanceLoggerEnd(RCTPLRAMBundleLoad); - RCTPerformanceLoggerSet(RCTPLRAMStartupCodeSize, startupCode.size); + [_performanceLogger markStopForTag:RCTPLRAMBundleLoad]; + [_performanceLogger setValue:startupCode.size forTag:RCTPLRAMStartupCodeSize]; return [NSData dataWithBytesNoCopy:startupCode.code.release() length:startupCode.size freeWhenDone:YES]; } diff --git a/React/Executors/RCTJSCWrapper.mm b/React/Executors/RCTJSCWrapper.mm index eb7696f09c8f8b..222ebfc661b75e 100644 --- a/React/Executors/RCTJSCWrapper.mm +++ b/React/Executors/RCTJSCWrapper.mm @@ -13,7 +13,6 @@ #import #import "RCTLog.h" -#import "RCTPerformanceLogger.h" #include @@ -26,9 +25,7 @@ ofType:nil inDirectory:@"Frameworks/JavaScriptCore.framework"] UTF8String]; if (path) { - RCTPerformanceLoggerStart(RCTPLJSCWrapperOpenLibrary); handler = dlopen(path, RTLD_LAZY); - RCTPerformanceLoggerEnd(RCTPLJSCWrapperOpenLibrary); if (!handler) { RCTLogWarn(@"Can't load custom JSC library: %s", dlerror()); } @@ -69,7 +66,6 @@ static void RCTSetUpCustomLibraryPointers(RCTJSCWrapper *wrapper) return; } - RCTPerformanceLoggerStart(RCTPLJSCWrapperLoadFunctions); wrapper->JSValueToStringCopy = (JSValueToStringCopyFuncType)dlsym(libraryHandle, "JSValueToStringCopy"); wrapper->JSStringCreateWithCFString = (JSStringCreateWithCFStringFuncType)dlsym(libraryHandle, "JSStringCreateWithCFString"); wrapper->JSStringCopyCFString = (JSStringCopyCFStringFuncType)dlsym(libraryHandle, "JSStringCopyCFString"); @@ -90,7 +86,6 @@ static void RCTSetUpCustomLibraryPointers(RCTJSCWrapper *wrapper) wrapper->JSContext = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSContext"); wrapper->JSValue = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSValue"); wrapper->configureJSContextForIOS = (configureJSContextForIOSFuncType)dlsym(libraryHandle, "configureJSContextForIOS"); - RCTPerformanceLoggerEnd(RCTPLJSCWrapperLoadFunctions); } RCTJSCWrapper *RCTJSCWrapperCreate(BOOL useCustomJSC) diff --git a/React/Profiler/RCTPerfMonitor.m b/React/Profiler/RCTPerfMonitor.m index 947722534f211b..5c0ac165544095 100644 --- a/React/Profiler/RCTPerfMonitor.m +++ b/React/Profiler/RCTPerfMonitor.m @@ -510,8 +510,9 @@ - (void)loadPerformanceLoggerData { NSUInteger i = 0; NSMutableArray *data = [NSMutableArray new]; - NSArray *values = RCTPerformanceLoggerOutput(); - for (NSString *label in RCTPerformanceLoggerLabels()) { + RCTPerformanceLogger *performanceLogger = [_bridge performanceLogger]; + NSArray *values = [performanceLogger valuesForTags]; + for (NSString *label in [performanceLogger labelsForTags]) { long long value = values[i+1].longLongValue - values[i].longLongValue; NSString *unit = @"ms"; if ([label hasSuffix:@"Size"]) { From 6779d13dcba9f1b9aaf1c600b8f049dec2720ca2 Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Thu, 7 Jul 2016 08:07:16 -0700 Subject: [PATCH 014/241] Reverted commit D3528215 Summary: So `PanReponder.onPanResponderRelease/onPanResponderTerminate` receive a `gestureState` object containing a `onPanResponderTerminate.vx/vy` property. On Android and iOS, they appear to be orders of magnitude different, which appear to be due to the different scale of timestamps that are used when generating touch events. This pull request fixes the timestamps to be milliseconds on both platforms (since I assume iOS is the more authoritative one, and is the one that `react-native-viewpager`'s vx thresholds written written to compare against.) As far as I can tell, the RN code doesn't use the `vx/vy` properties, so they should be okay. And looks like the RN code only cares about relative values of `startTimestamp/currentTimestamp/previousTimestamp` though, so should be fine too. it's quite possible there will be downstream android breakage with this change, particularly for those who are already compensating for the RN discrepancy. Closes https://github.com/facebook/react-native/pull/8199 Differential Revision: D3528215 Pulled By: davidaurelio fbshipit-source-id: d81732e50a5ece2168e8347309d8d52a0db42951 --- .../facebook/react/tests/TextInputTestCase.java | 12 ++++++------ .../com/facebook/react/common/SystemClock.java | 4 ---- .../com/facebook/react/modules/core/Timing.java | 2 +- .../react/uimanager/JSTouchDispatcher.java | 12 ++++++------ .../facebook/react/uimanager/OnLayoutEvent.java | 2 +- .../views/drawer/ReactDrawerLayoutManager.java | 8 ++++---- .../facebook/react/views/image/ReactImageView.java | 10 +++++----- .../react/views/modal/ReactModalHostManager.java | 4 ++-- .../react/views/picker/ReactPickerManager.java | 2 +- .../recyclerview/RecyclerViewBackedScrollView.java | 4 ++-- .../react/views/scroll/ReactScrollViewHelper.java | 2 +- .../react/views/slider/ReactSliderManager.java | 4 ++-- .../swiperefresh/SwipeRefreshLayoutManager.java | 2 +- .../react/views/switchview/ReactSwitchManager.java | 2 +- .../views/textinput/ReactTextInputManager.java | 14 +++++++------- .../react/views/toolbar/ReactToolbarManager.java | 4 ++-- .../react/views/viewpager/ReactViewPager.java | 6 +++--- .../react/views/webview/ReactWebViewManager.java | 8 ++++---- .../test/java/com/facebook/react/RootViewTest.java | 6 +++--- .../react/modules/timing/TimingModuleTest.java | 2 +- 20 files changed, 53 insertions(+), 57 deletions(-) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java index 44c2321b2aadf3..9558a5d912af60 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java @@ -116,7 +116,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -125,7 +125,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), newText.toString(), "", start, @@ -150,7 +150,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -159,7 +159,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), moreText, "", start, @@ -184,7 +184,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -193,7 +193,7 @@ public void testMetionsInputColors() throws Throwable { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), moreText, "", start, diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java b/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java index 1af4b55368bfb9..29c31b416c5f75 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java @@ -22,8 +22,4 @@ public static long currentTimeMillis() { public static long nanoTime() { return System.nanoTime(); } - - public static long elapsedRealtime() { - return android.os.SystemClock.elapsedRealtime(); - } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java index 394d025fa87006..d0ee51cfcd191f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java @@ -244,7 +244,7 @@ public void createTimer( return; } - long initialTargetTime = SystemClock.elapsedRealtime() + adjustedDuration; + long initialTargetTime = SystemClock.nanoTime() / 1000000 + adjustedDuration; Timer timer = new Timer(executorToken, callbackID, initialTargetTime, duration, repeat); synchronized (mTimerGuard) { mTimers.add(timer); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java index a31179baaca7d5..bbe5fc99530861 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java @@ -82,7 +82,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), TouchEventType.START, ev, mTargetCoordinates[0], @@ -105,7 +105,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), TouchEventType.END, ev, mTargetCoordinates[0], @@ -117,7 +117,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), TouchEventType.MOVE, ev, mTargetCoordinates[0], @@ -128,7 +128,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), TouchEventType.START, ev, mTargetCoordinates[0], @@ -139,7 +139,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), TouchEventType.END, ev, mTargetCoordinates[0], @@ -180,7 +180,7 @@ private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher event Assertions.assertNotNull(eventDispatcher).dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), TouchEventType.CANCEL, androidEvent, mTargetCoordinates[0], diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java index 19bf172aec52b4..c2b5c8a6e9d8b9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java @@ -45,7 +45,7 @@ private OnLayoutEvent() { } protected void init(int viewTag, int x, int y, int width, int height) { - super.init(viewTag, SystemClock.elapsedRealtime()); + super.init(viewTag, SystemClock.nanoTime()); mX = x; mY = y; mWidth = width; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java index 8c78b2c346efba..0595d0fd07627c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java @@ -188,25 +188,25 @@ public DrawerEventEmitter(DrawerLayout drawerLayout, EventDispatcher eventDispat @Override public void onDrawerSlide(View view, float v) { mEventDispatcher.dispatchEvent( - new DrawerSlideEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime(), v)); + new DrawerSlideEvent(mDrawerLayout.getId(), SystemClock.nanoTime(), v)); } @Override public void onDrawerOpened(View view) { mEventDispatcher.dispatchEvent( - new DrawerOpenedEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime())); + new DrawerOpenedEvent(mDrawerLayout.getId(), SystemClock.nanoTime())); } @Override public void onDrawerClosed(View view) { mEventDispatcher.dispatchEvent( - new DrawerClosedEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime())); + new DrawerClosedEvent(mDrawerLayout.getId(), SystemClock.nanoTime())); } @Override public void onDrawerStateChanged(int i) { mEventDispatcher.dispatchEvent( - new DrawerStateChangedEvent(mDrawerLayout.getId(), SystemClock.elapsedRealtime(), i)); + new DrawerStateChangedEvent(mDrawerLayout.getId(), SystemClock.nanoTime(), i)); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java index 42e262e2ef3bc6..a864b140c81379 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java @@ -192,7 +192,7 @@ public void setShouldNotifyLoadEvents(boolean shouldNotify) { @Override public void onSubmit(String id, Object callerContext) { mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD_START)); + new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD_START)); } @Override @@ -202,18 +202,18 @@ public void onFinalImageSet( @Nullable Animatable animatable) { if (imageInfo != null) { mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD)); + new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD)); mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD_END)); + new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD_END)); } } @Override public void onFailure(String id, Throwable throwable) { mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_ERROR)); + new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_ERROR)); mEventDispatcher.dispatchEvent( - new ImageLoadEvent(getId(), SystemClock.elapsedRealtime(), ImageLoadEvent.ON_LOAD_END)); + new ImageLoadEvent(getId(), SystemClock.nanoTime(), ImageLoadEvent.ON_LOAD_END)); } }; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index c7b2635966e99d..2fb42c594f6b27 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -85,14 +85,14 @@ protected void addEventEmitters( new ReactModalHostView.OnRequestCloseListener() { @Override public void onRequestClose(DialogInterface dialog) { - dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.elapsedRealtime())); + dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.nanoTime())); } }); view.setOnShowListener( new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { - dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.elapsedRealtime())); + dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.nanoTime())); } }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java index e0a348d529e728..11ea4c839fc974 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java @@ -157,7 +157,7 @@ public PickerEventEmitter(ReactPicker reactPicker, EventDispatcher eventDispatch @Override public void onItemSelected(int position) { mEventDispatcher.dispatchEvent( new PickerItemSelectEvent( - mReactPicker.getId(), SystemClock.elapsedRealtime(), position)); + mReactPicker.getId(), SystemClock.nanoTime(), position)); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java index 657a227f1d3dae..df3f2c57542ab3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java @@ -344,7 +344,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(ScrollEvent.obtain( getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), ScrollEventType.SCROLL, 0, /* offsetX = 0, horizontal scrolling only */ calculateAbsoluteOffset(), @@ -359,7 +359,7 @@ private void onTotalChildrenHeightChange(int newTotalChildrenHeight) { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(new ContentSizeChangeEvent( getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), getWidth(), newTotalChildrenHeight)); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index 5443bc33fc21a8..fd46f677b7954b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -57,7 +57,7 @@ private static void emitScrollEvent(ViewGroup scrollView, ScrollEventType scroll reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( ScrollEvent.obtain( scrollView.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), scrollEventType, scrollView.getScrollX(), scrollView.getScrollY(), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java index a38bf2496e0512..ec3f66d8073c02 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java @@ -81,7 +81,7 @@ public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSliderEvent( seekbar.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), ((ReactSlider)seekbar).toRealProgress(progress), fromUser)); } @@ -96,7 +96,7 @@ public void onStopTrackingTouch(SeekBar seekbar) { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSlidingCompleteEvent( seekbar.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), ((ReactSlider)seekbar).toRealProgress(seekbar.getProgress()))); } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index 1ff9d0edb3d0b4..1ae2bacf5a36b9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -89,7 +89,7 @@ protected void addEventEmitters( @Override public void onRefresh() { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher() - .dispatchEvent(new RefreshEvent(view.getId(), SystemClock.elapsedRealtime())); + .dispatchEvent(new RefreshEvent(view.getId(), SystemClock.nanoTime())); } }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java index c3945d3f2f7f3e..1db17fc222d11c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java @@ -78,7 +78,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSwitchEvent( buttonView.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), isChecked)); } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index 3ca02b999be06e..ebb82644490040 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -567,7 +567,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { mEventDispatcher.dispatchEvent( new ReactTextChangedEvent( mEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), s.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -576,7 +576,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { mEventDispatcher.dispatchEvent( new ReactTextInputEvent( mEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), newText, oldText, start, @@ -602,17 +602,17 @@ public void onFocusChange(View v, boolean hasFocus) { eventDispatcher.dispatchEvent( new ReactTextInputFocusEvent( editText.getId(), - SystemClock.elapsedRealtime())); + SystemClock.nanoTime())); } else { eventDispatcher.dispatchEvent( new ReactTextInputBlurEvent( editText.getId(), - SystemClock.elapsedRealtime())); + SystemClock.nanoTime())); eventDispatcher.dispatchEvent( new ReactTextInputEndEditingEvent( editText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), editText.getText().toString())); } } @@ -630,7 +630,7 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent keyEvent) { eventDispatcher.dispatchEvent( new ReactTextInputSubmitEditingEvent( editText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), editText.getText().toString())); } if (actionId == EditorInfo.IME_ACTION_NEXT || @@ -667,7 +667,7 @@ public void onSelectionChanged(int start, int end) { mEventDispatcher.dispatchEvent( new ReactTextInputSelectionEvent( mReactEditText.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), start, end ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java index eb8981870cce57..9f162f9f0b4145 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java @@ -131,7 +131,7 @@ protected void addEventEmitters(final ThemedReactContext reactContext, final Rea @Override public void onClick(View v) { mEventDispatcher.dispatchEvent( - new ToolbarClickEvent(view.getId(), SystemClock.elapsedRealtime(), -1)); + new ToolbarClickEvent(view.getId(), SystemClock.nanoTime(), -1)); } }); @@ -142,7 +142,7 @@ public boolean onMenuItemClick(MenuItem menuItem) { mEventDispatcher.dispatchEvent( new ToolbarClickEvent( view.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), menuItem.getOrder())); return true; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java index 8ffb06faee6171..8b86beb76f6721 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java @@ -91,14 +91,14 @@ private class PageChangeListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mEventDispatcher.dispatchEvent( - new PageScrollEvent(getId(), SystemClock.elapsedRealtime(), position, positionOffset)); + new PageScrollEvent(getId(), SystemClock.nanoTime(), position, positionOffset)); } @Override public void onPageSelected(int position) { if (!mIsCurrentItemFromJs) { mEventDispatcher.dispatchEvent( - new PageSelectedEvent(getId(), SystemClock.elapsedRealtime(), position)); + new PageSelectedEvent(getId(), SystemClock.nanoTime(), position)); } } @@ -119,7 +119,7 @@ public void onPageScrollStateChanged(int state) { throw new IllegalStateException("Unsupported pageScrollState"); } mEventDispatcher.dispatchEvent( - new PageScrollStateChangedEvent(getId(), SystemClock.elapsedRealtime(), pageScrollState)); + new PageScrollStateChangedEvent(getId(), SystemClock.nanoTime(), pageScrollState)); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 6e88217e4d6a2a..11350d16e24639 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -107,7 +107,7 @@ public void onPageStarted(WebView webView, String url, Bitmap favicon) { webView, new TopLoadingStartEvent( webView.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), createWebViewEvent(webView, url))); } @@ -130,7 +130,7 @@ public void onReceivedError( dispatchEvent( webView, - new TopLoadingErrorEvent(webView.getId(), SystemClock.elapsedRealtime(), eventData)); + new TopLoadingErrorEvent(webView.getId(), SystemClock.nanoTime(), eventData)); } @Override @@ -141,7 +141,7 @@ public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload webView, new TopLoadingStartEvent( webView.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), createWebViewEvent(webView, url))); } @@ -150,7 +150,7 @@ private void emitFinishEvent(WebView webView, String url) { webView, new TopLoadingFinishEvent( webView.getId(), - SystemClock.elapsedRealtime(), + SystemClock.nanoTime(), createWebViewEvent(webView, url))); } diff --git a/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java b/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java index 72074ebbef37b9..2eb7f5ed87d8ae 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java @@ -65,7 +65,7 @@ public class RootViewTest { @Before public void setUp() { - final long ts = SystemClock.elapsedRealtime(); + final long ts = SystemClock.nanoTime(); PowerMockito.mockStatic(Arguments.class); PowerMockito.when(Arguments.createArray()).thenAnswer(new Answer() { @Override @@ -80,7 +80,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } }); PowerMockito.mockStatic(SystemClock.class); - PowerMockito.when(SystemClock.elapsedRealtime()).thenAnswer(new Answer() { + PowerMockito.when(SystemClock.nanoTime()).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return ts; @@ -116,7 +116,7 @@ public void testTouchEmitter() { rootView.startReactApplication(instanceManager, ""); rootView.simulateAttachForTesting(); - long ts = SystemClock.elapsedRealtime(); + long ts = SystemClock.nanoTime(); // Test ACTION_DOWN event rootView.onTouchEvent( diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java index 7391974e47f6cc..7eebda7f1ac62b 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java @@ -74,7 +74,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { PowerMockito.mockStatic(SystemClock.class); when(SystemClock.currentTimeMillis()).thenReturn(mCurrentTimeNs / 1000000); - when(SystemClock.elapsedRealtime()).thenReturn(mCurrentTimeNs / 1000000); + when(SystemClock.nanoTime()).thenReturn(mCurrentTimeNs); mChoreographerMock = mock(ReactChoreographer.class); PowerMockito.mockStatic(ReactChoreographer.class); From f11a783e641c3e78fb497e1d369a0511caf8d1d3 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Thu, 7 Jul 2016 08:29:57 -0700 Subject: [PATCH 015/241] Make packager reconnect less noisy Reviewed By: frantic Differential Revision: D3509012 fbshipit-source-id: 66742ebed80ecf48ce8291b1816ef0ec672febee --- .../bridge/JSPackagerWebSocketClient.java | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSPackagerWebSocketClient.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSPackagerWebSocketClient.java index e3379017f6cc83..04759d15589803 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSPackagerWebSocketClient.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSPackagerWebSocketClient.java @@ -11,10 +11,11 @@ import javax.annotation.Nullable; import java.io.IOException; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.TimeUnit; +import android.os.Handler; +import android.os.Looper; + import com.facebook.common.logging.FLog; import com.fasterxml.jackson.core.JsonFactory; @@ -33,9 +34,13 @@ * A wrapper around WebSocketClient that recognizes packager's message format. */ public class JSPackagerWebSocketClient implements WebSocketListener { - private static final String TAG = "JSPackagerWebSocketClient"; + + private static final int RECONNECT_DELAY_MS = 2000; + private final String mUrl; + private final Handler mHandler; + private boolean mSuppressConnectionErrors; public interface JSPackagerCallback { void onMessage(String target, String action); @@ -48,6 +53,7 @@ public JSPackagerWebSocketClient(String url, JSPackagerCallback callback) { super(); mUrl = url; mCallback = callback; + mHandler = new Handler(Looper.getMainLooper()); } public void connect() { @@ -63,12 +69,17 @@ public void connect() { } private void reconnect() { - new Timer().schedule(new TimerTask() { - @Override - public void run() { - connect(); - } - }, 2000); + if (!mSuppressConnectionErrors) { + FLog.w(TAG, "Couldn't connect to packager, will silently retry"); + mSuppressConnectionErrors = true; + } + mHandler.postDelayed( + new Runnable() { + @Override + public void run() { + connect(); + } + }, RECONNECT_DELAY_MS); } public void closeQuietly() { @@ -137,13 +148,16 @@ public void onMessage(ResponseBody response) throws IOException { @Override public void onFailure(IOException e, Response response) { - abort("Websocket exception", e); + if (mWebSocket != null) { + abort("Websocket exception", e); + } reconnect(); } @Override public void onOpen(WebSocket webSocket, Response response) { mWebSocket = webSocket; + mSuppressConnectionErrors = false; } @Override From af24e8001a0e23b41fe88f8c2e0e3183e0655f09 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 7 Jul 2016 08:37:27 -0700 Subject: [PATCH 016/241] Add new FileSourceProvider Reviewed By: michalgr Differential Revision: D3207541 fbshipit-source-id: d53599c3cf36ae7c89e85a29f637987bc7139159 --- .../react/XReactInstanceManagerImpl.java | 5 +- .../react/cxxbridge/CatalystInstanceImpl.java | 2 +- .../react/cxxbridge/JSBundleLoader.java | 9 +- .../jni/xreact/jni/CatalystInstanceImpl.cpp | 125 +++++++++++++++++- .../jni/xreact/jni/CatalystInstanceImpl.h | 2 +- .../src/main/jni/xreact/jni/OnLoad.cpp | 8 +- ReactAndroid/src/main/jni/xreact/jni/OnLoad.h | 1 + ReactCommon/cxxreact/Executor.h | 64 +++++++++ ReactCommon/cxxreact/JSCExecutor.cpp | 23 ++++ ReactCommon/cxxreact/JSCHelpers.cpp | 82 +++++++----- ReactCommon/cxxreact/JSCHelpers.h | 12 ++ 11 files changed, 287 insertions(+), 46 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java index e510d129cbb9c6..42911f885d11b5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java @@ -412,9 +412,12 @@ public void run() { } private void recreateReactContextInBackgroundFromBundleFile() { + boolean useLazyBundle = mJSCConfig.getConfigMap().hasKey("useLazyBundle") ? + mJSCConfig.getConfigMap().getBoolean("useLazyBundle") : false; + recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()), - JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile)); + JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile, useLazyBundle)); } /** 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 b23335e05cefa5..cfa7256ab88894 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -161,7 +161,7 @@ private native void initializeBridge(ReactCallback callback, MessageQueueThread moduleQueue, ModuleRegistryHolder registryHolder); - /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL); + /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean useLazyBundle); /* package */ native void loadScriptFromFile(String fileName, String sourceURL); @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java index c55b30ce692049..7b4d6e49976710 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java @@ -28,11 +28,18 @@ public abstract class JSBundleLoader { public static JSBundleLoader createFileLoader( final Context context, final String fileName) { + return createFileLoader(context, fileName, false); + } + + public static JSBundleLoader createFileLoader( + final Context context, + final String fileName, + final boolean useLazyBundle) { return new JSBundleLoader() { @Override public void loadScript(CatalystInstanceImpl instance) { if (fileName.startsWith("assets://")) { - instance.loadScriptFromAssets(context.getAssets(), fileName); + instance.loadScriptFromAssets(context.getAssets(), fileName, useLazyBundle); } else { instance.loadScriptFromFile(fileName, fileName); } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index 64fa80f98100f2..a0d1a2cfac9003 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include #include #include @@ -23,6 +25,7 @@ #include "ModuleRegistryHolder.h" #include "NativeArray.h" #include "JNativeRunnable.h" +#include "OnLoad.h" using namespace facebook::jni; @@ -98,7 +101,7 @@ void CatalystInstanceImpl::registerNatives() { makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge), makeNativeMethod("loadScriptFromAssets", - "(Landroid/content/res/AssetManager;Ljava/lang/String;)V", + "(Landroid/content/res/AssetManager;Ljava/lang/String;Z)V", CatalystInstanceImpl::loadScriptFromAssets), makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile), makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction), @@ -149,20 +152,132 @@ void CatalystInstanceImpl::initializeBridge( mrh->getModuleRegistry()); } +#ifdef WITH_FBJSCEXTENSIONS +static std::unique_ptr loadScriptFromCache( + AAssetManager* manager, + std::string& sourceURL) { + + // 20-byte sha1 as hex + static const size_t HASH_STR_SIZE = 40; + + // load bundle hash from the metadata file in the APK + auto hash = react::loadScriptFromAssets(manager, sourceURL + ".meta"); + auto cacheDir = getApplicationCacheDir() + "/rn-bundle"; + auto encoding = static_cast(hash->c_str()[20]); + + if (mkdir(cacheDir.c_str(), 0755) == -1 && errno != EEXIST) { + throw std::runtime_error("Can't create cache directory"); + } + + if (encoding != JSBigMmapString::Encoding::Ascii) { + throw std::runtime_error("Can't use mmap fastpath for non-ascii bundles"); + } + + // convert hash to string + char hashStr[HASH_STR_SIZE + 1]; + for (size_t i = 0; i < HASH_STR_SIZE; i += 2) { + snprintf(hashStr + i, 3, "%02hhx", hash->c_str()[i / 2] & 0xFF); + } + + // the name of the cached bundle file should be the hash + std::string cachePath = cacheDir + "/" + hashStr; + FILE *cache = fopen(cachePath.c_str(), "r"); + SCOPE_EXIT { if (cache) fclose(cache); }; + + size_t size = 0; + if (cache == NULL) { + // delete old bundle, if there was one. + std::string metaPath = cacheDir + "/meta"; + if (auto meta = fopen(metaPath.c_str(), "r")) { + char oldBundleHash[HASH_STR_SIZE + 1]; + if (fread(oldBundleHash, HASH_STR_SIZE, 1, meta) == HASH_STR_SIZE) { + remove((cacheDir + "/" + oldBundleHash).c_str()); + remove(metaPath.c_str()); + } + fclose(meta); + } + + // load script from the APK and write to temporary file + auto script = react::loadScriptFromAssets(manager, sourceURL); + auto tmpPath = cachePath + "_"; + cache = fopen(tmpPath.c_str(), "w"); + if (!cache) { + throw std::runtime_error("Can't open cache, errno: " + errno); + } + if (fwrite(script->c_str(), 1, script->size(), cache) != size) { + remove(tmpPath.c_str()); + throw std::runtime_error("Failed to unpack bundle"); + } + + // force data to be written to disk + fsync(fileno(cache)); + fclose(cache); + + // move script to final path - atomic operation + if (rename(tmpPath.c_str(), cachePath.c_str())) { + throw std::runtime_error("Failed to update cache, errno: " + errno); + } + + // store the bundle hash in a metadata file + auto meta = fopen(metaPath.c_str(), "w"); + if (!meta) { + throw std::runtime_error("Failed to open metadata file to store bundle hash"); + } + if (fwrite(hashStr, HASH_STR_SIZE, 1, meta) != HASH_STR_SIZE) { + throw std::runtime_error("Failed to write bundle hash to metadata file"); + } + fsync(fileno(meta)); + fclose(meta); + + // return the final written cache + cache = fopen(cachePath.c_str(), "r"); + if (!cache) { + throw std::runtime_error("Cache has been cleared"); + } + } else { + struct stat fileInfo = {0}; + if (fstat(fileno(cache), &fileInfo)) { + throw std::runtime_error("Failed to get cache stats, errno: " + errno); + } + size = fileInfo.st_size; + } + + return folly::make_unique( + dup(fileno(cache)), + size, + reinterpret_cast(hash->c_str()), + encoding); +} +#endif + void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager, - const std::string& assetURL) { + const std::string& assetURL, + bool useLazyBundle) { const int kAssetsLength = 9; // strlen("assets://"); auto sourceURL = assetURL.substr(kAssetsLength); - auto manager = react::extractAssetManager(assetManager); - auto script = react::loadScriptFromAssets(manager, sourceURL); + if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) { + auto script = react::loadScriptFromAssets(manager, sourceURL); instance_->loadUnbundle( folly::make_unique(manager, sourceURL), std::move(script), sourceURL); + return; } else { - instance_->loadScriptFromString(std::move(script), std::move(sourceURL)); +#ifdef WITH_FBJSCEXTENSIONS + if (useLazyBundle) { + try { + auto script = loadScriptFromCache(manager, sourceURL); + instance_->loadScriptFromString(std::move(script), sourceURL); + return; + } catch (...) { + LOG(WARNING) << "Failed to load bundle as Source Code"; + } + } +#endif + auto script = react::loadScriptFromAssets(manager, sourceURL); + instance_->loadScriptFromString(std::move(script), sourceURL); } } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index cddc4e03c12eb1..c132dbadc44af9 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -47,7 +47,7 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref jsQueue, jni::alias_ref moduleQueue, ModuleRegistryHolder* mrh); - void loadScriptFromAssets(jobject assetManager, const std::string& assetURL); + void loadScriptFromAssets(jobject assetManager, const std::string& assetURL, bool useLazyBundle); void loadScriptFromFile(jni::alias_ref fileName, const std::string& sourceURL); void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments, const std::string& tracingName); diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp index 952a6b46c4c4ff..67e167b4eaa9c8 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp @@ -51,10 +51,6 @@ static std::string getApplicationDir(const char* methodName) { return getAbsolutePathMethod(dirObj)->toStdString(); } -static std::string getApplicationCacheDir() { - return getApplicationDir("getCacheDir"); -} - static std::string getApplicationPersistentDir() { return getApplicationDir("getFilesDir"); } @@ -162,6 +158,10 @@ class JReactMarker : public JavaClass { } +std::string getApplicationCacheDir() { + return getApplicationDir("getCacheDir"); +} + extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return initialize(vm, [] { // Inject some behavior into react/ diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h index 5cd3c1749e62bf..713d8b582daf36 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h @@ -10,5 +10,6 @@ namespace facebook { namespace react { jmethodID getLogMarkerMethod(); +std::string getApplicationCacheDir(); } // namespace react } // namespace facebook diff --git a/ReactCommon/cxxreact/Executor.h b/ReactCommon/cxxreact/Executor.h index e2c5a331b6bf18..6b99df0b2cc1d0 100644 --- a/ReactCommon/cxxreact/Executor.h +++ b/ReactCommon/cxxreact/Executor.h @@ -7,6 +7,8 @@ #include #include +#include + #include #include "JSModulesUnbundle.h" @@ -134,6 +136,68 @@ class JSBigBufferString : public facebook::react::JSBigString { size_t m_size; }; +class JSBigMmapString : public JSBigString { +public: + enum class Encoding { + Unknown, + Ascii, + Utf8, + Utf16, + }; + + + JSBigMmapString(int fd, size_t size, const uint8_t sha1[20], Encoding encoding) : + m_fd(fd), + m_size(size), + m_encoding(encoding), + m_str(nullptr) + { + memcpy(m_hash, sha1, 20); + } + + ~JSBigMmapString() { + if (m_str) { + CHECK(munmap((void *)m_str, m_size) != -1); + } + close(m_fd); + } + + bool isAscii() const override { + return m_encoding == Encoding::Ascii; + } + + const char* c_str() const override { + if (!m_str) { + m_str = (const char *)mmap(0, m_size, PROT_READ, MAP_SHARED, m_fd, 0); + CHECK(m_str != MAP_FAILED); + } + return m_str; + } + + size_t size() const override { + return m_size; + } + + int fd() const { + return m_fd; + } + + const uint8_t* hash() const { + return m_hash; + } + + Encoding encoding() const { + return m_encoding; + } + +private: + int m_fd; + size_t m_size; + uint8_t m_hash[20]; + Encoding m_encoding; + mutable const char *m_str; +}; + class JSExecutor { public: /** diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index 0b624da12ee5cb..d26409c2e0bcc7 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -253,10 +253,33 @@ void JSCExecutor::terminateOnJSVMThread() { m_context = nullptr; } +#ifdef WITH_FBJSCEXTENSIONS +static void loadApplicationSource( + const JSGlobalContextRef context, + const JSBigMmapString* script, + const std::string& sourceURL) { + String jsSourceURL(sourceURL.c_str()); + bool is8bit = script->encoding() == JSBigMmapString::Encoding::Ascii || script->encoding() == JSBigMmapString::Encoding::Utf8; + JSSourceCodeRef sourceCode = JSCreateSourceCode(script->fd(), script->size(), jsSourceURL, script->hash(), is8bit); + evaluateSourceCode(context, sourceCode, jsSourceURL); + JSReleaseSourceCode(sourceCode); +} +#endif + void JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL) throw(JSException) { SystraceSection s("JSCExecutor::loadApplicationScript", "sourceURL", sourceURL); + #ifdef WITH_FBJSCEXTENSIONS + if (auto source = dynamic_cast(script.get())) { + loadApplicationSource(m_context, source, sourceURL); + bindBridge(); + flush(); + ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); + return; + } + #endif + #ifdef WITH_FBSYSTRACE fbsystrace_begin_section( TRACE_TAG_REACT_CXX_BRIDGE, diff --git a/ReactCommon/cxxreact/JSCHelpers.cpp b/ReactCommon/cxxreact/JSCHelpers.cpp index 40554219ddf4ae..194cc473791eac 100644 --- a/ReactCommon/cxxreact/JSCHelpers.cpp +++ b/ReactCommon/cxxreact/JSCHelpers.cpp @@ -44,47 +44,63 @@ JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef JSValueRef exn, result; result = JSEvaluateScript(context, script, NULL, source, 0, &exn); if (result == nullptr) { - Value exception = Value(context, exn); - - std::string exceptionText = exception.toString().str(); - - // The null/empty-ness of source tells us if the JS came from a - // file/resource, or was a constructed statement. The location - // info will include that source, if any. - std::string locationInfo = source != nullptr ? String::ref(source).str() : ""; - Object exObject = exception.asObject(); - auto line = exObject.getProperty("line"); - if (line != nullptr && line.isNumber()) { - if (locationInfo.empty() && line.asInteger() != 1) { - // If there is a non-trivial line number, but there was no - // location info, we include a placeholder, and the line - // number. - locationInfo = folly::to(":", line.asInteger()); - } else if (!locationInfo.empty()) { - // If there is location info, we always include the line - // number, regardless of its value. - locationInfo += folly::to(":", line.asInteger()); - } - } + formatAndThrowJSException(context, exn, source); + } + return result; +} - if (!locationInfo.empty()) { - exceptionText += " (" + locationInfo + ")"; +#if WITH_FBJSCEXTENSIONS +JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) { + JSValueRef exn, result; + result = JSEvaluateSourceCode(context, source, NULL, &exn); + if (result == nullptr) { + formatAndThrowJSException(context, exn, sourceURL); + } + return result; +} +#endif + +void formatAndThrowJSException(JSContextRef context, JSValueRef exn, JSStringRef source) { + Value exception = Value(context, exn); + + std::string exceptionText = exception.toString().str(); + + // The null/empty-ness of source tells us if the JS came from a + // file/resource, or was a constructed statement. The location + // info will include that source, if any. + std::string locationInfo = source != nullptr ? String::ref(source).str() : ""; + Object exObject = exception.asObject(); + auto line = exObject.getProperty("line"); + if (line != nullptr && line.isNumber()) { + if (locationInfo.empty() && line.asInteger() != 1) { + // If there is a non-trivial line number, but there was no + // location info, we include a placeholder, and the line + // number. + locationInfo = folly::to(":", line.asInteger()); + } else if (!locationInfo.empty()) { + // If there is location info, we always include the line + // number, regardless of its value. + locationInfo += folly::to(":", line.asInteger()); } + } + + if (!locationInfo.empty()) { + exceptionText += " (" + locationInfo + ")"; + } - LOG(ERROR) << "Got JS Exception: " << exceptionText; + LOG(ERROR) << "Got JS Exception: " << exceptionText; - Value jsStack = exObject.getProperty("stack"); - if (jsStack.isNull() || !jsStack.isString()) { - throwJSExecutionException("%s", exceptionText.c_str()); - } else { - LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str(); - throwJSExecutionExceptionWithStack( + Value jsStack = exObject.getProperty("stack"); + if (jsStack.isNull() || !jsStack.isString()) { + throwJSExecutionException("%s", exceptionText.c_str()); + } else { + LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str(); + throwJSExecutionExceptionWithStack( exceptionText.c_str(), jsStack.toString().str().c_str()); - } } - return result; } + JSValueRef makeJSError(JSContextRef ctx, const char *error) { JSValueRef nestedException = nullptr; JSValueRef args[] = { Value(ctx, String(error)) }; diff --git a/ReactCommon/cxxreact/JSCHelpers.h b/ReactCommon/cxxreact/JSCHelpers.h index 3be708164ae028..a55906d560c2d8 100644 --- a/ReactCommon/cxxreact/JSCHelpers.h +++ b/ReactCommon/cxxreact/JSCHelpers.h @@ -49,6 +49,18 @@ JSValueRef evaluateScript( JSStringRef script, JSStringRef sourceURL); +#if WITH_FBJSCEXTENSIONS +JSValueRef evaluateSourceCode( + JSContextRef ctx, + JSSourceCodeRef source, + JSStringRef sourceURL); +#endif + +void formatAndThrowJSException( + JSContextRef ctx, + JSValueRef exn, + JSStringRef sourceURL); + JSValueRef makeJSError(JSContextRef ctx, const char *error); JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation); From 95401aba7250159b22c7ba952a56a7063c1f10a7 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Thu, 7 Jul 2016 08:43:56 -0700 Subject: [PATCH 017/241] Create tracing name in C++ instead of Java Reviewed By: mhorowitz Differential Revision: D3469140 fbshipit-source-id: 77a00a7150573e44f219972556cbb936a57d7054 --- .../com/facebook/react/bridge/CatalystInstance.java | 3 +-- .../facebook/react/bridge/CatalystInstanceImpl.java | 12 ++++++------ .../react/bridge/JavaScriptModuleRegistration.java | 13 ------------- .../react/bridge/JavaScriptModuleRegistry.java | 5 ++--- .../java/com/facebook/react/bridge/ReactBridge.java | 2 +- .../react/cxxbridge/CatalystInstanceImpl.java | 8 +++----- ReactAndroid/src/main/jni/react/Bridge.cpp | 9 +++++---- ReactAndroid/src/main/jni/react/Bridge.h | 3 +-- ReactAndroid/src/main/jni/react/jni/OnLoad.cpp | 5 ++--- .../main/jni/xreact/jni/CatalystInstanceImpl.cpp | 6 ++---- .../src/main/jni/xreact/jni/CatalystInstanceImpl.h | 3 +-- ReactCommon/cxxreact/Instance.cpp | 5 ++--- ReactCommon/cxxreact/Instance.h | 2 +- ReactCommon/cxxreact/NativeToJsBridge.cpp | 10 +++++++--- ReactCommon/cxxreact/NativeToJsBridge.h | 3 +-- 15 files changed, 35 insertions(+), 54 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java index 2987512efc1953..46235d2230b209 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -32,8 +32,7 @@ void callFunction( ExecutorToken executorToken, String module, String method, - NativeArray arguments, - String tracingName); + NativeArray arguments); /** * Destroys this catalyst instance, waiting for any other threads in ReactQueueConfiguration * (besides the UI thread) to finish running. Must be called from the UI thread so that we can diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java index 8def48cb504973..7e4683a3765095 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java @@ -164,8 +164,7 @@ public void callFunction( ExecutorToken executorToken, String module, String method, - NativeArray arguments, - String tracingName) { + NativeArray arguments) { if (mIsBeingDestroyed) { FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed."); return; @@ -178,10 +177,11 @@ public void callFunction( incrementPendingJSCalls(); - Assertions.assertNotNull(mBridge).callFunction(executorToken, - module, - method, arguments, tracingName); - } + Assertions.assertNotNull(mBridge).callFunction( + executorToken, + module, + method, arguments); + } } // This is called from java code, so it won't be stripped anyway, but proguard will rename it, diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistration.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistration.java index 42b518e472218d..afa32d964b7827 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistration.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistration.java @@ -13,10 +13,8 @@ import java.lang.reflect.Method; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; import com.facebook.react.common.build.ReactBuildConfig; @@ -28,11 +26,9 @@ public class JavaScriptModuleRegistration { private final Class mModuleInterface; - private final Map mMethodsToTracingNames; public JavaScriptModuleRegistration(Class moduleInterface) { mModuleInterface = moduleInterface; - mMethodsToTracingNames = new HashMap<>(); if (ReactBuildConfig.DEBUG) { Set methodNames = new LinkedHashSet<>(); @@ -46,15 +42,6 @@ public JavaScriptModuleRegistration(Class moduleInte } } - public String getTracingName(Method method) { - String name = mMethodsToTracingNames.get(method); - if (name == null) { - name = "JSCall__" + getName() + "_" + method.getName(); - mMethodsToTracingNames.put(method, name); - } - return name; - } - public Class getModuleInterface() { return mModuleInterface; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistry.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistry.java index 466afcccf58778..e14bc332fc462f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistry.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistry.java @@ -106,14 +106,13 @@ public JavaScriptModuleInvocationHandler( FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away..."); return null; } - String tracingName = mModuleRegistration.getTracingName(method); NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray(); mCatalystInstance.callFunction( executorToken, mModuleRegistration.getName(), method.getName(), - jsArgs, - tracingName); + jsArgs + ); return null; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java index 2e69e78f94075b..d966ce2ff51111 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java @@ -88,7 +88,7 @@ private native void initialize( */ public native void loadScriptFromAssets(AssetManager assetManager, String assetName); public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL); - public native void callFunction(ExecutorToken executorToken, String module, String method, NativeArray arguments, String tracingName); + public native void callFunction(ExecutorToken executorToken, String module, String method, NativeArray arguments); public native void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments); public native void setGlobalVariable(String propertyName, String jsonEncodedArgument); public native boolean supportsProfiling(); 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 cfa7256ab88894..569ae980ed3476 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -182,16 +182,14 @@ private native void callJSFunction( ExecutorToken token, String module, String method, - NativeArray arguments, - String tracingName); + NativeArray arguments); @Override public void callFunction( ExecutorToken executorToken, final String module, final String method, - final NativeArray arguments, - final String tracingName) { + final NativeArray arguments) { if (mDestroyed) { FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed."); return; @@ -200,7 +198,7 @@ public void callFunction( throw new RuntimeException("Attempt to call JS function before JS bundle is loaded."); } - callJSFunction(executorToken, module, method, arguments, tracingName); + callJSFunction(executorToken, module, method, arguments); } private native void callJSCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments); diff --git a/ReactAndroid/src/main/jni/react/Bridge.cpp b/ReactAndroid/src/main/jni/react/Bridge.cpp index ee5bcb16cc44f3..8940b86fd0e79e 100644 --- a/ReactAndroid/src/main/jni/react/Bridge.cpp +++ b/ReactAndroid/src/main/jni/react/Bridge.cpp @@ -49,10 +49,11 @@ void Bridge::callFunction( ExecutorToken executorToken, const std::string& moduleId, const std::string& methodId, - const folly::dynamic& arguments, - const std::string& tracingName) { + const folly::dynamic& arguments) { #ifdef WITH_FBSYSTRACE int systraceCookie = m_systraceCookie++; + std::string tracingName = fbsystrace_is_tracing(TRACE_TAG_REACT_CXX_BRIDGE) ? + folly::to("JSCall__", moduleId, '_', methodId) : std::string(); FbSystraceAsyncFlow::begin( TRACE_TAG_REACT_CXX_BRIDGE, tracingName.c_str(), @@ -60,14 +61,14 @@ void Bridge::callFunction( #endif #ifdef WITH_FBSYSTRACE - runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName, systraceCookie] (JSExecutor* executor) { + runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) { FbSystraceAsyncFlow::end( TRACE_TAG_REACT_CXX_BRIDGE, tracingName.c_str(), systraceCookie); FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, tracingName.c_str()); #else - runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName] (JSExecutor* executor) { + runOnExecutorQueue(executorToken, [moduleId, methodId, arguments] (JSExecutor* executor) { #endif // This is safe because we are running on the executor's thread: it won't // destruct until after it's been unregistered (which we check above) and diff --git a/ReactAndroid/src/main/jni/react/Bridge.h b/ReactAndroid/src/main/jni/react/Bridge.h index b2c004a5855bb5..3920fdd7db01a4 100644 --- a/ReactAndroid/src/main/jni/react/Bridge.h +++ b/ReactAndroid/src/main/jni/react/Bridge.h @@ -68,8 +68,7 @@ class Bridge { ExecutorToken executorToken, const std::string& module, const std::string& method, - const folly::dynamic& args, - const std::string& tracingName); + const folly::dynamic& args); /** * Invokes a callback with the cbID, and optional additional arguments in JS. diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index 63fee95574aaca..2f2f92cec3845c 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -293,7 +293,7 @@ static void loadScriptFromFile(JNIEnv* env, jobject obj, jstring fileName, jstri } static void callFunction(JNIEnv* env, jobject obj, JExecutorToken::jhybridobject jExecutorToken, jstring module, jstring method, - NativeArray::jhybridobject args, jstring tracingName) { + NativeArray::jhybridobject args) { auto bridge = extractRefPtr(env, obj); auto arguments = cthis(wrap_alias(args)); try { @@ -301,8 +301,7 @@ static void callFunction(JNIEnv* env, jobject obj, JExecutorToken::jhybridobject cthis(wrap_alias(jExecutorToken))->getExecutorToken(wrap_alias(jExecutorToken)), fromJString(env, module), fromJString(env, method), - std::move(arguments->array), - fromJString(env, tracingName) + std::move(arguments->array) ); } catch (...) { translatePendingCppExceptionToJavaException(); diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index a0d1a2cfac9003..3ac86d9e2416a9 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -288,8 +288,7 @@ void CatalystInstanceImpl::loadScriptFromFile(jni::alias_ref fileName, } void CatalystInstanceImpl::callJSFunction( - JExecutorToken* token, std::string module, std::string method, NativeArray* arguments, - const std::string& tracingName) { + JExecutorToken* token, std::string module, std::string method, NativeArray* arguments) { // We want to share the C++ code, and on iOS, modules pass module/method // names as strings all the way through to JS, and there's no way to do // string -> id mapping on the objc side. So on Android, we convert the @@ -300,8 +299,7 @@ void CatalystInstanceImpl::callJSFunction( instance_->callJSFunction(token->getExecutorToken(nullptr), module, method, - std::move(arguments->array), - tracingName); + std::move(arguments->array)); } void CatalystInstanceImpl::callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments) { diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index c132dbadc44af9..64835e6b461d1c 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -49,8 +49,7 @@ class CatalystInstanceImpl : public jni::HybridClass { ModuleRegistryHolder* mrh); void loadScriptFromAssets(jobject assetManager, const std::string& assetURL, bool useLazyBundle); void loadScriptFromFile(jni::alias_ref fileName, const std::string& sourceURL); - void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments, - const std::string& tracingName); + void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments); void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments); local_ref getMainExecutorToken(); void setGlobalVariable(std::string propName, diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index a179fa14f72432..2168bad0d4d63e 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -102,10 +102,9 @@ void Instance::setGlobalVariable(std::string propName, } void Instance::callJSFunction(ExecutorToken token, const std::string& module, const std::string& method, - folly::dynamic&& params, const std::string& tracingName) { - SystraceSection s(tracingName.c_str()); + folly::dynamic&& params) { callback_->incrementPendingJSCalls(); - nativeToJsBridge_->callFunction(token, module, method, std::move(params), tracingName); + nativeToJsBridge_->callFunction(token, module, method, std::move(params)); } void Instance::callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params) { diff --git a/ReactCommon/cxxreact/Instance.h b/ReactCommon/cxxreact/Instance.h index d5a3507eb80bc2..af2f1bda9937e0 100644 --- a/ReactCommon/cxxreact/Instance.h +++ b/ReactCommon/cxxreact/Instance.h @@ -45,7 +45,7 @@ class Instance { void stopProfiler(const std::string& title, const std::string& filename); void setGlobalVariable(std::string propName, std::unique_ptr jsonValue); void callJSFunction(ExecutorToken token, const std::string& module, const std::string& method, - folly::dynamic&& params, const std::string& tracingName); + folly::dynamic&& params); void callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params); MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args); diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 4a935cf5b25dca..317cc87124c742 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -145,18 +145,22 @@ void NativeToJsBridge::callFunction( ExecutorToken executorToken, const std::string& moduleId, const std::string& methodId, - const folly::dynamic& arguments, - const std::string& tracingName) { + const folly::dynamic& arguments) { int systraceCookie = -1; #ifdef WITH_FBSYSTRACE systraceCookie = m_systraceCookie++; + std::string tracingName = fbsystrace_is_tracing(TRACE_TAG_REACT_CXX_BRIDGE) ? + folly::to("JSCall__", moduleId, '_', methodId) : std::string(); + SystraceSection s(tracingName.c_str()); FbSystraceAsyncFlow::begin( TRACE_TAG_REACT_CXX_BRIDGE, tracingName.c_str(), systraceCookie); + #else + std::string tracingName; #endif - runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName, systraceCookie] (JSExecutor* executor) { + runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) { #ifdef WITH_FBSYSTRACE FbSystraceAsyncFlow::end( TRACE_TAG_REACT_CXX_BRIDGE, diff --git a/ReactCommon/cxxreact/NativeToJsBridge.h b/ReactCommon/cxxreact/NativeToJsBridge.h index 942ee17c93607e..fea5a86ee18553 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.h +++ b/ReactCommon/cxxreact/NativeToJsBridge.h @@ -70,8 +70,7 @@ class NativeToJsBridge { ExecutorToken executorToken, const std::string& moduleId, const std::string& methodId, - const folly::dynamic& args, - const std::string& tracingName); + const folly::dynamic& args); /** * Invokes a callback with the cbID, and optional additional arguments in JS. From be0abd17e5c37bd7dcc1728c41d5bcfb64f3a4b2 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Thu, 7 Jul 2016 08:44:01 -0700 Subject: [PATCH 018/241] Remove a bunch of copies Reviewed By: astreet Differential Revision: D3475592 fbshipit-source-id: 37148bb8d8d47e9301ad549b183029337f7c4ca0 --- .../jni/xreact/jni/CatalystInstanceImpl.cpp | 4 ++-- ReactCommon/cxxreact/Instance.cpp | 4 ++-- ReactCommon/cxxreact/Instance.h | 2 +- ReactCommon/cxxreact/NativeToJsBridge.cpp | 17 ++++++++--------- ReactCommon/cxxreact/NativeToJsBridge.h | 8 ++++---- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index 3ac86d9e2416a9..612107b7cc684c 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -297,8 +297,8 @@ void CatalystInstanceImpl::callJSFunction( // strings otherwise. Eventually, we'll probably want to modify the stack // from the JS proxy through here to use strings, too. instance_->callJSFunction(token->getExecutorToken(nullptr), - module, - method, + std::move(module), + std::move(method), std::move(arguments->array)); } diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index 2168bad0d4d63e..cbc8e656bc39fe 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -101,10 +101,10 @@ void Instance::setGlobalVariable(std::string propName, nativeToJsBridge_->setGlobalVariable(std::move(propName), std::move(jsonValue)); } -void Instance::callJSFunction(ExecutorToken token, const std::string& module, const std::string& method, +void Instance::callJSFunction(ExecutorToken token, std::string&& module, std::string&& method, folly::dynamic&& params) { callback_->incrementPendingJSCalls(); - nativeToJsBridge_->callFunction(token, module, method, std::move(params)); + nativeToJsBridge_->callFunction(token, std::move(module), std::move(method), std::move(params)); } void Instance::callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params) { diff --git a/ReactCommon/cxxreact/Instance.h b/ReactCommon/cxxreact/Instance.h index af2f1bda9937e0..fada0a7cfba7af 100644 --- a/ReactCommon/cxxreact/Instance.h +++ b/ReactCommon/cxxreact/Instance.h @@ -44,7 +44,7 @@ class Instance { void startProfiler(const std::string& title); void stopProfiler(const std::string& title, const std::string& filename); void setGlobalVariable(std::string propName, std::unique_ptr jsonValue); - void callJSFunction(ExecutorToken token, const std::string& module, const std::string& method, + void callJSFunction(ExecutorToken token, std::string&& module, std::string&& method, folly::dynamic&& params); void callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params); MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 317cc87124c742..8da6bb0fce6890 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -143,14 +143,14 @@ void NativeToJsBridge::loadApplicationUnbundle( void NativeToJsBridge::callFunction( ExecutorToken executorToken, - const std::string& moduleId, - const std::string& methodId, - const folly::dynamic& arguments) { + std::string&& module, + std::string&& method, + folly::dynamic&& arguments) { int systraceCookie = -1; #ifdef WITH_FBSYSTRACE systraceCookie = m_systraceCookie++; std::string tracingName = fbsystrace_is_tracing(TRACE_TAG_REACT_CXX_BRIDGE) ? - folly::to("JSCall__", moduleId, '_', methodId) : std::string(); + folly::to("JSCall__", module, '_', method) : std::string(); SystraceSection s(tracingName.c_str()); FbSystraceAsyncFlow::begin( TRACE_TAG_REACT_CXX_BRIDGE, @@ -160,7 +160,7 @@ void NativeToJsBridge::callFunction( std::string tracingName; #endif - runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) { + runOnExecutorQueue(executorToken, [module = std::move(module), method = std::move(method), arguments = std::move(arguments), tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) { #ifdef WITH_FBSYSTRACE FbSystraceAsyncFlow::end( TRACE_TAG_REACT_CXX_BRIDGE, @@ -172,12 +172,11 @@ void NativeToJsBridge::callFunction( // This is safe because we are running on the executor's thread: it won't // destruct until after it's been unregistered (which we check above) and // that will happen on this thread - executor->callFunction(moduleId, methodId, arguments); + executor->callFunction(module, method, arguments); }); } -void NativeToJsBridge::invokeCallback(ExecutorToken executorToken, const double callbackId, - const folly::dynamic& arguments) { +void NativeToJsBridge::invokeCallback(ExecutorToken executorToken, double callbackId, folly::dynamic&& arguments) { int systraceCookie = -1; #ifdef WITH_FBSYSTRACE systraceCookie = m_systraceCookie++; @@ -187,7 +186,7 @@ void NativeToJsBridge::invokeCallback(ExecutorToken executorToken, const double systraceCookie); #endif - runOnExecutorQueue(executorToken, [callbackId, arguments, systraceCookie] (JSExecutor* executor) { + runOnExecutorQueue(executorToken, [callbackId, arguments = std::move(arguments), systraceCookie] (JSExecutor* executor) { #ifdef WITH_FBSYSTRACE FbSystraceAsyncFlow::end( TRACE_TAG_REACT_CXX_BRIDGE, diff --git a/ReactCommon/cxxreact/NativeToJsBridge.h b/ReactCommon/cxxreact/NativeToJsBridge.h index fea5a86ee18553..786c268675b064 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.h +++ b/ReactCommon/cxxreact/NativeToJsBridge.h @@ -68,14 +68,14 @@ class NativeToJsBridge { */ void callFunction( ExecutorToken executorToken, - const std::string& moduleId, - const std::string& methodId, - const folly::dynamic& args); + std::string&& module, + std::string&& method, + folly::dynamic&& args); /** * Invokes a callback with the cbID, and optional additional arguments in JS. */ - void invokeCallback(ExecutorToken executorToken, const double callbackId, const folly::dynamic& args); + void invokeCallback(ExecutorToken executorToken, double callbackId, folly::dynamic&& args); /** * Starts the JS application from an "bundle", i.e. a JavaScript file that From 2537157d994ac83f53a5ebc6ba5d74a9ad41f0c2 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Thu, 7 Jul 2016 08:44:59 -0700 Subject: [PATCH 019/241] Implement TextInput onContentSizeChange Summary: This adds proper support for tracking a TextInput content size as discussed in #6552 by adding a new callback that is called every time the content size changes including when first rendering the view. Some points that are up for discussion are what do we want to do with the onChange callback as I don't see any use left for it now that we can track text change in onChangeText and size changes in onContentSizeChange. Also a bit off topic but should we consider renaming onChangeText to onTextChange to keep the naming more consistent (see [this naming justification](https://twitter.com/notbrent/status/709445076850597888)). This is split in 2 commits for easier review, one for iOS and one for android. The iOS implementation simply checks if the content size has changed everytime we update it and fire the callback, the only small issue was that the content size had several different values on initial render so I added a check to not fire events before the layoutSubviews where at this point the value is g Closes https://github.com/facebook/react-native/pull/8457 Differential Revision: D3528202 Pulled By: dmmiller fbshipit-source-id: fefe83f10cc5bfde1f5937c48c88b10408e58d9d --- .../UIExplorer/TextInputExample.android.js | 15 +++-- Examples/UIExplorer/TextInputExample.ios.js | 15 +++-- Libraries/Components/TextInput/TextInput.js | 12 +++- Libraries/Text/RCTTextView.h | 1 + Libraries/Text/RCTTextView.m | 19 ++++++ Libraries/Text/RCTTextViewManager.m | 1 + .../uimanager/UIManagerModuleConstants.java | 9 +-- .../RecyclerViewBackedScrollViewManager.java | 3 - .../views/textinput/ContentSizeWatcher.java | 14 +++++ .../ReactContentSizeChangedEvent.java | 58 +++++++++++++++++++ .../react/views/textinput/ReactEditText.java | 34 +++++++++-- .../textinput/ReactTextInputManager.java | 56 +++++++++++++++++- 12 files changed, 209 insertions(+), 28 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/textinput/ContentSizeWatcher.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java diff --git a/Examples/UIExplorer/TextInputExample.android.js b/Examples/UIExplorer/TextInputExample.android.js index cc86ca45186df0..4891ae8e4e8e9b 100644 --- a/Examples/UIExplorer/TextInputExample.android.js +++ b/Examples/UIExplorer/TextInputExample.android.js @@ -76,18 +76,21 @@ var TextEventsExample = React.createClass({ class AutoExpandingTextInput extends React.Component { constructor(props) { super(props); - this.state = {text: '', height: 0}; + 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: event.nativeEvent.text, - height: event.nativeEvent.contentSize.height, - }); + onContentSizeChange={(event) => { + this.setState({height: event.nativeEvent.contentSize.height}); + }} + onChangeText={(text) => { + this.setState({text}); }} style={[styles.default, {height: Math.max(35, this.state.height)}]} value={this.state.text} diff --git a/Examples/UIExplorer/TextInputExample.ios.js b/Examples/UIExplorer/TextInputExample.ios.js index a3bae1d4629641..bebce92032d962 100644 --- a/Examples/UIExplorer/TextInputExample.ios.js +++ b/Examples/UIExplorer/TextInputExample.ios.js @@ -102,18 +102,21 @@ class AutoExpandingTextInput extends React.Component { constructor(props) { super(props); - this.state = {text: '', height: 0}; + 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: event.nativeEvent.text, - height: event.nativeEvent.contentSize.height, - }); + onChangeText={(text) => { + 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} diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 4a34b7c4fb5b31..c950b42918f8df 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -271,7 +271,7 @@ const TextInput = React.createClass({ * Sets the return key to the label. Use it instead of `returnKeyType`. * @platform android */ - returnKeyLabel: PropTypes.string, + returnKeyLabel: PropTypes.string, /** * Limits the maximum number of characters that can be entered. Use this * instead of implementing the logic in JS to avoid flicker. @@ -311,6 +311,14 @@ const TextInput = React.createClass({ * Changed text is passed as an argument to the callback handler. */ onChangeText: PropTypes.func, + /** + * Callback that is called when the text input's content size changes. + * This will be called with + * `{ nativeEvent: { contentSize: { width, height } } }`. + * + * Only called for multiline text inputs. + */ + onContentSizeChange: PropTypes.func, /** * Callback that is called when text input ends. */ @@ -581,6 +589,7 @@ const TextInput = React.createClass({ onFocus={this._onFocus} onBlur={this._onBlur} onChange={this._onChange} + onContentSizeChange={this.props.onContentSizeChange} onSelectionChange={onSelectionChange} onTextInput={this._onTextInput} onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue} @@ -641,6 +650,7 @@ const TextInput = React.createClass({ onFocus={this._onFocus} onBlur={this._onBlur} onChange={this._onChange} + onContentSizeChange={this.props.onContentSizeChange} onSelectionChange={onSelectionChange} onTextInput={this._onTextInput} onEndEditing={this.props.onEndEditing} diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h index 8268e7a53ddea9..49e499fedccdf5 100644 --- a/Libraries/Text/RCTTextView.h +++ b/Libraries/Text/RCTTextView.h @@ -29,6 +29,7 @@ @property (nonatomic, strong) NSNumber *maxLength; @property (nonatomic, copy) RCTDirectEventBlock onChange; +@property (nonatomic, copy) RCTDirectEventBlock onContentSizeChange; @property (nonatomic, copy) RCTDirectEventBlock onSelectionChange; @property (nonatomic, copy) RCTDirectEventBlock onTextInput; diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index 94e99af4cc6001..4c377a58bcc11b 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -77,6 +77,9 @@ @implementation RCTTextView BOOL _blockTextShouldChange; BOOL _nativeUpdatesInFlight; NSInteger _nativeEventCount; + + CGSize _previousContentSize; + BOOL _viewDidCompleteInitialLayout; } - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher @@ -261,6 +264,17 @@ - (void)updateContentSize size.height = [_textView sizeThatFits:size].height; _scrollView.contentSize = size; _textView.frame = (CGRect){CGPointZero, size}; + + if (_viewDidCompleteInitialLayout && _onContentSizeChange && !CGSizeEqualToSize(_previousContentSize, size)) { + _previousContentSize = size; + _onContentSizeChange(@{ + @"contentSize": @{ + @"height": @(size.height), + @"width": @(size.width), + }, + @"target": self.reactTag, + }); + } } - (void)updatePlaceholder @@ -633,6 +647,11 @@ - (BOOL)resignFirstResponder - (void)layoutSubviews { [super layoutSubviews]; + + // 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; + [self updateFrames]; } diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index 97fbe5e351c5d4..cbbd60a38fa1eb 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -35,6 +35,7 @@ - (UIView *)view RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, textView.keyboardAppearance, UIKeyboardAppearance) RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber) RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onContentSizeChange, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) 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 d3bdab1be52a73..7b5528bf20daee 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java @@ -71,11 +71,12 @@ /* package */ static Map getDirectEventTypeConstants() { return MapBuilder.builder() - .put("topSelectionChange", MapBuilder.of("registrationName", "onSelectionChange")) - .put("topLoadingStart", MapBuilder.of("registrationName", "onLoadingStart")) - .put("topLoadingFinish", MapBuilder.of("registrationName", "onLoadingFinish")) - .put("topLoadingError", MapBuilder.of("registrationName", "onLoadingError")) + .put("topContentSizeChange", MapBuilder.of("registrationName", "onContentSizeChange")) .put("topLayout", MapBuilder.of("registrationName", "onLayout")) + .put("topLoadingError", MapBuilder.of("registrationName", "onLoadingError")) + .put("topLoadingFinish", MapBuilder.of("registrationName", "onLoadingFinish")) + .put("topLoadingStart", MapBuilder.of("registrationName", "onLoadingStart")) + .put("topSelectionChange", MapBuilder.of("registrationName", "onSelectionChange")) .build(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollViewManager.java index dfac0da4e10585..b82c03f74734e8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollViewManager.java @@ -85,9 +85,6 @@ public void scrollTo( Map getExportedCustomDirectEventTypeConstants() { return MapBuilder.builder() .put(ScrollEventType.SCROLL.getJSEventName(), MapBuilder.of("registrationName", "onScroll")) - .put( - ContentSizeChangeEvent.EVENT_NAME, - MapBuilder.of("registrationName", "onContentSizeChange")) .build(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ContentSizeWatcher.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ContentSizeWatcher.java new file mode 100644 index 00000000000000..fab6bbc1e34d1a --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ContentSizeWatcher.java @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.views.textinput; + +public interface ContentSizeWatcher { + public void onLayout(); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java new file mode 100644 index 00000000000000..a5d0ef21936f32 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java @@ -0,0 +1,58 @@ +/** + * 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.views.textinput; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.events.Event; +import com.facebook.react.uimanager.events.RCTEventEmitter; + +/** + * Event emitted by EditText native view when content size changes. + */ +public class ReactContentSizeChangedEvent extends Event { + + public static final String EVENT_NAME = "topContentSizeChange"; + + private int mContentWidth; + private int mContentHeight; + + public ReactContentSizeChangedEvent( + int viewId, + long timestampMs, + int contentSizeWidth, + int contentSizeHeight) { + super(viewId, timestampMs); + mContentWidth = contentSizeWidth; + mContentHeight = contentSizeHeight; + } + + @Override + public String getEventName() { + return EVENT_NAME; + } + + @Override + public void dispatch(RCTEventEmitter rctEventEmitter) { + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); + } + + private WritableMap serializeEventData() { + WritableMap eventData = Arguments.createMap(); + + WritableMap contentSize = Arguments.createMap(); + contentSize.putDouble("width", mContentWidth); + contentSize.putDouble("height", mContentHeight); + eventData.putMap("contentSize", contentSize); + + eventData.putInt("target", getViewTag()); + return eventData; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index 7f9a13d7d27b7a..d5f9c604229a8a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -71,6 +71,7 @@ public class ReactEditText extends EditText { private boolean mContainsImages; private boolean mBlurOnSubmit; private @Nullable SelectionWatcher mSelectionWatcher; + private @Nullable ContentSizeWatcher mContentSizeWatcher; private final InternalKeyListener mKeyListener; private static final KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard(); @@ -102,15 +103,30 @@ public ReactEditText(Context context) { // TODO: t6408636 verify if we should schedule a layout after a View does a requestLayout() @Override public boolean isLayoutRequested() { - return false; + // If we are watching and updating container height based on content size + // then we don't want to scroll right away. This isn't perfect -- you might + // want to limit the height the text input can grow to. Possible solution + // is to add another prop that determines whether we should scroll to end + // of text. + if (mContentSizeWatcher != null) { + return isMultiline(); + } else { + return false; + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (mContentSizeWatcher != null) { + mContentSizeWatcher.onLayout(); + } } // Consume 'Enter' key events: TextView tries to give focus to the next TextInput, but it can't // since we only allow JS to change focus, which in turn causes TextView to crash. @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_ENTER && - ((getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0 )) { + if (keyCode == KeyEvent.KEYCODE_ENTER && !isMultiline()) { hideSoftKeyboard(); return true; } @@ -162,6 +178,10 @@ public void removeTextChangedListener(TextWatcher watcher) { } } + public void setContentSizeWatcher(ContentSizeWatcher contentSizeWatcher) { + mContentSizeWatcher = contentSizeWatcher; + } + @Override protected void onSelectionChanged(int selStart, int selEnd) { super.onSelectionChanged(selStart, selEnd); @@ -212,7 +232,7 @@ public void setInputType(int type) { mStagedInputType = type; // Input type password defaults to monospace font, so we need to re-apply the font super.setTypeface(tf); - + // We override the KeyListener so that all keys on the soft input keyboard as well as hardware // keyboards work. Some KeyListeners like DigitsKeyListener will display the keyboard but not // accept all input from it @@ -329,6 +349,10 @@ private TextWatcherDelegator getTextWatcherDelegator() { return mTextWatcherDelegator; } + private boolean isMultiline() { + return (getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0; + } + /* package */ void setGravityHorizontal(int gravityHorizontal) { if (gravityHorizontal == 0) { gravityHorizontal = mDefaultGravityHorizontal; @@ -447,7 +471,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { @Override public void afterTextChanged(Editable s) { if (!mIsSettingTextFromJS && mListeners != null) { - for (android.text.TextWatcher listener : mListeners) { + for (TextWatcher listener : mListeners) { listener.afterTextChanged(s); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index ebb82644490040..f8c42d92072805 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -67,7 +67,7 @@ public class ReactTextInputManager extends BaseViewManager Date: Thu, 7 Jul 2016 09:10:42 -0700 Subject: [PATCH 020/241] Don't hard crash if you get a null stack trace in Android Summary: If stacktrace-parser can't parse a stack trace, it'll return null. This can cause us to accidentally enter a crash loop where whenever you start your app, you load the last JS bundle you had, get a crash, and then hard crash trying to print the stack trace. Reviewed By: frantic Differential Revision: D3528141 fbshipit-source-id: 1146f43bc40492bfa79b6a1c0f81092383896164 --- .../com/facebook/react/devsupport/StackTraceHelper.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java index 007f3607ae6f64..a2b1bb2a8f5e86 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java @@ -9,6 +9,8 @@ package com.facebook.react.devsupport; +import javax.annotation.Nullable; + import java.io.File; import com.facebook.react.bridge.ReadableArray; @@ -91,9 +93,10 @@ public String getFileName() { * Convert a JavaScript stack trace (see {@code parseErrorStack} JS module) to an array of * {@link StackFrame}s. */ - public static StackFrame[] convertJsStackTrace(ReadableArray stack) { - StackFrame[] result = new StackFrame[stack.size()]; - for (int i = 0; i < stack.size(); i++) { + public static StackFrame[] convertJsStackTrace(@Nullable ReadableArray stack) { + int size = stack != null ? stack.size() : 0; + StackFrame[] result = new StackFrame[size]; + for (int i = 0; i < size; i++) { ReadableMap frame = stack.getMap(i); String methodName = frame.getString("methodName"); String fileName = frame.getString("file"); From eeb9cd8075886722df84e91ab64f9513f41cd0fa Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 7 Jul 2016 11:03:09 -0700 Subject: [PATCH 021/241] Better error message when trying to parse a empty body in fetch Summary: Previously, trying to parse an empty network body would throw "Unexpected EOF" from JSON.parse. Now we explicitly call out the error and provide steps for possible mitigation. Reviewed By: frantic Differential Revision: D3528297 fbshipit-source-id: 3b52c9491c1504c282eb9bc12ed46069cb6cbd60 --- Libraries/Fetch/fetch.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Libraries/Fetch/fetch.js b/Libraries/Fetch/fetch.js index 7cb122a07f4cc2..171030496d0b1a 100644 --- a/Libraries/Fetch/fetch.js +++ b/Libraries/Fetch/fetch.js @@ -11,6 +11,8 @@ * * @providesModule fetch * @nolint + * + * NOTE: This file has local changes for RN and is not a straight copy! */ /* eslint-disable */ 'use strict'; @@ -240,7 +242,26 @@ var self = {}; } this.json = function() { - return this.text().then(JSON.parse) + var status = this.status; + return this.text().then(function(text) { + if (!text) { + var errorText = + 'Trying to parse the body of a network response as JSON, but the ' + + 'body is null or empty.'; + if (status !== 200) { + errorText += + '\n\nThis can be the result of not properly checking for a 200 OK ' + + 'status code before trying to parse the body (the status code for ' + + 'this response was ' + status + ').'; + } else { + errorText += + '\n\nThe status code for this response was 200 OK, so nothing ' + + 'went obviously wrong. Is your server configured properly?'; + } + throw new Error(errorText); + } + return JSON.parse(text); + }); } return this From 41064991a238b2432fec65d68961df2ec77d9268 Mon Sep 17 00:00:00 2001 From: Konstantin Raev Date: Thu, 7 Jul 2016 11:40:52 -0700 Subject: [PATCH 022/241] fix: increased fs timeout for assetserver to test if this helps for large codebases Reviewed By: jingc Differential Revision: D3528913 fbshipit-source-id: f04eff42327bd729ebfcd71856a1d38ef9810986 --- packager/react-packager/src/AssetServer/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packager/react-packager/src/AssetServer/index.js b/packager/react-packager/src/AssetServer/index.js index 65f1edcfc2686b..88a7bebe442dd2 100644 --- a/packager/react-packager/src/AssetServer/index.js +++ b/packager/react-packager/src/AssetServer/index.js @@ -28,9 +28,9 @@ function timeoutableDenodeify(fsFunc, timeout) { }; } -const stat = timeoutableDenodeify(fs.stat, 5000); -const readDir = timeoutableDenodeify(fs.readdir, 5000); -const readFile = timeoutableDenodeify(fs.readFile, 5000); +const stat = timeoutableDenodeify(fs.stat, 15000); +const readDir = timeoutableDenodeify(fs.readdir, 15000); +const readFile = timeoutableDenodeify(fs.readFile, 15000); const validateOpts = declareOpts({ projectRoots: { From bcf4bb6eddb8da491fd9c648bd8693f49e417416 Mon Sep 17 00:00:00 2001 From: Skotch Vail Date: Thu, 7 Jul 2016 12:36:56 -0700 Subject: [PATCH 023/241] Automated changes to remove implicit capture of self in blocks: Libraries/FBReactKit/BUCK Reviewed By: javache Differential Revision: D3442470 fbshipit-source-id: 584a2bb3df5f7122166778b8fd44fae45560491e --- .../RCTLoggingTests.m | 8 +-- .../UIExplorerUnitTests/RCTBridgeTests.m | 6 +- .../UIExplorerUnitTests/RCTModuleInitTests.m | 4 +- Libraries/CameraRoll/RCTCameraRollManager.m | 4 +- Libraries/Image/RCTImageEditingManager.m | 2 +- Libraries/Image/RCTImageLoader.m | 72 +++++++++---------- Libraries/Image/RCTImageStoreManager.m | 10 +-- Libraries/Image/RCTImageView.m | 2 +- Libraries/Network/RCTNetworking.m | 16 ++--- Libraries/RCTTest/RCTTestModule.m | 16 ++--- Libraries/Settings/RCTSettingsManager.m | 4 +- Libraries/WebSocket/RCTSRWebSocket.m | 52 +++++++------- Libraries/WebSocket/RCTWebSocketExecutor.m | 6 +- React/Base/RCTBatchedBridge.m | 48 ++++++------- React/Base/RCTDisplayLink.m | 2 +- React/Base/RCTModuleData.m | 2 +- React/Base/RCTRootView.m | 10 +-- React/Executors/RCTJSCExecutor.mm | 4 +- React/Modules/RCTAsyncLocalStorage.m | 2 +- React/Modules/RCTDevLoadingView.m | 38 +++++----- React/Modules/RCTDevMenu.m | 24 +++---- React/Modules/RCTRedBox.m | 10 +-- React/Modules/RCTUIManager.m | 30 ++++---- React/Profiler/RCTFPSGraph.m | 2 +- React/Profiler/RCTPerfMonitor.m | 10 +-- React/Views/RCTComponentData.m | 4 +- React/Views/RCTMap.m | 20 +++--- React/Views/RCTModalHostView.m | 4 +- React/Views/RCTNavigator.m | 16 ++--- React/Views/RCTScrollView.m | 8 +-- React/Views/RCTShadowView.m | 4 +- React/Views/RCTTabBar.m | 8 +-- 32 files changed, 224 insertions(+), 224 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m index b46d2cae91ffa9..fecc3b3a8fbdb9 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m @@ -54,10 +54,10 @@ - (void)setUp _logSem = dispatch_semaphore_create(0); RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { if (source == RCTLogSourceJavaScript) { - _lastLogLevel = level; - _lastLogSource = source; - _lastLogMessage = message; - dispatch_semaphore_signal(_logSem); + self->_lastLogLevel = level; + self->_lastLogSource = source; + self->_lastLogMessage = message; + dispatch_semaphore_signal(self->_logSem); } }); } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m index 9f2570bcd25f85..15744669bc779a 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m @@ -152,7 +152,7 @@ - (void)setUp _unregisteredTestModule = [UnregisteredTestModule new]; _bridge = [[RCTBridge alloc] initWithBundleURL:nil - moduleProvider:^{ return @[self, _unregisteredTestModule]; } + moduleProvider:^{ return @[self, self->_unregisteredTestModule]; } launchOptions:nil]; _bridge.executorClass = [TestExecutor class]; @@ -232,7 +232,7 @@ - (void)testCallNativeMethod dispatch_sync(_methodQueue, ^{ // clear the queue - XCTAssertTrue(_testMethodCalled); + XCTAssertTrue(self->_testMethodCalled); }); } @@ -263,7 +263,7 @@ - (void)testCallUnregisteredModuleMethod [_bridge.batchedBridge handleBuffer:buffer]; dispatch_sync(_unregisteredTestModule.methodQueue, ^{ - XCTAssertTrue(_unregisteredTestModule.testMethodCalled); + XCTAssertTrue(self->_unregisteredTestModule.testMethodCalled); }); } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m index bf05e72bad620e..0de479392420ff 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m @@ -225,7 +225,7 @@ - (void)testCustomSetBridgeModuleInitializedAtBridgeStartup __block RCTTestCustomSetBridgeModule *module; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]]; + module = [self->_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]]; }); RUN_RUNLOOP_WHILE(!module); @@ -253,7 +253,7 @@ - (void)testLazyInitModuleNotInitializedDuringBridgeInit __block RCTLazyInitModule *module; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - module = [_bridge moduleForClass:[RCTLazyInitModule class]]; + module = [self->_bridge moduleForClass:[RCTLazyInitModule class]]; }); RUN_RUNLOOP_WHILE(!module); diff --git a/Libraries/CameraRoll/RCTCameraRollManager.m b/Libraries/CameraRoll/RCTCameraRollManager.m index 8512cf64e743d6..83519a059d2241 100644 --- a/Libraries/CameraRoll/RCTCameraRollManager.m +++ b/Libraries/CameraRoll/RCTCameraRollManager.m @@ -90,7 +90,7 @@ @implementation RCTCameraRollManager if ([type isEqualToString:@"video"]) { // It's unclear if writeVideoAtPathToSavedPhotosAlbum is thread-safe dispatch_async(dispatch_get_main_queue(), ^{ - [_bridge.assetsLibrary writeVideoAtPathToSavedPhotosAlbum:request.URL completionBlock:^(NSURL *assetURL, NSError *saveError) { + [self->_bridge.assetsLibrary writeVideoAtPathToSavedPhotosAlbum:request.URL completionBlock:^(NSURL *assetURL, NSError *saveError) { if (saveError) { reject(RCTErrorUnableToSave, nil, saveError); } else { @@ -107,7 +107,7 @@ @implementation RCTCameraRollManager } // It's unclear if writeImageToSavedPhotosAlbum is thread-safe dispatch_async(dispatch_get_main_queue(), ^{ - [_bridge.assetsLibrary writeImageToSavedPhotosAlbum:loadedImage.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *saveError) { + [self->_bridge.assetsLibrary writeImageToSavedPhotosAlbum:loadedImage.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *saveError) { if (saveError) { RCTLogWarn(@"Error saving cropped image: %@", saveError); reject(RCTErrorUnableToSave, nil, saveError); diff --git a/Libraries/Image/RCTImageEditingManager.m b/Libraries/Image/RCTImageEditingManager.m index cc8dfd0faf27f6..bb65990ba75fff 100644 --- a/Libraries/Image/RCTImageEditingManager.m +++ b/Libraries/Image/RCTImageEditingManager.m @@ -67,7 +67,7 @@ @implementation RCTImageEditingManager } // Store image - [_bridge.imageStoreManager storeImage:croppedImage withBlock:^(NSString *croppedImageTag) { + [self->_bridge.imageStoreManager storeImage:croppedImage withBlock:^(NSString *croppedImageTag) { if (!croppedImageTag) { NSString *errorMessage = @"Error storing cropped image in RCTImageStoreManager"; RCTLogWarn(@"%@", errorMessage); diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index 2132837a1d3eb7..028157903d8179 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -234,11 +234,11 @@ - (void)dequeueTasks dispatch_async(_URLCacheQueue, ^{ // Remove completed tasks - for (RCTNetworkTask *task in _pendingTasks.reverseObjectEnumerator) { + for (RCTNetworkTask *task in self->_pendingTasks.reverseObjectEnumerator) { switch (task.status) { case RCTNetworkTaskFinished: - [_pendingTasks removeObject:task]; - _activeTasks--; + [self->_pendingTasks removeObject:task]; + self->_activeTasks--; break; case RCTNetworkTaskPending: break; @@ -246,8 +246,8 @@ - (void)dequeueTasks // Check task isn't "stuck" if (task.requestToken == nil) { RCTLogWarn(@"Task orphaned for request %@", task.request); - [_pendingTasks removeObject:task]; - _activeTasks--; + [self->_pendingTasks removeObject:task]; + self->_activeTasks--; [task cancel]; } break; @@ -255,12 +255,12 @@ - (void)dequeueTasks } // Start queued decode - NSInteger activeDecodes = _scheduledDecodes - _pendingDecodes.count; - while (activeDecodes == 0 || (_activeBytes <= _maxConcurrentDecodingBytes && - activeDecodes <= _maxConcurrentDecodingTasks)) { - dispatch_block_t decodeBlock = _pendingDecodes.firstObject; + NSInteger activeDecodes = self->_scheduledDecodes - self->_pendingDecodes.count; + while (activeDecodes == 0 || (self->_activeBytes <= self->_maxConcurrentDecodingBytes && + activeDecodes <= self->_maxConcurrentDecodingTasks)) { + dispatch_block_t decodeBlock = self->_pendingDecodes.firstObject; if (decodeBlock) { - [_pendingDecodes removeObjectAtIndex:0]; + [self->_pendingDecodes removeObjectAtIndex:0]; decodeBlock(); } else { break; @@ -268,13 +268,13 @@ - (void)dequeueTasks } // Start queued tasks - for (RCTNetworkTask *task in _pendingTasks) { - if (MAX(_activeTasks, _scheduledDecodes) >= _maxConcurrentLoadingTasks) { + for (RCTNetworkTask *task in self->_pendingTasks) { + if (MAX(self->_activeTasks, self->_scheduledDecodes) >= self->_maxConcurrentLoadingTasks) { break; } if (task.status == RCTNetworkTaskPending) { [task start]; - _activeTasks++; + self->_activeTasks++; } } }); @@ -317,8 +317,8 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * } dispatch_async(_URLCacheQueue, ^{ - if (!_URLCache) { - _URLCache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB + if (!self->_URLCache) { + self->_URLCache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB diskCapacity:200 * 1024 * 1024 // 200MB diskPath:@"React/RCTImageDownloader"]; } @@ -342,7 +342,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * } // Check if networking module is available - if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) { + if (RCT_DEBUG && ![self->_bridge respondsToSelector:@selector(networking)]) { RCTLogError(@"No suitable image URL loader found for %@. You may need to " " import the RCTNetwork library in order to load images.", request.URL.absoluteString); @@ -350,7 +350,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * } // Check if networking module can load image - if (RCT_DEBUG && ![_bridge.networking canHandleRequest:request]) { + if (RCT_DEBUG && ![self->_bridge.networking canHandleRequest:request]) { RCTLogError(@"No suitable image URL loader found for %@", request.URL.absoluteString); return; } @@ -392,7 +392,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * // Check for cached response before reloading // TODO: move URL cache out of RCTImageLoader into its own module - NSCachedURLResponse *cachedResponse = [_URLCache cachedResponseForRequest:request]; + NSCachedURLResponse *cachedResponse = [self->_URLCache cachedResponseForRequest:request]; while (cachedResponse) { if ([cachedResponse.response isKindOfClass:[NSHTTPURLResponse class]]) { @@ -406,7 +406,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * NSURL *redirectURL = [NSURL URLWithString: location relativeToURL: request.URL]; request = [NSURLRequest requestWithURL:redirectURL]; - cachedResponse = [_URLCache cachedResponseForRequest:request]; + cachedResponse = [self->_URLCache cachedResponseForRequest:request]; continue; } } @@ -416,14 +416,14 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * } // Download image - RCTNetworkTask *task = [_bridge.networking networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { + RCTNetworkTask *task = [self->_bridge.networking networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { if (error) { completionHandler(error, nil); [weakSelf dequeueTasks]; return; } - dispatch_async(_URLCacheQueue, ^{ + dispatch_async(self->_URLCacheQueue, ^{ // Cache the response // TODO: move URL cache out of RCTImageLoader into its own module @@ -445,11 +445,11 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * }]; task.downloadProgressBlock = progressHandler; - if (!_pendingTasks) { - _pendingTasks = [NSMutableArray new]; + if (!self->_pendingTasks) { + self->_pendingTasks = [NSMutableArray new]; } if (task) { - [_pendingTasks addObject:task]; + [self->_pendingTasks addObject:task]; [weakSelf dequeueTasks]; } @@ -498,7 +498,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)image if (image) { CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * 4; if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) { - [_decodedImageCache setObject:image forKey:cacheKey cost:bytes]; + [self->_decodedImageCache setObject:image forKey:cacheKey cost:bytes]; } } completionBlock(error, image); @@ -579,7 +579,7 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data NSInteger decodedImageBytes = (size.width * scale) * (size.height * scale) * 4; // Mark these bytes as in-use - _activeBytes += decodedImageBytes; + self->_activeBytes += decodedImageBytes; // Do actual decompression on a concurrent background queue dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @@ -612,9 +612,9 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data // We're no longer retaining the uncompressed data, so now we'll mark // the decoding as complete so that the loading task queue can resume. - dispatch_async(_URLCacheQueue, ^{ - _scheduledDecodes--; - _activeBytes -= decodedImageBytes; + dispatch_async(self->_URLCacheQueue, ^{ + self->_scheduledDecodes--; + self->_activeBytes -= decodedImageBytes; [self dequeueTasks]; }); }); @@ -623,17 +623,17 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data // The decode operation retains the compressed image data until it's // complete, so we'll mark it as having started, in order to block // further image loads from happening until we're done with the data. - _scheduledDecodes++; + self->_scheduledDecodes++; - if (!_pendingDecodes) { - _pendingDecodes = [NSMutableArray new]; + if (!self->_pendingDecodes) { + self->_pendingDecodes = [NSMutableArray new]; } - NSInteger activeDecodes = _scheduledDecodes - _pendingDecodes.count - 1; - if (activeDecodes == 0 || (_activeBytes <= _maxConcurrentDecodingBytes && - activeDecodes <= _maxConcurrentDecodingTasks)) { + NSInteger activeDecodes = self->_scheduledDecodes - self->_pendingDecodes.count - 1; + if (activeDecodes == 0 || (self->_activeBytes <= self->_maxConcurrentDecodingBytes && + activeDecodes <= self->_maxConcurrentDecodingTasks)) { decodeBlock(); } else { - [_pendingDecodes addObject:decodeBlock]; + [self->_pendingDecodes addObject:decodeBlock]; } }); diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index 32afcee20f8647..55c1b978525401 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -66,7 +66,7 @@ - (void)getImageDataForTag:(NSString *)imageTag withBlock:(void (^)(NSData *imag { RCTAssertParam(block); dispatch_async(_methodQueue, ^{ - block(_store[imageTag]); + block(self->_store[imageTag]); }); } @@ -117,7 +117,7 @@ - (void)storeImage:(UIImage *)image withBlock:(void (^)(NSString *imageTag))bloc dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64String options:0]; if (imageData) { - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ successCallback(@[[self _storeImageData:imageData]]); }); } else { @@ -147,7 +147,7 @@ - (id)sendRequest:(NSURLRequest *)request withDelegate:(id_store[imageTag]; if (!imageData) { NSError *error = RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]); [delegate URLRequest:cancellationBlock didCompleteWithError:error]; @@ -206,7 +206,7 @@ - (UIImage *)imageForTag:(NSString *)imageTag RCTLogWarn(@"RCTImageStoreManager.imageForTag() is deprecated and has poor performance. Use an alternative method instead."); __block NSData *imageData; dispatch_sync(_methodQueue, ^{ - imageData = _store[imageTag]; + imageData = self->_store[imageTag]; }); return [UIImage imageWithData:imageData]; } @@ -215,7 +215,7 @@ - (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image)) { RCTAssertParam(block); dispatch_async(_methodQueue, ^{ - NSData *imageData = _store[imageTag]; + NSData *imageData = self->_store[imageTag]; dispatch_async(dispatch_get_main_queue(), ^{ // imageWithData: is not thread-safe, so we can't do this on methodQueue block([UIImage imageWithData:imageData]); diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index 48cfca6d8e2d91..51c78d9d4de079 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -216,7 +216,7 @@ - (void)reloadImage RCTImageLoaderProgressBlock progressHandler = nil; if (_onProgress) { progressHandler = ^(int64_t loaded, int64_t total) { - _onProgress(@{ + self->_onProgress(@{ @"loaded": @((double)loaded), @"total": @((double)total), }); diff --git a/Libraries/Network/RCTNetworking.m b/Libraries/Network/RCTNetworking.m index 07c21f095d3fad..95891d0d4825fa 100644 --- a/Libraries/Network/RCTNetworking.m +++ b/Libraries/Network/RCTNetworking.m @@ -95,7 +95,7 @@ - (RCTURLRequestCancellationBlock)handleResult:(NSDictionary *)r headers[@"content-type"] = partContentType; } [headers enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) { - [_multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue] + [self->_multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue] dataUsingEncoding:NSUTF8StringEncoding]]; }]; @@ -239,7 +239,7 @@ - (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary *)q [request setValue:(@(request.HTTPBody.length)).description forHTTPHeaderField:@"Content-Length"]; } - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ block(request); }); @@ -287,7 +287,7 @@ - (RCTURLRequestCancellationBlock)processDataForHTTPQuery:(nullable NSDictionary __block RCTURLRequestCancellationBlock cancellationBlock = nil; RCTNetworkTask *task = [self networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil); }); }]; @@ -365,14 +365,14 @@ - (void)sendRequest:(NSURLRequest *)request __block RCTNetworkTask *task; RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) { - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ NSArray *responseJSON = @[task.requestID, @((double)progress), @((double)total)]; [self sendEventWithName:@"didSendNetworkData" body:responseJSON]; }); }; void (^responseBlock)(NSURLResponse *) = ^(NSURLResponse *response) { - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ NSDictionary *headers; NSInteger status; if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request @@ -390,14 +390,14 @@ - (void)sendRequest:(NSURLRequest *)request }; void (^incrementalDataBlock)(NSData *) = incrementalUpdates ? ^(NSData *data) { - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ [self sendData:data forTask:task]; }); } : nil; RCTURLRequestCompletionBlock completionBlock = ^(NSURLResponse *response, NSData *data, NSError *error) { - dispatch_async(_methodQueue, ^{ + dispatch_async(self->_methodQueue, ^{ if (!incrementalUpdates) { [self sendData:data forTask:task]; } @@ -407,7 +407,7 @@ - (void)sendRequest:(NSURLRequest *)request ]; [self sendEventWithName:@"didCompleteNetworkResponse" body:responseJSON]; - [_tasksByRequestID removeObjectForKey:task.requestID]; + [self->_tasksByRequestID removeObjectForKey:task.requestID]; }); }; diff --git a/Libraries/RCTTest/RCTTestModule.m b/Libraries/RCTTest/RCTTestModule.m index 2d86a1c6ba2115..e9ba29132c6680 100644 --- a/Libraries/RCTTest/RCTTestModule.m +++ b/Libraries/RCTTest/RCTTestModule.m @@ -35,16 +35,16 @@ - (dispatch_queue_t)methodQueue [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - NSString *testName = NSStringFromSelector(_testSelector); - if (!_snapshotCounter) { - _snapshotCounter = [NSMutableDictionary new]; + NSString *testName = NSStringFromSelector(self->_testSelector); + if (!self->_snapshotCounter) { + self->_snapshotCounter = [NSMutableDictionary new]; } - _snapshotCounter[testName] = (@([_snapshotCounter[testName] integerValue] + 1)).stringValue; + self->_snapshotCounter[testName] = (@([self->_snapshotCounter[testName] integerValue] + 1)).stringValue; NSError *error = nil; - BOOL success = [_controller compareSnapshotOfView:_view - selector:_testSelector - identifier:_snapshotCounter[testName] + BOOL success = [self->_controller compareSnapshotOfView:self->_view + selector:self->_testSelector + identifier:self->_snapshotCounter[testName] error:&error]; callback(@[@(success)]); }]; @@ -76,7 +76,7 @@ - (dispatch_queue_t)methodQueue RCT_EXPORT_METHOD(markTestPassed:(BOOL)success) { [_bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary *viewRegistry) { - _status = success ? RCTTestStatusPassed : RCTTestStatusFailed; + self->_status = success ? RCTTestStatusPassed : RCTTestStatusFailed; }]; } diff --git a/Libraries/Settings/RCTSettingsManager.m b/Libraries/Settings/RCTSettingsManager.m index 859506e671e12a..eac4e3e98f952a 100644 --- a/Libraries/Settings/RCTSettingsManager.m +++ b/Libraries/Settings/RCTSettingsManager.m @@ -80,9 +80,9 @@ - (void)userDefaultsDidChange:(NSNotification *)note [values enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, BOOL *stop) { id plist = [RCTConvert NSPropertyList:json]; if (plist) { - [_defaults setObject:plist forKey:key]; + [self->_defaults setObject:plist forKey:key]; } else { - [_defaults removeObjectForKey:key]; + [self->_defaults removeObjectForKey:key]; } }]; diff --git a/Libraries/WebSocket/RCTSRWebSocket.m b/Libraries/WebSocket/RCTSRWebSocket.m index e5ed0adb99ef25..26bd54bcf93b73 100644 --- a/Libraries/WebSocket/RCTSRWebSocket.m +++ b/Libraries/WebSocket/RCTSRWebSocket.m @@ -471,9 +471,9 @@ - (void)_readHTTPHeader; } [self _readUntilHeaderCompleteWithCallback:^(RCTSRWebSocket *socket, NSData *data) { - CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); + CFHTTPMessageAppendBytes(self->_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); - if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { + if (CFHTTPMessageIsHeaderComplete(self->_receivedHTTPHeaders)) { RCTSRLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); [socket _HTTPHeadersDidFinish]; } else { @@ -643,7 +643,7 @@ - (void)_closeWithProtocolError:(NSString *)message; // Need to shunt this on the _callbackQueue first to see if they received any messages [self _performDelegateBlock:^{ [self closeWithCode:RCTSRStatusCodeProtocolError reason:message]; - dispatch_async(_workQueue, ^{ + dispatch_async(self->_workQueue, ^{ [self _disconnect]; }); }]; @@ -653,7 +653,7 @@ - (void)_failWithError:(NSError *)error; { dispatch_async(_workQueue, ^{ if (self.readyState != RCTSR_CLOSED) { - _failed = YES; + self->_failed = YES; [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { [self.delegate webSocket:self didFailWithError:error]; @@ -661,7 +661,7 @@ - (void)_failWithError:(NSError *)error; }]; self.readyState = RCTSR_CLOSED; - _selfRetain = nil; + self->_selfRetain = nil; RCTSRLog(@"Failing with error %@", error.localizedDescription); @@ -713,7 +713,7 @@ - (void)handlePing:(NSData *)pingData; { // Need to pingpong this off _callbackQueue first to make sure messages happen in order [self _performDelegateBlock:^{ - dispatch_async(_workQueue, ^{ + dispatch_async(self->_workQueue, ^{ [self _sendFrameWithOpcode:RCTSROpCodePong data:pingData]; }); }]; @@ -990,7 +990,7 @@ - (void)_readFrameContinue; [socket _closeWithProtocolError:@"Client must receive unmasked data"]; } - size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; + size_t extra_bytes_needed = header.masked ? sizeof(self->_currentReadMaskKey) : 0; if (header.payload_length == 126) { extra_bytes_needed += sizeof(uint16_t); @@ -1020,7 +1020,7 @@ - (void)_readFrameContinue; } if (header.masked) { - assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); + assert(mapped_size >= sizeof(self->_currentReadMaskOffset) + offset); memcpy(_socket->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(_socket->_currentReadMaskKey)); } @@ -1033,12 +1033,12 @@ - (void)_readFrameContinue; - (void)_readFrameNew; { dispatch_async(_workQueue, ^{ - _currentFrameData.length = 0; + self->_currentFrameData.length = 0; - _currentFrameOpcode = 0; - _currentFrameCount = 0; - _readOpCount = 0; - _currentStringScanPosition = 0; + self->_currentFrameOpcode = 0; + self->_currentFrameCount = 0; + self->_readOpCount = 0; + self->_currentStringScanPosition = 0; [self _readFrameContinue]; }); @@ -1081,7 +1081,7 @@ - (void)_pumpWriting; if (!_failed) { [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; + [self.delegate webSocket:self didCloseWithCode:self->_closeCode reason:self->_closeReason wasClean:YES]; } }]; } @@ -1388,9 +1388,9 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; if (self.readyState >= RCTSR_CLOSING) { return; } - assert(_readBuffer); + assert(self->_readBuffer); - if (self.readyState == RCTSR_CONNECTING && aStream == _inputStream) { + if (self.readyState == RCTSR_CONNECTING && aStream == self->_inputStream) { [self didConnect]; } [self _pumpWriting]; @@ -1402,8 +1402,8 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; RCTSRLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [aStream.streamError copy]); // TODO: specify error better! [self _failWithError:aStream.streamError]; - _readBufferOffset = 0; - _readBuffer.length = 0; + self->_readBufferOffset = 0; + self->_readBuffer.length = 0; break; } @@ -1414,14 +1414,14 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; if (aStream.streamError) { [self _failWithError:aStream.streamError]; } else { - dispatch_async(_workQueue, ^{ + dispatch_async(self->_workQueue, ^{ if (self.readyState != RCTSR_CLOSED) { self.readyState = RCTSR_CLOSED; - _selfRetain = nil; + self->_selfRetain = nil; } - if (!_sentClose && !_failed) { - _sentClose = YES; + if (!self->_sentClose && !self->_failed) { + self->_sentClose = YES; // If we get closed in this state it's probably not clean because we should be sending this when we send messages [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { @@ -1440,13 +1440,13 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; const int bufferSize = 2048; uint8_t buffer[bufferSize]; - while (_inputStream.hasBytesAvailable) { - NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize]; + while (self->_inputStream.hasBytesAvailable) { + NSInteger bytes_read = [self->_inputStream read:buffer maxLength:bufferSize]; if (bytes_read > 0) { - [_readBuffer appendBytes:buffer length:bytes_read]; + [self->_readBuffer appendBytes:buffer length:bytes_read]; } else if (bytes_read < 0) { - [self _failWithError:_inputStream.streamError]; + [self _failWithError:self->_inputStream.streamError]; } if (bytes_read != bufferSize) { diff --git a/Libraries/WebSocket/RCTWebSocketExecutor.m b/Libraries/WebSocket/RCTWebSocketExecutor.m index c9696c1bb5e62c..ccf7e6db3a36ea 100644 --- a/Libraries/WebSocket/RCTWebSocketExecutor.m +++ b/Libraries/WebSocket/RCTWebSocketExecutor.m @@ -154,10 +154,10 @@ - (void)sendMessage:(NSDictionary *)message waitForReply:(RCTWSM } NSNumber *expectedID = @(lastID++); - _callbacks[expectedID] = [callback copy]; + self->_callbacks[expectedID] = [callback copy]; NSMutableDictionary *messageWithID = [message mutableCopy]; messageWithID[@"id"] = expectedID; - [_socket send:RCTJSONStringify(messageWithID, NULL)]; + [self->_socket send:RCTJSONStringify(messageWithID, NULL)]; }); } @@ -215,7 +215,7 @@ - (void)_executeJSCall:(NSString *)method arguments:(NSArray *)arguments callbac - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete { dispatch_async(_jsQueue, ^{ - _injectedObjects[objectName] = script; + self->_injectedObjects[objectName] = script; onComplete(nil); }); } diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 4ea36012a2237e..2db2c4bbc88ecf 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -26,7 +26,7 @@ #import "RCTRedBox.h" #define RCTAssertJSThread() \ - RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTJSCExecutor"] || \ + RCTAssert(![NSStringFromClass([self->_javaScriptExecutor class]) isEqualToString:@"RCTJSCExecutor"] || \ [[[NSThread currentThread] name] isEqualToString:RCTJSCThreadName], \ @"This method must be called on JS thread") @@ -174,7 +174,7 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad } else if (self.bundleURL) { [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:^(NSError *error, NSData *source, int64_t sourceLength) { if (error && [self.delegate respondsToSelector:@selector(fallbackSourceURLForBridge:)]) { - NSURL *fallbackURL = [self.delegate fallbackSourceURLForBridge:_parentBridge]; + NSURL *fallbackURL = [self.delegate fallbackSourceURLForBridge:self->_parentBridge]; if (fallbackURL && ![fallbackURL isEqual:self.bundleURL]) { RCTLogError(@"Failed to load bundle(%@) with error:(%@)", self.bundleURL, error.localizedDescription); self.bundleURL = fallbackURL; @@ -190,7 +190,7 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad [self didFinishLoading]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:_parentBridge userInfo:@{@"bridge": self}]; + object:self->_parentBridge userInfo:@{@"bridge": self}]; }); onSourceLoad(nil, nil, 0); } @@ -462,7 +462,7 @@ - (void)executeSourceCode:(NSData *)sourceCode sourceCodeModule.scriptURL = self.bundleURL; [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) { - if (!_valid) { + if (!self->_valid) { return; } @@ -474,8 +474,8 @@ - (void)executeSourceCode:(NSData *)sourceCode } // Register the display link to start sending js calls after everything is setup - NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTJSCExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop]; - [_displayLink addToRunLoop:targetRunLoop]; + NSRunLoop *targetRunLoop = [self->_javaScriptExecutor isKindOfClass:[RCTJSCExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop]; + [self->_displayLink addToRunLoop:targetRunLoop]; // Perform the state update and notification on the main thread, so we can't run into // timing issues with RCTRootView @@ -483,7 +483,7 @@ - (void)executeSourceCode:(NSData *)sourceCode [self didFinishLoading]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:_parentBridge userInfo:@{@"bridge": self}]; + object:self->_parentBridge userInfo:@{@"bridge": self}]; }); }]; @@ -505,7 +505,7 @@ - (void)didFinishLoading [_performanceLogger markStopForTag:RCTPLBridgeStartup]; _loading = NO; [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ - for (dispatch_block_t call in _pendingCalls) { + for (dispatch_block_t call in self->_pendingCalls) { call(); } }]; @@ -637,24 +637,24 @@ - (void)invalidate } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ - [_displayLink invalidate]; - _displayLink = nil; + [self->_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ + [self->_displayLink invalidate]; + self->_displayLink = nil; - [_javaScriptExecutor invalidate]; - _javaScriptExecutor = nil; + [self->_javaScriptExecutor invalidate]; + self->_javaScriptExecutor = nil; if (RCTProfileIsProfiling()) { RCTProfileUnhookModules(self); } - _moduleDataByName = nil; - _moduleDataByID = nil; - _moduleClassesByID = nil; - _pendingCalls = nil; + self->_moduleDataByName = nil; + self->_moduleDataByID = nil; + self->_moduleClassesByID = nil; + self->_pendingCalls = nil; - if (_flowIDMap != NULL) { - CFRelease(_flowIDMap); + if (self->_flowIDMap != NULL) { + CFRelease(self->_flowIDMap); } }]; }); @@ -778,7 +778,7 @@ - (void)enqueueApplicationScript:(NSData *)script } RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"FetchApplicationScriptCallbacks", nil); - [_javaScriptExecutor flushedQueue:^(id json, NSError *error) + [self->_javaScriptExecutor flushedQueue:^(id json, NSError *error) { RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call,init", @{ @"json": RCTNullIfNil(json), @@ -889,7 +889,7 @@ - (void)handleBuffer:(NSArray *)buffer capacity:_moduleDataByName.count]; [moduleIDs enumerateObjectsUsingBlock:^(NSNumber *moduleID, NSUInteger i, __unused BOOL *stop) { - RCTModuleData *moduleData = _moduleDataByID[moduleID.integerValue]; + RCTModuleData *moduleData = self->_moduleDataByID[moduleID.integerValue]; dispatch_queue_t queue = moduleData.methodQueue; NSMutableOrderedSet *set = [buckets objectForKey:queue]; if (!set) { @@ -910,11 +910,11 @@ - (void)handleBuffer:(NSArray *)buffer @autoreleasepool { for (NSNumber *indexObj in calls) { NSUInteger index = indexObj.unsignedIntegerValue; - if (RCT_DEV && callID != -1 && _flowIDMap != NULL && RCTProfileIsProfiling()) { + if (RCT_DEV && callID != -1 && self->_flowIDMap != NULL && RCTProfileIsProfiling()) { [self.flowIDMapLock lock]; - int64_t newFlowID = (int64_t)CFDictionaryGetValue(_flowIDMap, (const void *)(_flowID + index)); + int64_t newFlowID = (int64_t)CFDictionaryGetValue(self->_flowIDMap, (const void *)(self->_flowID + index)); _RCTProfileEndFlowEvent(@(newFlowID)); - CFDictionaryRemoveValue(_flowIDMap, (const void *)(_flowID + index)); + CFDictionaryRemoveValue(self->_flowIDMap, (const void *)(self->_flowID + index)); [self.flowIDMapLock unlock]; } [self _handleRequestNumber:index diff --git a/React/Base/RCTDisplayLink.m b/React/Base/RCTDisplayLink.m index 7ad102daec1cfa..12450cb96cd489 100644 --- a/React/Base/RCTDisplayLink.m +++ b/React/Base/RCTDisplayLink.m @@ -55,7 +55,7 @@ - (void)registerModuleForFrameUpdates:(id)module CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop]; - if (!_runLoop) { + if (!self->_runLoop) { return; } diff --git a/React/Base/RCTModuleData.m b/React/Base/RCTModuleData.m index 4d6caaf5df01db..96aefd2358baf4 100644 --- a/React/Base/RCTModuleData.m +++ b/React/Base/RCTModuleData.m @@ -277,7 +277,7 @@ - (void)gatherConstants RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass], nil); (void)[self instance]; RCTExecuteOnMainThread(^{ - _constantsToExport = [_instance constantsToExport] ?: @{}; + self->_constantsToExport = [self->_instance constantsToExport] ?: @{}; }, YES); } RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil); diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index d335aac834ce55..20fc643a1a1bdb 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -158,12 +158,12 @@ - (void)hideLoadingView dispatch_get_main_queue(), ^{ [UIView transitionWithView:self - duration:_loadingViewFadeDuration + duration:self->_loadingViewFadeDuration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ - _loadingView.hidden = YES; + self->_loadingView.hidden = YES; } completion:^(__unused BOOL finished) { - [_loadingView removeFromSuperview]; + [self->_loadingView removeFromSuperview]; }]; }); } else { @@ -342,8 +342,8 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex [super insertReactSubview:subview atIndex:atIndex]; [_bridge->_performanceLogger markStopForTag:RCTPLTTI]; dispatch_async(dispatch_get_main_queue(), ^{ - if (!_contentHasAppeared) { - _contentHasAppeared = YES; + if (!self->_contentHasAppeared) { + self->_contentHasAppeared = YES; [[NSNotificationCenter defaultCenter] postNotificationName:RCTContentDidAppearNotification object:self.superview]; } diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 506b5119b82cb4..090c4ae9448823 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -477,7 +477,7 @@ - (void)setUp } JSContext *context = strongSelf.context.context; - RCTInstallJSCProfiler(_bridge, context.JSGlobalContextRef); + RCTInstallJSCProfiler(self->_bridge, context.JSGlobalContextRef); }]; // Inject handler used by HMR @@ -501,7 +501,7 @@ - (void)toggleProfilingFlag:(NSNotification *)notification { [self executeBlockOnJavaScriptQueue:^{ BOOL enabled = [notification.name isEqualToString:RCTProfileDidStartProfiling]; - [_bridge enqueueJSCall:@"Systrace.setEnabled" args:@[enabled ? @YES : @NO]]; + [self->_bridge enqueueJSCall:@"Systrace.setEnabled" args:@[enabled ? @YES : @NO]]; }]; } diff --git a/React/Modules/RCTAsyncLocalStorage.m b/React/Modules/RCTAsyncLocalStorage.m index f3f6a208a4dc23..6d150d0761d783 100644 --- a/React/Modules/RCTAsyncLocalStorage.m +++ b/React/Modules/RCTAsyncLocalStorage.m @@ -169,7 +169,7 @@ - (dispatch_queue_t)methodQueue - (void)clearAllData { dispatch_async(RCTGetMethodQueue(), ^{ - [_manifest removeAllObjects]; + [self->_manifest removeAllObjects]; [RCTGetCache() removeAllObjects]; RCTDeleteStorageDirectory(); }); diff --git a/React/Modules/RCTDevLoadingView.m b/React/Modules/RCTDevLoadingView.m index a451f79fce3726..0b2d4f3f8c098e 100644 --- a/React/Modules/RCTDevLoadingView.m +++ b/React/Modules/RCTDevLoadingView.m @@ -70,26 +70,26 @@ - (void)setBridge:(RCTBridge *)bridge } dispatch_async(dispatch_get_main_queue(), ^{ - _showDate = [NSDate date]; - if (!_window && !RCTRunningInTestEnvironment()) { + self->_showDate = [NSDate date]; + if (!self->_window && !RCTRunningInTestEnvironment()) { CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; - _window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 22)]; - _window.windowLevel = UIWindowLevelStatusBar + 1; + self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 22)]; + self->_window.windowLevel = UIWindowLevelStatusBar + 1; // set a root VC so rotation is supported - _window.rootViewController = [UIViewController new]; + self->_window.rootViewController = [UIViewController new]; - _label = [[UILabel alloc] initWithFrame:_window.bounds]; - _label.font = [UIFont systemFontOfSize:12.0]; - _label.textAlignment = NSTextAlignmentCenter; + self->_label = [[UILabel alloc] initWithFrame:self->_window.bounds]; + self->_label.font = [UIFont systemFontOfSize:12.0]; + self->_label.textAlignment = NSTextAlignmentCenter; - [_window addSubview:_label]; + [self->_window addSubview:self->_label]; } - _label.text = message; - _label.textColor = color; - _window.backgroundColor = backgroundColor; - _window.hidden = NO; + self->_label.text = message; + self->_label.textColor = color; + self->_window.backgroundColor = backgroundColor; + self->_window.hidden = NO; }); } @@ -101,18 +101,18 @@ - (void)setBridge:(RCTBridge *)bridge dispatch_async(dispatch_get_main_queue(), ^{ const NSTimeInterval MIN_PRESENTED_TIME = 0.6; - NSTimeInterval presentedTime = [[NSDate date] timeIntervalSinceDate:_showDate]; + NSTimeInterval presentedTime = [[NSDate date] timeIntervalSinceDate:self->_showDate]; NSTimeInterval delay = MAX(0, MIN_PRESENTED_TIME - presentedTime); - CGRect windowFrame = _window.frame; + CGRect windowFrame = self->_window.frame; [UIView animateWithDuration:0.25 delay:delay options:0 animations:^{ - _window.frame = CGRectOffset(windowFrame, 0, -windowFrame.size.height); + self->_window.frame = CGRectOffset(windowFrame, 0, -windowFrame.size.height); } completion:^(__unused BOOL finished) { - _window.frame = windowFrame; - _window.hidden = YES; - _window = nil; + self->_window.frame = windowFrame; + self->_window.hidden = YES; + self->_window = nil; }]; }); } diff --git a/React/Modules/RCTDevMenu.m b/React/Modules/RCTDevMenu.m index 0b2fdaa9e9faa0..3708028aa26bd7 100644 --- a/React/Modules/RCTDevMenu.m +++ b/React/Modules/RCTDevMenu.m @@ -192,12 +192,12 @@ - (instancetype)init static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - _executorOverride = [_defaults objectForKey:@"executor-override"]; + self->_executorOverride = [self->_defaults objectForKey:@"executor-override"]; }); // Delay setup until after Bridge init dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf updateSettings:_settings]; + [weakSelf updateSettings:self->_settings]; [weakSelf connectPackager]; }); @@ -389,12 +389,12 @@ - (void)jsLoaded:(NSNotification *)notification dispatch_async(dispatch_get_main_queue(), ^{ // Hit these setters again after bridge has finished loading - self.profilingEnabled = _profilingEnabled; - self.liveReloadEnabled = _liveReloadEnabled; - self.executorClass = _executorClass; + self.profilingEnabled = self->_profilingEnabled; + self.liveReloadEnabled = self->_liveReloadEnabled; + self.executorClass = self->_executorClass; // Inspector can only be shown after JS has loaded - if ([_settings[@"showInspector"] boolValue]) { + if ([self->_settings[@"showInspector"] boolValue]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self.bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil]; @@ -457,8 +457,8 @@ - (void)addItem:(RCTDevMenuItem *)item if (!jsDebuggingExecutorClass) { [items addObject:[RCTDevMenuItem buttonItemWithTitle:[NSString stringWithFormat:@"%@ Debugger Unavailable", _webSocketExecutorName] handler:^{ UIAlertView *alert = RCTAlertView( - [NSString stringWithFormat:@"%@ Debugger Unavailable", _webSocketExecutorName], - [NSString stringWithFormat:@"You need to include the RCTWebSocket library to enable %@ debugging", _webSocketExecutorName], + [NSString stringWithFormat:@"%@ Debugger Unavailable", self->_webSocketExecutorName], + [NSString stringWithFormat:@"You need to include the RCTWebSocket library to enable %@ debugging", self->_webSocketExecutorName], nil, @"OK", nil); @@ -476,19 +476,19 @@ - (void)addItem:(RCTDevMenuItem *)item if (_liveReloadURL) { NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; [items addObject:[RCTDevMenuItem buttonItemWithTitle:liveReloadTitle handler:^{ - weakSelf.liveReloadEnabled = !_liveReloadEnabled; + weakSelf.liveReloadEnabled = !self->_liveReloadEnabled; }]]; NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Systrace" : @"Start Systrace"; [items addObject:[RCTDevMenuItem buttonItemWithTitle:profilingTitle handler:^{ - weakSelf.profilingEnabled = !_profilingEnabled; + weakSelf.profilingEnabled = !self->_profilingEnabled; }]]; } if ([self hotLoadingAvailable]) { NSString *hotLoadingTitle = _hotLoadingEnabled ? @"Disable Hot Reloading" : @"Enable Hot Reloading"; [items addObject:[RCTDevMenuItem buttonItemWithTitle:hotLoadingTitle handler:^{ - weakSelf.hotLoadingEnabled = !_hotLoadingEnabled; + weakSelf.hotLoadingEnabled = !self->_hotLoadingEnabled; }]]; } @@ -576,7 +576,7 @@ - (void)setProfilingEnabled:(BOOL)enabled [_bridge startProfiling]; } else { [_bridge stopProfiling:^(NSData *logData) { - RCTProfileSendResult(_bridge, @"systrace", logData); + RCTProfileSendResult(self->_bridge, @"systrace", logData); }]; } } diff --git a/React/Modules/RCTRedBox.m b/React/Modules/RCTRedBox.m index ab0a985f31904a..92c3c541fc6aa1 100644 --- a/React/Modules/RCTRedBox.m +++ b/React/Modules/RCTRedBox.m @@ -359,18 +359,18 @@ - (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate { dispatch_async(dispatch_get_main_queue(), ^{ - if (!_window) { - _window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - _window.actionDelegate = self; + if (!self->_window) { + self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + self->_window.actionDelegate = self; } - [_window showErrorMessage:message withStack:stack isUpdate:isUpdate]; + [self->_window showErrorMessage:message withStack:stack isUpdate:isUpdate]; }); } RCT_EXPORT_METHOD(dismiss) { dispatch_async(dispatch_get_main_queue(), ^{ - [_window dismiss]; + [self->_window dismiss]; }); } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 52d9fe0b207821..2ad81d6d35cdc0 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -271,15 +271,15 @@ - (void)invalidate dispatch_async(dispatch_get_main_queue(), ^{ RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"UIManager invalidate", nil); - for (NSNumber *rootViewTag in _rootViewTags) { - [(id)_viewRegistry[rootViewTag] invalidate]; + for (NSNumber *rootViewTag in self->_rootViewTags) { + [(id)self->_viewRegistry[rootViewTag] invalidate]; } - _rootViewTags = nil; - _shadowViewRegistry = nil; - _viewRegistry = nil; - _bridgeTransactionListeners = nil; - _bridge = nil; + self->_rootViewTags = nil; + self->_shadowViewRegistry = nil; + self->_viewRegistry = nil; + self->_bridgeTransactionListeners = nil; + self->_bridge = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil); @@ -386,7 +386,7 @@ - (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSize __weak RCTUIManager *weakSelf = self; dispatch_async(RCTGetUIManagerQueue(), ^{ RCTUIManager *strongSelf = weakSelf; - if (!_viewRegistry) { + if (!self->_viewRegistry) { return; } RCTRootShadowView *shadowView = [RCTRootShadowView new]; @@ -426,7 +426,7 @@ - (void)setFrame:(CGRect)frame forView:(UIView *)view NSNumber *reactTag = view.reactTag; dispatch_async(RCTGetUIManagerQueue(), ^{ - RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; + RCTShadowView *shadowView = self->_shadowViewRegistry[reactTag]; RCTAssert(shadowView != nil, @"Could not locate shadow view with tag #%@", reactTag); BOOL dirtyLayout = NO; @@ -459,7 +459,7 @@ - (void)setIntrinsicContentSize:(CGSize)size forView:(UIView *)view NSNumber *reactTag = view.reactTag; dispatch_async(RCTGetUIManagerQueue(), ^{ - RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; + RCTShadowView *shadowView = self->_shadowViewRegistry[reactTag]; RCTAssert(shadowView != nil, @"Could not locate root view with tag #%@", reactTag); shadowView.intrinsicContentSize = size; @@ -477,7 +477,7 @@ - (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view __weak RCTUIManager *weakSelf = self; dispatch_async(RCTGetUIManagerQueue(), ^{ RCTUIManager *strongSelf = weakSelf; - if (!_viewRegistry) { + if (!self->_viewRegistry) { return; } RCTShadowView *shadowView = strongSelf->_shadowViewRegistry[reactTag]; @@ -502,8 +502,8 @@ - (void)_purgeChildren:(NSArray> *)children } [registry removeObjectForKey:subview.reactTag]; - if (registry == (NSMutableDictionary> *)_viewRegistry) { - [_bridgeTransactionListeners removeObject:subview]; + if (registry == (NSMutableDictionary> *)self->_viewRegistry) { + [self->_bridgeTransactionListeners removeObject:subview]; } }); } @@ -609,7 +609,7 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView * CGSize contentSize = shadowView.frame.size; dispatch_async(dispatch_get_main_queue(), ^{ - UIView *view = _viewRegistry[reactTag]; + UIView *view = self->_viewRegistry[reactTag]; RCTAssert(view != nil, @"view (for ID %@) not found", reactTag); RCTRootView *rootView = (RCTRootView *)[view superview]; @@ -802,7 +802,7 @@ - (void)_removeChildren:(NSArray *)children void (^completion)(BOOL) = ^(BOOL finished) { completionsCalled++; - [_viewsToBeDeleted removeObject:removedChild]; + [self->_viewsToBeDeleted removeObject:removedChild]; [container removeReactSubview:removedChild]; if (animation.callback && completionsCalled == children.count) { diff --git a/React/Profiler/RCTFPSGraph.m b/React/Profiler/RCTFPSGraph.m index 0dbbe9f286fe0b..0b1d1e5ed85ea7 100644 --- a/React/Profiler/RCTFPSGraph.m +++ b/React/Profiler/RCTFPSGraph.m @@ -97,7 +97,7 @@ - (void)onTick:(NSTimeInterval)timestamp _maxFPS = MAX(_maxFPS, _FPS); dispatch_async(dispatch_get_main_queue(), ^{ - _label.text = [NSString stringWithFormat:@"%lu", (unsigned long)_FPS]; + self->_label.text = [NSString stringWithFormat:@"%lu", (unsigned long)self->_FPS]; }); CGFloat scale = 60.0 / _height; diff --git a/React/Profiler/RCTPerfMonitor.m b/React/Profiler/RCTPerfMonitor.m index 5c0ac165544095..5f2304cba66d1d 100644 --- a/React/Profiler/RCTPerfMonitor.m +++ b/React/Profiler/RCTPerfMonitor.m @@ -329,9 +329,9 @@ - (void)show [self.container addSubview:self.jsGraph]; [self.container addSubview:self.jsGraphLabel]; [executor executeBlockOnJavaScriptQueue:^{ - _jsDisplayLink = [CADisplayLink displayLinkWithTarget:self + self->_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(threadUpdate:)]; - [_jsDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] + [self->_jsDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; }]; } @@ -398,7 +398,7 @@ - (void)redirectLogs const void *buffer, size_t size ) { - write(_stderr, buffer, size); + write(self->_stderr, buffer, size); NSString *log = [[NSString alloc] initWithBytes:buffer length:size @@ -495,8 +495,8 @@ - (void)tap [UIView animateWithDuration:.25 animations:^{ CGRect tmp = self.container.frame; - self.container.frame = _storedMonitorFrame; - _storedMonitorFrame = tmp; + self.container.frame = self->_storedMonitorFrame; + self->_storedMonitorFrame = tmp; }]; } diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index d22b41e33c68e5..97da98cc764f7f 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -343,7 +343,7 @@ - (void)setProps:(NSDictionary *)props forView:(id } [props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) { - [self propBlockForKey:key inDictionary:_viewPropBlocks](view, json); + [self propBlockForKey:key inDictionary:self->_viewPropBlocks](view, json); }]; if ([view respondsToSelector:@selector(didSetProps:)]) { @@ -358,7 +358,7 @@ - (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowV } [props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) { - [self propBlockForKey:key inDictionary:_shadowPropBlocks](shadowView, json); + [self propBlockForKey:key inDictionary:self->_shadowPropBlocks](shadowView, json); }]; if ([shadowView respondsToSelector:@selector(didSetProps:)]) { diff --git a/React/Views/RCTMap.m b/React/Views/RCTMap.m index fc071ec024f4a6..d620419dc33848 100644 --- a/React/Views/RCTMap.m +++ b/React/Views/RCTMap.m @@ -61,18 +61,18 @@ - (void)layoutSubviews if (_legalLabel) { dispatch_async(dispatch_get_main_queue(), ^{ - CGRect frame = _legalLabel.frame; - if (_legalLabelInsets.left) { - frame.origin.x = _legalLabelInsets.left; - } else if (_legalLabelInsets.right) { - frame.origin.x = self.frame.size.width - _legalLabelInsets.right - frame.size.width; + CGRect frame = self->_legalLabel.frame; + if (self->_legalLabelInsets.left) { + frame.origin.x = self->_legalLabelInsets.left; + } else if (self->_legalLabelInsets.right) { + frame.origin.x = self.frame.size.width - self->_legalLabelInsets.right - frame.size.width; } - if (_legalLabelInsets.top) { - frame.origin.y = _legalLabelInsets.top; - } else if (_legalLabelInsets.bottom) { - frame.origin.y = self.frame.size.height - _legalLabelInsets.bottom - frame.size.height; + if (self->_legalLabelInsets.top) { + frame.origin.y = self->_legalLabelInsets.top; + } else if (self->_legalLabelInsets.bottom) { + frame.origin.y = self.frame.size.height - self->_legalLabelInsets.bottom - frame.size.height; } - _legalLabel.frame = frame; + self->_legalLabel.frame = frame; }); } } diff --git a/React/Views/RCTModalHostView.m b/React/Views/RCTModalHostView.m index 3dce5ed759910b..5b2f954ef0c863 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -101,8 +101,8 @@ - (void)didMoveToWindow _modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; } [self.reactViewController presentViewController:_modalViewController animated:[self hasAnimationType] completion:^{ - if (_onShow) { - _onShow(nil); + if (self->_onShow) { + self->_onShow(nil); } }]; _isPresented = YES; diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index eb19b1f6c36579..64de186799bbe7 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -395,24 +395,24 @@ - (void)navigationController:(UINavigationController *)navigationController (RCTWrapperViewController *)[context viewControllerForKey:UITransitionContextToViewControllerKey]; // This may be triggered by a navigation controller unrelated to me: if so, ignore. - if (fromController.navigationController != _navigationController || - toController.navigationController != _navigationController) { + if (fromController.navigationController != self->_navigationController || + toController.navigationController != self->_navigationController) { return; } NSUInteger indexOfFrom = [self.reactSubviews indexOfObject:fromController.navItem]; NSUInteger indexOfTo = [self.reactSubviews indexOfObject:toController.navItem]; CGFloat destination = indexOfFrom < indexOfTo ? 1.0 : -1.0; - _dummyView.frame = (CGRect){{destination, 0}, CGSizeZero}; - _currentlyTransitioningFrom = indexOfFrom; - _currentlyTransitioningTo = indexOfTo; + self->_dummyView.frame = (CGRect){{destination, 0}, CGSizeZero}; + self->_currentlyTransitioningFrom = indexOfFrom; + self->_currentlyTransitioningTo = indexOfTo; self.paused = NO; } completion:^(__unused id context) { [weakSelf freeLock]; - _currentlyTransitioningFrom = 0; - _currentlyTransitioningTo = 0; - _dummyView.frame = CGRectZero; + self->_currentlyTransitioningFrom = 0; + self->_currentlyTransitioningTo = 0; + self->_dummyView.frame = CGRectZero; self.paused = YES; // Reset the parallel position tracker }]; diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index e65e900f2eb255..60ac1bff15fb89 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -653,12 +653,12 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView // Check if new or changed CGRect newFrame = subview.frame; BOOL frameChanged = NO; - if (_cachedChildFrames.count <= idx) { + if (self->_cachedChildFrames.count <= idx) { frameChanged = YES; - [_cachedChildFrames addObject:[NSValue valueWithCGRect:newFrame]]; - } else if (!CGRectEqualToRect(newFrame, [_cachedChildFrames[idx] CGRectValue])) { + [self->_cachedChildFrames addObject:[NSValue valueWithCGRect:newFrame]]; + } else if (!CGRectEqualToRect(newFrame, [self->_cachedChildFrames[idx] CGRectValue])) { frameChanged = YES; - _cachedChildFrames[idx] = [NSValue valueWithCGRect:newFrame]; + self->_cachedChildFrames[idx] = [NSValue valueWithCGRect:newFrame]; } // Create JS frame object diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index a15e0e7ada57d7..2ff78efc187a2e 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -185,7 +185,7 @@ - (void)applyLayoutToChildren:(css_node_t *)node _didUpdateSubviews = NO; [self didUpdateReactSubviews]; [applierBlocks addObject:^(NSDictionary *viewRegistry) { - UIView *view = viewRegistry[_reactTag]; + UIView *view = viewRegistry[self->_reactTag]; [view clearSortedSubviews]; [view didUpdateReactSubviews]; }]; @@ -195,7 +195,7 @@ - (void)applyLayoutToChildren:(css_node_t *)node UIColor *parentBackgroundColor = parentProperties[RCTBackgroundColorProp]; if (parentBackgroundColor) { [applierBlocks addObject:^(NSDictionary *viewRegistry) { - UIView *view = viewRegistry[_reactTag]; + UIView *view = viewRegistry[self->_reactTag]; [view reactSetInheritedBackgroundColor:parentBackgroundColor]; }]; } diff --git a/React/Views/RCTTabBar.m b/React/Views/RCTTabBar.m index d544d75b2efa48..e6cb93a5a3b97a 100644 --- a/React/Views/RCTTabBar.m +++ b/React/Views/RCTTabBar.m @@ -107,16 +107,16 @@ - (void)reactBridgeDidFinishTransaction [self.reactSubviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger index, __unused BOOL *stop) { RCTTabBarItem *tab = (RCTTabBarItem *)view; - UIViewController *controller = _tabController.viewControllers[index]; - if (_unselectedTintColor) { - [tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: _unselectedTintColor} forState:UIControlStateNormal]; + UIViewController *controller = self->_tabController.viewControllers[index]; + if (self->_unselectedTintColor) { + [tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self->_unselectedTintColor} forState:UIControlStateNormal]; } [tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self.tintColor} forState:UIControlStateSelected]; controller.tabBarItem = tab.barItem; if (tab.selected) { - _tabController.selectedViewController = controller; + self->_tabController.selectedViewController = controller; } }]; } From f51fc36ebc2efe85f736b4f70cdc363402506c00 Mon Sep 17 00:00:00 2001 From: Jan Monschke Date: Thu, 7 Jul 2016 13:08:00 -0700 Subject: [PATCH 024/241] remove unneeded preserve_paths declaration Summary: The subspec for `RTCAnimation` defines a `preserve_paths` attribute of `Libraries/NativeAnimation/*.js` (https://github.com/facebook/react-native/blob/master/React.podspec#L60) but there is no JavaScript file in that repository. Linting the podspec therefore fails. The fix was to simply remove `preserve_paths` for this subspec. Closes https://github.com/facebook/react-native/pull/8626 Differential Revision: D3529959 fbshipit-source-id: b187f6ce3898493d9f6a03960caf5ec25c9e4ac2 --- React.podspec | 1 - 1 file changed, 1 deletion(-) diff --git a/React.podspec b/React.podspec index 24c366dc99e158..77b01c46a22bce 100644 --- a/React.podspec +++ b/React.podspec @@ -57,7 +57,6 @@ Pod::Spec.new do |s| s.subspec 'RCTAnimation' do |ss| ss.dependency 'React/Core' ss.source_files = "Libraries/NativeAnimation/{Nodes/*,*}.{h,m}" - ss.preserve_paths = "Libraries/NativeAnimation/*.js" end s.subspec 'RCTCameraRoll' do |ss| From 5323b77fba9ea61c01208c81cce4a0cba37aeb37 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 13:31:14 -0700 Subject: [PATCH 025/241] Expose a way to get the JSContext from a RCTJSCExecutor Reviewed By: javache Differential Revision: D3517664 fbshipit-source-id: cafda7eccbf25f6e197ba9bd18e82c814f08e3bb --- React/Executors/RCTJSCExecutor.h | 5 +++++ React/Executors/RCTJSCExecutor.mm | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/React/Executors/RCTJSCExecutor.h b/React/Executors/RCTJSCExecutor.h index db690042e4d94c..f450277c005b6a 100644 --- a/React/Executors/RCTJSCExecutor.h +++ b/React/Executors/RCTJSCExecutor.h @@ -51,4 +51,9 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification; */ - (NSError *)convertJSErrorToNSError:(JSValueRef)jsError context:(JSContextRef)context; +/** + * Returns the underlying JSContext. + */ +- (JSContext *)underlyingJSContext; + @end diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 090c4ae9448823..e3743c9e1aa8cb 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -318,6 +318,11 @@ - (RCTJavaScriptContext *)context return _context; } +- (JSContext *)underlyingJSContext +{ + return self.context.context; +} + - (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block { __weak RCTJSCExecutor *weakSelf = self; From b3ac5f06d235dff5f879e4eaa5a4366096cf5c34 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 13:31:19 -0700 Subject: [PATCH 026/241] Close RAM bundle as soon as we read the magic number Summary: No need to keep it open; it just makes it harder to reason about error handling. Reviewed By: majak Differential Revision: D3518200 fbshipit-source-id: dc1af6eb0f75de7e9f73513ed1dd522048f76670 --- React/Base/RCTJavaScriptLoader.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 8b6b0b750e4d1e..8ea430702dba96 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -56,8 +56,9 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp } uint32_t magicNumber; - if (fread(&magicNumber, sizeof(magicNumber), 1, bundle) != 1) { - fclose(bundle); + size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); + fclose(bundle); + if (readResult != 1) { onComplete(RCTErrorWithMessage(@"Error reading bundle"), source, 0); return; } @@ -81,7 +82,6 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp sourceLength = source.length; } - fclose(bundle); onComplete(error, source, sourceLength); }); return; From 77752a0399070a71fd62b4e370c8e27f838a4378 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 13:31:20 -0700 Subject: [PATCH 027/241] Move locals closer to point they are used Summary: No need to have these way at the top; they're not used until later. Reviewed By: majak Differential Revision: D3518364 fbshipit-source-id: 3e7461665e90dea5c6d323d45b1ffb11fb610b09 --- React/Base/RCTJavaScriptLoader.m | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 8ea430702dba96..c4341844f9553e 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -42,16 +42,13 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp // Load local script file if (scriptURL.fileURL) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error = nil; - NSData *source = nil; - // Load the first 4 bytes to check if the bundle is regular or RAM ("Random Access Modules" bundle). // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. // The benefit of RAM bundle over a regular bundle is that we can lazily inject // modules into JSC as they're required. FILE *bundle = fopen(scriptURL.path.UTF8String, "r"); if (!bundle) { - onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), source, 0); + onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), nil, 0); return; } @@ -59,12 +56,14 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); fclose(bundle); if (readResult != 1) { - onComplete(RCTErrorWithMessage(@"Error reading bundle"), source, 0); + onComplete(RCTErrorWithMessage(@"Error reading bundle"), nil, 0); return; } magicNumber = NSSwapLittleIntToHost(magicNumber); + NSError *error = nil; + NSData *source = nil; int64_t sourceLength = 0; if (magicNumber == RCTRAMBundleMagicNumber) { source = [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; From 65120d70527fc5c4ec958dae5a61ea5004417291 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 13:31:21 -0700 Subject: [PATCH 028/241] Avoid dispatching to the global queue to read the 4 byte RAM bundle magic number Summary: Reading four bytes is not slow. Don't bother dispatching for that. Reviewed By: javache Differential Revision: D3518405 fbshipit-source-id: 910079cec2a1f624dd71760438765bd035055229 --- React/Base/RCTJavaScriptLoader.m | 66 ++++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index c4341844f9553e..fbc5cc1c9a008a 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -41,47 +41,47 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp // Load local script file if (scriptURL.fileURL) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - // Load the first 4 bytes to check if the bundle is regular or RAM ("Random Access Modules" bundle). - // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. - // The benefit of RAM bundle over a regular bundle is that we can lazily inject - // modules into JSC as they're required. - FILE *bundle = fopen(scriptURL.path.UTF8String, "r"); - if (!bundle) { - onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), nil, 0); - return; - } + // Load the first 4 bytes to check if the bundle is regular or RAM ("Random Access Modules" bundle). + // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. + // The benefit of RAM bundle over a regular bundle is that we can lazily inject + // modules into JSC as they're required. + FILE *bundle = fopen(scriptURL.path.UTF8String, "r"); + if (!bundle) { + onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), nil, 0); + return; + } - uint32_t magicNumber; - size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); - fclose(bundle); - if (readResult != 1) { - onComplete(RCTErrorWithMessage(@"Error reading bundle"), nil, 0); - return; - } + uint32_t magicNumber; + size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); + fclose(bundle); + if (readResult != 1) { + onComplete(RCTErrorWithMessage(@"Error reading bundle"), nil, 0); + return; + } - magicNumber = NSSwapLittleIntToHost(magicNumber); + magicNumber = NSSwapLittleIntToHost(magicNumber); + if (magicNumber == RCTRAMBundleMagicNumber) { + NSData *source = [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; NSError *error = nil; - NSData *source = nil; int64_t sourceLength = 0; - if (magicNumber == RCTRAMBundleMagicNumber) { - source = [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; - - struct stat statInfo; - if (stat(scriptURL.path.UTF8String, &statInfo) != 0) { - error = RCTErrorWithMessage(@"Error reading bundle"); - } else { - sourceLength = statInfo.st_size; - } + + struct stat statInfo; + if (stat(scriptURL.path.UTF8String, &statInfo) != 0) { + error = RCTErrorWithMessage(@"Error reading bundle"); } else { - source = [NSData dataWithContentsOfFile:scriptURL.path - options:NSDataReadingMappedIfSafe - error:&error]; - sourceLength = source.length; + sourceLength = statInfo.st_size; } - onComplete(error, source, sourceLength); + } + + // Reading in a large bundle can be slow. Dispatch to the background queue to do it. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSError *error = nil; + NSData *source = [NSData dataWithContentsOfFile:scriptURL.path + options:NSDataReadingMappedIfSafe + error:&error]; + onComplete(error, source, source.length); }); return; } From a203cf47913e89f42793c4cf7ba630e465e3d3d5 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 13:31:23 -0700 Subject: [PATCH 029/241] Make addSynchronousHookWithName private Summary: It's not widely used, and you can do something equivalent anyway by using existing public API. Reviewed By: javache Differential Revision: D3518896 fbshipit-source-id: 6995a5d840aecfff4ffd78ac43f3f592a4f47f91 --- React/Base/RCTJavaScriptExecutor.h | 6 ------ React/Executors/RCTJSCExecutor.mm | 30 +++++++++++++++--------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index 2e0b80860d001e..832b78e3cddaa8 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -85,10 +85,4 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); */ - (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block; -/** - * For executors that support it, this method can be used to add a synchronous - * callback function for communicating with the javascript context. - */ -- (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block; - @end diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index e3743c9e1aa8cb..b4024e619d9d9f 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -323,7 +323,7 @@ - (JSContext *)underlyingJSContext return self.context.context; } -- (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block +- (void)_addSynchronousHookWithName:(NSString *)name usingBlock:(id)block { __weak RCTJSCExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:^{ @@ -364,9 +364,9 @@ - (void)setUp } }]; - [self addSynchronousHookWithName:@"noop" usingBlock:^{}]; + [self _addSynchronousHookWithName:@"noop" usingBlock:^{}]; - [self addSynchronousHookWithName:@"nativeLoggingHook" usingBlock:^(NSString *message, NSNumber *logLevel) { + [self _addSynchronousHookWithName:@"nativeLoggingHook" usingBlock:^(NSString *message, NSNumber *logLevel) { RCTLogLevel level = RCTLogLevelInfo; if (logLevel) { level = MAX(level, (RCTLogLevel)logLevel.integerValue); @@ -375,7 +375,7 @@ - (void)setUp _RCTLogJavaScriptInternal(level, message); }]; - [self addSynchronousHookWithName:@"nativeRequireModuleConfig" usingBlock:^NSString *(NSString *moduleName) { + [self _addSynchronousHookWithName:@"nativeRequireModuleConfig" usingBlock:^NSString *(NSString *moduleName) { RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf.valid) { return nil; @@ -388,7 +388,7 @@ - (void)setUp return result; }]; - [self addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray *calls){ + [self _addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray *calls){ RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf.valid || !calls) { return; @@ -399,18 +399,18 @@ - (void)setUp RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); }]; - [self addSynchronousHookWithName:@"nativePerformanceNow" usingBlock:^{ + [self _addSynchronousHookWithName:@"nativePerformanceNow" usingBlock:^{ return @(CACurrentMediaTime() * 1000); }]; #if RCT_PROFILE if (RCTProfileIsProfiling()) { // Cheating, since it's not a "hook", but meh - [self addSynchronousHookWithName:@"__RCTProfileIsProfiling" usingBlock:@YES]; + [self _addSynchronousHookWithName:@"__RCTProfileIsProfiling" usingBlock:@YES]; } _cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); - [self addSynchronousHookWithName:@"nativeTraceBeginAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) { + [self _addSynchronousHookWithName:@"nativeTraceBeginAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) { RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf) { return; @@ -419,7 +419,7 @@ - (void)setUp CFDictionarySetValue(strongSelf->_cookieMap, (const void *)cookie, (const void *)newCookie); }]; - [self addSynchronousHookWithName:@"nativeTraceEndAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) { + [self _addSynchronousHookWithName:@"nativeTraceEndAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) { RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf) { return; @@ -429,7 +429,7 @@ - (void)setUp CFDictionaryRemoveValue(strongSelf->_cookieMap, (const void *)cookie); }]; - [self addSynchronousHookWithName:@"nativeTraceBeginSection" usingBlock:^(NSNumber *tag, NSString *profileName, NSDictionary *args) { + [self _addSynchronousHookWithName:@"nativeTraceBeginSection" usingBlock:^(NSNumber *tag, NSString *profileName, NSDictionary *args) { static int profileCounter = 1; if (!profileName) { profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++]; @@ -438,7 +438,7 @@ - (void)setUp RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, args); }]; - [self addSynchronousHookWithName:@"nativeTraceEndSection" usingBlock:^(NSNumber *tag) { + [self _addSynchronousHookWithName:@"nativeTraceEndSection" usingBlock:^(NSNumber *tag) { RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); }]; @@ -447,7 +447,7 @@ - (void)setUp _bridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); #endif _bridge.flowIDMapLock = [NSLock new]; - [self addSynchronousHookWithName:@"nativeTraceBeginAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + [self _addSynchronousHookWithName:@"nativeTraceBeginAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { if (RCTProfileIsProfiling()) { [weakBridge.flowIDMapLock lock]; int64_t newCookie = [_RCTProfileBeginFlowEvent() longLongValue]; @@ -456,7 +456,7 @@ - (void)setUp } }]; - [self addSynchronousHookWithName:@"nativeTraceEndAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + [self _addSynchronousHookWithName:@"nativeTraceEndAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { if (RCTProfileIsProfiling()) { [weakBridge.flowIDMapLock lock]; int64_t newCookie = (int64_t)CFDictionaryGetValue(weakBridge.flowIDMap, (const void *)cookie); @@ -486,7 +486,7 @@ - (void)setUp }]; // Inject handler used by HMR - [self addSynchronousHookWithName:@"nativeInjectHMRUpdate" usingBlock:^(NSString *sourceCode, NSString *sourceCodeURL) { + [self _addSynchronousHookWithName:@"nativeInjectHMRUpdate" usingBlock:^(NSString *sourceCode, NSString *sourceCodeURL) { RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf.valid) { return; @@ -803,7 +803,7 @@ - (void)registerNativeRequire [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; __weak RCTJSCExecutor *weakSelf = self; - [self addSynchronousHookWithName:@"nativeRequire" usingBlock:^(NSNumber *moduleID) { + [self _addSynchronousHookWithName:@"nativeRequire" usingBlock:^(NSNumber *moduleID) { RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf || !moduleID) { return; From 7e1ea4837973766f7c7cd66f7c85beb8f80447e1 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 13:31:24 -0700 Subject: [PATCH 030/241] Make executeAsyncBlockOnJavaScriptQueue required Summary: This leaves no optional methods on `RCTJavaScriptExecutor`, which is certainly a good thing. Reviewed By: majak Differential Revision: D3518915 fbshipit-source-id: e606b9076c3299f81a225a181ea244148a1832cb --- React/Base/RCTBatchedBridge.m | 11 ++--------- React/Base/RCTJavaScriptExecutor.h | 2 -- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 2db2c4bbc88ecf..bad2a4de3caca9 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -747,18 +747,11 @@ - (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args - (void)_immediatelyCallTimer:(NSNumber *)timer { RCTAssertJSThread(); - - dispatch_block_t block = ^{ + [_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:^{ [self _actuallyInvokeAndProcessModule:@"JSTimersExecution" method:@"callTimers" arguments:@[@[timer]]]; - }; - - if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) { - [_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block]; - } else { - [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; - } + }]; } - (void)enqueueApplicationScript:(NSData *)script diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index 832b78e3cddaa8..aa47d3cae1952d 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -76,8 +76,6 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); */ - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block; -@optional - /** * Special case for Timers + ContextExecutor - instead of the default * if jsthread then call else dispatch call on jsthread From 5e10d3e3858a5dfdb2f33bda43d51ed39308db85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Dorrestijn?= Date: Thu, 7 Jul 2016 14:11:00 -0700 Subject: [PATCH 031/241] Fixed the diagram in the FBPortForwarding README Summary: ![screen shot 2016-07-07 at 17 11 05](https://cloud.githubusercontent.com/assets/570297/16658236/e3ae8f38-4465-11e6-9e24-2d6a1bf11ac6.png) ![screen shot 2016-07-07 at 17 11 12](https://cloud.githubusercontent.com/assets/570297/16658235/e3ab0fb6-4465-11e6-991b-a4dbcd675322.png) Closes https://github.com/facebook/react-native/pull/8632 Differential Revision: D3529638 fbshipit-source-id: 7d7dba00975d738e32a7340cfe4d270cc4dc7686 --- Tools/FBPortForwarding/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/FBPortForwarding/README.md b/Tools/FBPortForwarding/README.md index b67db7a06412d5..790387c49b3207 100644 --- a/Tools/FBPortForwarding/README.md +++ b/Tools/FBPortForwarding/README.md @@ -34,7 +34,7 @@ to local port and forwards all communication back via the same peertalk channel. | | incoming +----------------+ | | +--------------+ connections |Proxy Server | | | |Real Server | - ------------->> | | +-------------+ commands | | | + ------------>> | | +-------------+ commands | | | | Port 8081| | create | | stream | | Port 8081| +-+--------------+ +---------> Peertalk <----------+ +-^------------+ | | Channel | ^ From 6565edc9ed80f32ddb9b6f704d26e7cfd5c53c6a Mon Sep 17 00:00:00 2001 From: Jon Green Date: Thu, 7 Jul 2016 14:24:32 -0700 Subject: [PATCH 032/241] Fixing typo Summary: 'iOS' was written but it should be 'Android'. Closes https://github.com/facebook/react-native/pull/8637 Differential Revision: D3530611 Pulled By: JoelMarcey fbshipit-source-id: 04dbb2e2188f3de73f9bc185f18950bc5de5607b --- docs/IntegrationWithExistingApps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/IntegrationWithExistingApps.md b/docs/IntegrationWithExistingApps.md index 05d95838e41dc8..256d66062fabfe 100644 --- a/docs/IntegrationWithExistingApps.md +++ b/docs/IntegrationWithExistingApps.md @@ -61,7 +61,7 @@ The keys to integrating React Native components into your iOS application are to -The keys to integrating React Native components into your iOS application are to: +The keys to integrating React Native components into your Android application are to: 1. Understand what React Native components you want to integrate. 2. Install `react-native` in your Android application root directory to create `node_modules/` directory. From 80c23ae324b207f16dd7facfbb87af91f602616c Mon Sep 17 00:00:00 2001 From: Clement Genzmer Date: Thu, 7 Jul 2016 14:46:12 -0700 Subject: [PATCH 033/241] Reverted commit D3207541 Reviewed By: michalgr Differential Revision: D3207541 fbshipit-source-id: 36366188b7cb74c51d4aa974abd4a648498bcf78 --- .../react/XReactInstanceManagerImpl.java | 5 +- .../react/cxxbridge/CatalystInstanceImpl.java | 2 +- .../react/cxxbridge/JSBundleLoader.java | 9 +- .../jni/xreact/jni/CatalystInstanceImpl.cpp | 125 +----------------- .../jni/xreact/jni/CatalystInstanceImpl.h | 2 +- .../src/main/jni/xreact/jni/OnLoad.cpp | 8 +- ReactAndroid/src/main/jni/xreact/jni/OnLoad.h | 1 - ReactCommon/cxxreact/Executor.h | 64 --------- ReactCommon/cxxreact/JSCExecutor.cpp | 23 ---- ReactCommon/cxxreact/JSCHelpers.cpp | 82 +++++------- ReactCommon/cxxreact/JSCHelpers.h | 12 -- 11 files changed, 46 insertions(+), 287 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java index 42911f885d11b5..e510d129cbb9c6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java @@ -412,12 +412,9 @@ public void run() { } private void recreateReactContextInBackgroundFromBundleFile() { - boolean useLazyBundle = mJSCConfig.getConfigMap().hasKey("useLazyBundle") ? - mJSCConfig.getConfigMap().getBoolean("useLazyBundle") : false; - recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()), - JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile, useLazyBundle)); + JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile)); } /** 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 569ae980ed3476..007495e763cbbf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -161,7 +161,7 @@ private native void initializeBridge(ReactCallback callback, MessageQueueThread moduleQueue, ModuleRegistryHolder registryHolder); - /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean useLazyBundle); + /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL); /* package */ native void loadScriptFromFile(String fileName, String sourceURL); @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java index 7b4d6e49976710..c55b30ce692049 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java @@ -28,18 +28,11 @@ public abstract class JSBundleLoader { public static JSBundleLoader createFileLoader( final Context context, final String fileName) { - return createFileLoader(context, fileName, false); - } - - public static JSBundleLoader createFileLoader( - final Context context, - final String fileName, - final boolean useLazyBundle) { return new JSBundleLoader() { @Override public void loadScript(CatalystInstanceImpl instance) { if (fileName.startsWith("assets://")) { - instance.loadScriptFromAssets(context.getAssets(), fileName, useLazyBundle); + instance.loadScriptFromAssets(context.getAssets(), fileName); } else { instance.loadScriptFromFile(fileName, fileName); } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index 612107b7cc684c..e9bf94140ebf00 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -13,8 +13,6 @@ #include #include -#include - #include #include #include @@ -25,7 +23,6 @@ #include "ModuleRegistryHolder.h" #include "NativeArray.h" #include "JNativeRunnable.h" -#include "OnLoad.h" using namespace facebook::jni; @@ -101,7 +98,7 @@ void CatalystInstanceImpl::registerNatives() { makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge), makeNativeMethod("loadScriptFromAssets", - "(Landroid/content/res/AssetManager;Ljava/lang/String;Z)V", + "(Landroid/content/res/AssetManager;Ljava/lang/String;)V", CatalystInstanceImpl::loadScriptFromAssets), makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile), makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction), @@ -152,132 +149,20 @@ void CatalystInstanceImpl::initializeBridge( mrh->getModuleRegistry()); } -#ifdef WITH_FBJSCEXTENSIONS -static std::unique_ptr loadScriptFromCache( - AAssetManager* manager, - std::string& sourceURL) { - - // 20-byte sha1 as hex - static const size_t HASH_STR_SIZE = 40; - - // load bundle hash from the metadata file in the APK - auto hash = react::loadScriptFromAssets(manager, sourceURL + ".meta"); - auto cacheDir = getApplicationCacheDir() + "/rn-bundle"; - auto encoding = static_cast(hash->c_str()[20]); - - if (mkdir(cacheDir.c_str(), 0755) == -1 && errno != EEXIST) { - throw std::runtime_error("Can't create cache directory"); - } - - if (encoding != JSBigMmapString::Encoding::Ascii) { - throw std::runtime_error("Can't use mmap fastpath for non-ascii bundles"); - } - - // convert hash to string - char hashStr[HASH_STR_SIZE + 1]; - for (size_t i = 0; i < HASH_STR_SIZE; i += 2) { - snprintf(hashStr + i, 3, "%02hhx", hash->c_str()[i / 2] & 0xFF); - } - - // the name of the cached bundle file should be the hash - std::string cachePath = cacheDir + "/" + hashStr; - FILE *cache = fopen(cachePath.c_str(), "r"); - SCOPE_EXIT { if (cache) fclose(cache); }; - - size_t size = 0; - if (cache == NULL) { - // delete old bundle, if there was one. - std::string metaPath = cacheDir + "/meta"; - if (auto meta = fopen(metaPath.c_str(), "r")) { - char oldBundleHash[HASH_STR_SIZE + 1]; - if (fread(oldBundleHash, HASH_STR_SIZE, 1, meta) == HASH_STR_SIZE) { - remove((cacheDir + "/" + oldBundleHash).c_str()); - remove(metaPath.c_str()); - } - fclose(meta); - } - - // load script from the APK and write to temporary file - auto script = react::loadScriptFromAssets(manager, sourceURL); - auto tmpPath = cachePath + "_"; - cache = fopen(tmpPath.c_str(), "w"); - if (!cache) { - throw std::runtime_error("Can't open cache, errno: " + errno); - } - if (fwrite(script->c_str(), 1, script->size(), cache) != size) { - remove(tmpPath.c_str()); - throw std::runtime_error("Failed to unpack bundle"); - } - - // force data to be written to disk - fsync(fileno(cache)); - fclose(cache); - - // move script to final path - atomic operation - if (rename(tmpPath.c_str(), cachePath.c_str())) { - throw std::runtime_error("Failed to update cache, errno: " + errno); - } - - // store the bundle hash in a metadata file - auto meta = fopen(metaPath.c_str(), "w"); - if (!meta) { - throw std::runtime_error("Failed to open metadata file to store bundle hash"); - } - if (fwrite(hashStr, HASH_STR_SIZE, 1, meta) != HASH_STR_SIZE) { - throw std::runtime_error("Failed to write bundle hash to metadata file"); - } - fsync(fileno(meta)); - fclose(meta); - - // return the final written cache - cache = fopen(cachePath.c_str(), "r"); - if (!cache) { - throw std::runtime_error("Cache has been cleared"); - } - } else { - struct stat fileInfo = {0}; - if (fstat(fileno(cache), &fileInfo)) { - throw std::runtime_error("Failed to get cache stats, errno: " + errno); - } - size = fileInfo.st_size; - } - - return folly::make_unique( - dup(fileno(cache)), - size, - reinterpret_cast(hash->c_str()), - encoding); -} -#endif - void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager, - const std::string& assetURL, - bool useLazyBundle) { + const std::string& assetURL) { const int kAssetsLength = 9; // strlen("assets://"); auto sourceURL = assetURL.substr(kAssetsLength); - auto manager = react::extractAssetManager(assetManager); + auto manager = react::extractAssetManager(assetManager); + auto script = react::loadScriptFromAssets(manager, sourceURL); if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) { - auto script = react::loadScriptFromAssets(manager, sourceURL); instance_->loadUnbundle( folly::make_unique(manager, sourceURL), std::move(script), sourceURL); - return; } else { -#ifdef WITH_FBJSCEXTENSIONS - if (useLazyBundle) { - try { - auto script = loadScriptFromCache(manager, sourceURL); - instance_->loadScriptFromString(std::move(script), sourceURL); - return; - } catch (...) { - LOG(WARNING) << "Failed to load bundle as Source Code"; - } - } -#endif - auto script = react::loadScriptFromAssets(manager, sourceURL); - instance_->loadScriptFromString(std::move(script), sourceURL); + instance_->loadScriptFromString(std::move(script), std::move(sourceURL)); } } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index 64835e6b461d1c..1e5f2a894336bc 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -47,7 +47,7 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref jsQueue, jni::alias_ref moduleQueue, ModuleRegistryHolder* mrh); - void loadScriptFromAssets(jobject assetManager, const std::string& assetURL, bool useLazyBundle); + void loadScriptFromAssets(jobject assetManager, const std::string& assetURL); void loadScriptFromFile(jni::alias_ref fileName, const std::string& sourceURL); void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments); void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments); diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp index 67e167b4eaa9c8..952a6b46c4c4ff 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp @@ -51,6 +51,10 @@ static std::string getApplicationDir(const char* methodName) { return getAbsolutePathMethod(dirObj)->toStdString(); } +static std::string getApplicationCacheDir() { + return getApplicationDir("getCacheDir"); +} + static std::string getApplicationPersistentDir() { return getApplicationDir("getFilesDir"); } @@ -158,10 +162,6 @@ class JReactMarker : public JavaClass { } -std::string getApplicationCacheDir() { - return getApplicationDir("getCacheDir"); -} - extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return initialize(vm, [] { // Inject some behavior into react/ diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h index 713d8b582daf36..5cd3c1749e62bf 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h @@ -10,6 +10,5 @@ namespace facebook { namespace react { jmethodID getLogMarkerMethod(); -std::string getApplicationCacheDir(); } // namespace react } // namespace facebook diff --git a/ReactCommon/cxxreact/Executor.h b/ReactCommon/cxxreact/Executor.h index 6b99df0b2cc1d0..e2c5a331b6bf18 100644 --- a/ReactCommon/cxxreact/Executor.h +++ b/ReactCommon/cxxreact/Executor.h @@ -7,8 +7,6 @@ #include #include -#include - #include #include "JSModulesUnbundle.h" @@ -136,68 +134,6 @@ class JSBigBufferString : public facebook::react::JSBigString { size_t m_size; }; -class JSBigMmapString : public JSBigString { -public: - enum class Encoding { - Unknown, - Ascii, - Utf8, - Utf16, - }; - - - JSBigMmapString(int fd, size_t size, const uint8_t sha1[20], Encoding encoding) : - m_fd(fd), - m_size(size), - m_encoding(encoding), - m_str(nullptr) - { - memcpy(m_hash, sha1, 20); - } - - ~JSBigMmapString() { - if (m_str) { - CHECK(munmap((void *)m_str, m_size) != -1); - } - close(m_fd); - } - - bool isAscii() const override { - return m_encoding == Encoding::Ascii; - } - - const char* c_str() const override { - if (!m_str) { - m_str = (const char *)mmap(0, m_size, PROT_READ, MAP_SHARED, m_fd, 0); - CHECK(m_str != MAP_FAILED); - } - return m_str; - } - - size_t size() const override { - return m_size; - } - - int fd() const { - return m_fd; - } - - const uint8_t* hash() const { - return m_hash; - } - - Encoding encoding() const { - return m_encoding; - } - -private: - int m_fd; - size_t m_size; - uint8_t m_hash[20]; - Encoding m_encoding; - mutable const char *m_str; -}; - class JSExecutor { public: /** diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index d26409c2e0bcc7..0b624da12ee5cb 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -253,33 +253,10 @@ void JSCExecutor::terminateOnJSVMThread() { m_context = nullptr; } -#ifdef WITH_FBJSCEXTENSIONS -static void loadApplicationSource( - const JSGlobalContextRef context, - const JSBigMmapString* script, - const std::string& sourceURL) { - String jsSourceURL(sourceURL.c_str()); - bool is8bit = script->encoding() == JSBigMmapString::Encoding::Ascii || script->encoding() == JSBigMmapString::Encoding::Utf8; - JSSourceCodeRef sourceCode = JSCreateSourceCode(script->fd(), script->size(), jsSourceURL, script->hash(), is8bit); - evaluateSourceCode(context, sourceCode, jsSourceURL); - JSReleaseSourceCode(sourceCode); -} -#endif - void JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL) throw(JSException) { SystraceSection s("JSCExecutor::loadApplicationScript", "sourceURL", sourceURL); - #ifdef WITH_FBJSCEXTENSIONS - if (auto source = dynamic_cast(script.get())) { - loadApplicationSource(m_context, source, sourceURL); - bindBridge(); - flush(); - ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); - return; - } - #endif - #ifdef WITH_FBSYSTRACE fbsystrace_begin_section( TRACE_TAG_REACT_CXX_BRIDGE, diff --git a/ReactCommon/cxxreact/JSCHelpers.cpp b/ReactCommon/cxxreact/JSCHelpers.cpp index 194cc473791eac..40554219ddf4ae 100644 --- a/ReactCommon/cxxreact/JSCHelpers.cpp +++ b/ReactCommon/cxxreact/JSCHelpers.cpp @@ -44,63 +44,47 @@ JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef JSValueRef exn, result; result = JSEvaluateScript(context, script, NULL, source, 0, &exn); if (result == nullptr) { - formatAndThrowJSException(context, exn, source); - } - return result; -} - -#if WITH_FBJSCEXTENSIONS -JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) { - JSValueRef exn, result; - result = JSEvaluateSourceCode(context, source, NULL, &exn); - if (result == nullptr) { - formatAndThrowJSException(context, exn, sourceURL); - } - return result; -} -#endif - -void formatAndThrowJSException(JSContextRef context, JSValueRef exn, JSStringRef source) { - Value exception = Value(context, exn); - - std::string exceptionText = exception.toString().str(); - - // The null/empty-ness of source tells us if the JS came from a - // file/resource, or was a constructed statement. The location - // info will include that source, if any. - std::string locationInfo = source != nullptr ? String::ref(source).str() : ""; - Object exObject = exception.asObject(); - auto line = exObject.getProperty("line"); - if (line != nullptr && line.isNumber()) { - if (locationInfo.empty() && line.asInteger() != 1) { - // If there is a non-trivial line number, but there was no - // location info, we include a placeholder, and the line - // number. - locationInfo = folly::to(":", line.asInteger()); - } else if (!locationInfo.empty()) { - // If there is location info, we always include the line - // number, regardless of its value. - locationInfo += folly::to(":", line.asInteger()); + Value exception = Value(context, exn); + + std::string exceptionText = exception.toString().str(); + + // The null/empty-ness of source tells us if the JS came from a + // file/resource, or was a constructed statement. The location + // info will include that source, if any. + std::string locationInfo = source != nullptr ? String::ref(source).str() : ""; + Object exObject = exception.asObject(); + auto line = exObject.getProperty("line"); + if (line != nullptr && line.isNumber()) { + if (locationInfo.empty() && line.asInteger() != 1) { + // If there is a non-trivial line number, but there was no + // location info, we include a placeholder, and the line + // number. + locationInfo = folly::to(":", line.asInteger()); + } else if (!locationInfo.empty()) { + // If there is location info, we always include the line + // number, regardless of its value. + locationInfo += folly::to(":", line.asInteger()); + } } - } - if (!locationInfo.empty()) { - exceptionText += " (" + locationInfo + ")"; - } + if (!locationInfo.empty()) { + exceptionText += " (" + locationInfo + ")"; + } - LOG(ERROR) << "Got JS Exception: " << exceptionText; + LOG(ERROR) << "Got JS Exception: " << exceptionText; - Value jsStack = exObject.getProperty("stack"); - if (jsStack.isNull() || !jsStack.isString()) { - throwJSExecutionException("%s", exceptionText.c_str()); - } else { - LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str(); - throwJSExecutionExceptionWithStack( + Value jsStack = exObject.getProperty("stack"); + if (jsStack.isNull() || !jsStack.isString()) { + throwJSExecutionException("%s", exceptionText.c_str()); + } else { + LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str(); + throwJSExecutionExceptionWithStack( exceptionText.c_str(), jsStack.toString().str().c_str()); + } } + return result; } - JSValueRef makeJSError(JSContextRef ctx, const char *error) { JSValueRef nestedException = nullptr; JSValueRef args[] = { Value(ctx, String(error)) }; diff --git a/ReactCommon/cxxreact/JSCHelpers.h b/ReactCommon/cxxreact/JSCHelpers.h index a55906d560c2d8..3be708164ae028 100644 --- a/ReactCommon/cxxreact/JSCHelpers.h +++ b/ReactCommon/cxxreact/JSCHelpers.h @@ -49,18 +49,6 @@ JSValueRef evaluateScript( JSStringRef script, JSStringRef sourceURL); -#if WITH_FBJSCEXTENSIONS -JSValueRef evaluateSourceCode( - JSContextRef ctx, - JSSourceCodeRef source, - JSStringRef sourceURL); -#endif - -void formatAndThrowJSException( - JSContextRef ctx, - JSValueRef exn, - JSStringRef sourceURL); - JSValueRef makeJSError(JSContextRef ctx, const char *error); JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation); From 96b47eb933f01c03290582396259cd2ef34f21ea Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 16:36:48 -0700 Subject: [PATCH 034/241] Inline _addSynchronousHookWithName Summary: It's just a simple helper. It's actually more readable and performant when inlined. Reviewed By: javache Differential Revision: D3528540 fbshipit-source-id: 8086770f7fd88b40623dc943c715deb4f9fd9262 --- React/Executors/RCTJSCExecutor.mm | 307 ++++++++++++++---------------- 1 file changed, 147 insertions(+), 160 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index b4024e619d9d9f..e1e098ff212ba6 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -323,18 +323,26 @@ - (JSContext *)underlyingJSContext return self.context.context; } -- (void)_addSynchronousHookWithName:(NSString *)name usingBlock:(id)block -{ - __weak RCTJSCExecutor *weakSelf = self; - [self executeBlockOnJavaScriptQueue:^{ - weakSelf.context.context[name] = block; - }]; -} - - (void)setUp { __weak RCTJSCExecutor *weakSelf = self; + _cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); + __weak RCTBridge *weakBridge = _bridge; +#ifndef __clang_analyzer__ + _bridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); +#endif + _bridge.flowIDMapLock = [NSLock new]; + +#if RCT_PROFILE + for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(toggleProfilingFlag:) + name:event + object:nil]; + } +#endif + [self executeBlockOnJavaScriptQueue:^{ RCTJSCExecutor *strongSelf = weakSelf; if (!strongSelf.valid) { @@ -344,162 +352,133 @@ - (void)setUp [strongSelf->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary]; strongSelf->_jscWrapper = RCTJSCWrapperCreate(strongSelf->_useCustomJSCLibrary); [strongSelf->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; - }]; - - [self executeBlockOnJavaScriptQueue:^{ - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid) { - return; - } - - if (strongSelf->_jscWrapper->configureJSContextForIOS == NULL) { - return; - } - - NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; - RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); - if (cachesPath) { - strongSelf->_jscWrapper->configureJSContextForIOS(strongSelf.context.ctx, [cachesPath UTF8String]); + if (strongSelf->_jscWrapper->configureJSContextForIOS != NULL) { + NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; + RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); + if (cachesPath) { + strongSelf->_jscWrapper->configureJSContextForIOS(strongSelf.context.ctx, [cachesPath UTF8String]); + } } - }]; - [self _addSynchronousHookWithName:@"noop" usingBlock:^{}]; + // Synchronous hooks: + JSContext *context = strongSelf.context.context; + context[@"noop"] = ^{}; - [self _addSynchronousHookWithName:@"nativeLoggingHook" usingBlock:^(NSString *message, NSNumber *logLevel) { - RCTLogLevel level = RCTLogLevelInfo; - if (logLevel) { - level = MAX(level, (RCTLogLevel)logLevel.integerValue); - } + context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) { + RCTLogLevel level = RCTLogLevelInfo; + if (logLevel) { + level = MAX(level, (RCTLogLevel)logLevel.integerValue); + } - _RCTLogJavaScriptInternal(level, message); - }]; + _RCTLogJavaScriptInternal(level, message); + }; - [self _addSynchronousHookWithName:@"nativeRequireModuleConfig" usingBlock:^NSString *(NSString *moduleName) { - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid) { - return nil; - } + context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) { + RCTJSCExecutor *strongSelf2 = weakSelf; + if (!strongSelf2.valid) { + return nil; + } - RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeRequireModuleConfig", nil); - NSArray *config = [strongSelf->_bridge configForModuleName:moduleName]; - NSString *result = config ? RCTJSONStringify(config, NULL) : nil; - RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call,config", @{ @"moduleName": moduleName }); - return result; - }]; + RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeRequireModuleConfig", nil); + NSArray *config = [strongSelf2->_bridge configForModuleName:moduleName]; + NSString *result = config ? RCTJSONStringify(config, NULL) : nil; + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call,config", @{ @"moduleName": moduleName }); + return result; + }; - [self _addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray *calls){ - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid || !calls) { - return; - } + context[@"nativeFlushQueueImmediate"] = ^(NSArray *calls){ + RCTJSCExecutor *strongSelf2 = weakSelf; + if (!strongSelf2.valid || !calls) { + return; + } - RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeFlushQueueImmediate", nil); - [strongSelf->_bridge handleBuffer:calls batchEnded:NO]; - RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); - }]; + RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeFlushQueueImmediate", nil); + [strongSelf2->_bridge handleBuffer:calls batchEnded:NO]; + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); + }; - [self _addSynchronousHookWithName:@"nativePerformanceNow" usingBlock:^{ - return @(CACurrentMediaTime() * 1000); - }]; + context[@"nativePerformanceNow"] = ^{ + return @(CACurrentMediaTime() * 1000); + }; #if RCT_PROFILE - if (RCTProfileIsProfiling()) { - // Cheating, since it's not a "hook", but meh - [self _addSynchronousHookWithName:@"__RCTProfileIsProfiling" usingBlock:@YES]; - } - - _cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); - [self _addSynchronousHookWithName:@"nativeTraceBeginAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) { - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil); - CFDictionarySetValue(strongSelf->_cookieMap, (const void *)cookie, (const void *)newCookie); - }]; - - [self _addSynchronousHookWithName:@"nativeTraceEndAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) { - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(strongSelf->_cookieMap, (const void *)cookie); - RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, @"JS async", nil); - CFDictionaryRemoveValue(strongSelf->_cookieMap, (const void *)cookie); - }]; - - [self _addSynchronousHookWithName:@"nativeTraceBeginSection" usingBlock:^(NSNumber *tag, NSString *profileName, NSDictionary *args) { - static int profileCounter = 1; - if (!profileName) { - profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++]; + if (RCTProfileIsProfiling()) { + // Cheating, since it's not a "hook", but meh + context[@"__RCTProfileIsProfiling"] = @YES; } - RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, args); - }]; + context[@"nativeTraceBeginAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { + RCTJSCExecutor *strongSelf2 = weakSelf; + if (!strongSelf2) { + return; + } + NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil); + CFDictionarySetValue(strongSelf2->_cookieMap, (const void *)cookie, (const void *)newCookie); + }; - [self _addSynchronousHookWithName:@"nativeTraceEndSection" usingBlock:^(NSNumber *tag) { - RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); - }]; + context[@"nativeTraceEndAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { + RCTJSCExecutor *strongSelf2 = weakSelf; + if (!strongSelf2) { + return; + } + NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(strongSelf2->_cookieMap, (const void *)cookie); + RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, @"JS async", nil); + CFDictionaryRemoveValue(strongSelf2->_cookieMap, (const void *)cookie); + }; + + context[@"nativeTraceBeginSection"] = ^(NSNumber *tag, NSString *profileName, NSDictionary *args) { + static int profileCounter = 1; + if (!profileName) { + profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++]; + } - __weak RCTBridge *weakBridge = _bridge; -#ifndef __clang_analyzer__ - _bridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); -#endif - _bridge.flowIDMapLock = [NSLock new]; - [self _addSynchronousHookWithName:@"nativeTraceBeginAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { - if (RCTProfileIsProfiling()) { - [weakBridge.flowIDMapLock lock]; - int64_t newCookie = [_RCTProfileBeginFlowEvent() longLongValue]; - CFDictionarySetValue(weakBridge.flowIDMap, (const void *)cookie, (const void *)newCookie); - [weakBridge.flowIDMapLock unlock]; - } - }]; + RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, args); + }; - [self _addSynchronousHookWithName:@"nativeTraceEndAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { - if (RCTProfileIsProfiling()) { - [weakBridge.flowIDMapLock lock]; - int64_t newCookie = (int64_t)CFDictionaryGetValue(weakBridge.flowIDMap, (const void *)cookie); - _RCTProfileEndFlowEvent(@(newCookie)); - CFDictionaryRemoveValue(weakBridge.flowIDMap, (const void *)cookie); - [weakBridge.flowIDMapLock unlock]; - } - }]; + context[@"nativeTraceEndSection"] = ^(NSNumber *tag) { + RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); + }; - for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(toggleProfilingFlag:) - name:event - object:nil]; - } + context[@"nativeTraceBeginAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + if (RCTProfileIsProfiling()) { + [weakBridge.flowIDMapLock lock]; + int64_t newCookie = [_RCTProfileBeginFlowEvent() longLongValue]; + CFDictionarySetValue(weakBridge.flowIDMap, (const void *)cookie, (const void *)newCookie); + [weakBridge.flowIDMapLock unlock]; + } + }; + + context[@"nativeTraceEndAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + if (RCTProfileIsProfiling()) { + [weakBridge.flowIDMapLock lock]; + int64_t newCookie = (int64_t)CFDictionaryGetValue(weakBridge.flowIDMap, (const void *)cookie); + _RCTProfileEndFlowEvent(@(newCookie)); + CFDictionaryRemoveValue(weakBridge.flowIDMap, (const void *)cookie); + [weakBridge.flowIDMapLock unlock]; + } + }; #endif #if RCT_DEV - [self executeBlockOnJavaScriptQueue:^{ - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid) { - return; - } - - JSContext *context = strongSelf.context.context; - RCTInstallJSCProfiler(self->_bridge, context.JSGlobalContextRef); - }]; + RCTInstallJSCProfiler(strongSelf->_bridge, context.JSGlobalContextRef); - // Inject handler used by HMR - [self _addSynchronousHookWithName:@"nativeInjectHMRUpdate" usingBlock:^(NSString *sourceCode, NSString *sourceCodeURL) { - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid) { - return; - } + // Inject handler used by HMR + context[@"nativeInjectHMRUpdate"] = ^(NSString *sourceCode, NSString *sourceCodeURL) { + RCTJSCExecutor *strongSelf2 = weakSelf; + if (!strongSelf2.valid) { + return; + } - RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; - JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString(sourceCode.UTF8String); - JSStringRef jsURL = jscWrapper->JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String); - jscWrapper->JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, NULL); - jscWrapper->JSStringRelease(jsURL); - jscWrapper->JSStringRelease(execJSString); - }]; + RCTJSCWrapper *jscWrapper = strongSelf2->_jscWrapper; + JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString(sourceCode.UTF8String); + JSStringRef jsURL = jscWrapper->JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String); + jscWrapper->JSEvaluateScript(strongSelf2->_context.ctx, execJSString, NULL, jsURL, 0, NULL); + jscWrapper->JSStringRelease(jsURL); + jscWrapper->JSStringRelease(execJSString); + }; #endif + }]; } - (void)toggleProfilingFlag:(NSNotification *)notification @@ -803,35 +782,43 @@ - (void)registerNativeRequire [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; __weak RCTJSCExecutor *weakSelf = self; - [self _addSynchronousHookWithName:@"nativeRequire" usingBlock:^(NSNumber *moduleID) { + [self executeBlockOnJavaScriptQueue:^{ RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf || !moduleID) { + if (!strongSelf.valid) { return; } - [strongSelf->_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; - [strongSelf->_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; - RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, - [@"nativeRequire_" stringByAppendingFormat:@"%@", moduleID], nil); + JSContext *context = strongSelf.context.context; + context[@"nativeRequire"] = ^(NSNumber *moduleID) { + RCTJSCExecutor *strongSelf2 = weakSelf; + if (!strongSelf2 || !moduleID) { + return; + } - const uint32_t ID = [moduleID unsignedIntValue]; + [strongSelf2->_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; + [strongSelf2->_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; + RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, + [@"nativeRequire_" stringByAppendingFormat:@"%@", moduleID], nil); - if (ID < strongSelf->_randomAccessBundle.numTableEntries) { - ModuleData *moduleData = &strongSelf->_randomAccessBundle.table[ID]; - const uint32_t size = NSSwapLittleIntToHost(moduleData->size); + const uint32_t ID = [moduleID unsignedIntValue]; - // sparse entry in the table -- module does not exist or is contained in the startup section - if (size == 0) { - return; - } + if (ID < strongSelf2->_randomAccessBundle.numTableEntries) { + ModuleData *moduleData = &strongSelf2->_randomAccessBundle.table[ID]; + const uint32_t size = NSSwapLittleIntToHost(moduleData->size); - [strongSelf->_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; - executeRandomAccessModule(strongSelf, ID, NSSwapLittleIntToHost(moduleData->offset), size); - } + // sparse entry in the table -- module does not exist or is contained in the startup section + if (size == 0) { + return; + } - RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); - [strongSelf->_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; - }]; + [strongSelf2->_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; + executeRandomAccessModule(strongSelf2, ID, NSSwapLittleIntToHost(moduleData->offset), size); + } + + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); + [strongSelf2->_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; + }; +}]; } static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccessBundleData &randomAccessBundle) From dd5bb7b9e0c7fd31d8eb7707f7e7ef7ea9935c1f Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 16:36:51 -0700 Subject: [PATCH 035/241] Pull native hook registration into class method Summary: Ideally, native hooks should not require any sort of reference to self. Pull all those that fulfill this criteria into a class method (to make it impossible to accidentally capture self). Future diffs will pull more and more hooks into this category. Reviewed By: javache Differential Revision: D3528558 fbshipit-source-id: 270c5bec53674a91ec2129d55e5cad59440a51da --- React/Executors/RCTJSCExecutor.mm | 66 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index e1e098ff212ba6..44e45264264eed 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -363,16 +363,7 @@ - (void)setUp // Synchronous hooks: JSContext *context = strongSelf.context.context; - context[@"noop"] = ^{}; - - context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) { - RCTLogLevel level = RCTLogLevelInfo; - if (logLevel) { - level = MAX(level, (RCTLogLevel)logLevel.integerValue); - } - - _RCTLogJavaScriptInternal(level, message); - }; + [[self class] installSynchronousHooksOnContext:context]; context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) { RCTJSCExecutor *strongSelf2 = weakSelf; @@ -398,16 +389,7 @@ - (void)setUp RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); }; - context[@"nativePerformanceNow"] = ^{ - return @(CACurrentMediaTime() * 1000); - }; - #if RCT_PROFILE - if (RCTProfileIsProfiling()) { - // Cheating, since it's not a "hook", but meh - context[@"__RCTProfileIsProfiling"] = @YES; - } - context[@"nativeTraceBeginAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { RCTJSCExecutor *strongSelf2 = weakSelf; if (!strongSelf2) { @@ -427,19 +409,6 @@ - (void)setUp CFDictionaryRemoveValue(strongSelf2->_cookieMap, (const void *)cookie); }; - context[@"nativeTraceBeginSection"] = ^(NSNumber *tag, NSString *profileName, NSDictionary *args) { - static int profileCounter = 1; - if (!profileName) { - profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++]; - } - - RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, args); - }; - - context[@"nativeTraceEndSection"] = ^(NSNumber *tag) { - RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); - }; - context[@"nativeTraceBeginAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { if (RCTProfileIsProfiling()) { [weakBridge.flowIDMapLock lock]; @@ -481,6 +450,39 @@ - (void)setUp }]; } ++ (void)installSynchronousHooksOnContext:(JSContext *)context +{ + context[@"noop"] = ^{}; + context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) { + RCTLogLevel level = RCTLogLevelInfo; + if (logLevel) { + level = MAX(level, (RCTLogLevel)logLevel.integerValue); + } + + _RCTLogJavaScriptInternal(level, message); + }; + context[@"nativePerformanceNow"] = ^{ + return @(CACurrentMediaTime() * 1000); + }; +#if RCT_PROFILE + if (RCTProfileIsProfiling()) { + // Cheating, since it's not a "hook", but meh + context[@"__RCTProfileIsProfiling"] = @YES; + } + context[@"nativeTraceBeginSection"] = ^(NSNumber *tag, NSString *profileName, NSDictionary *args) { + static int profileCounter = 1; + if (!profileName) { + profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++]; + } + + RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, args); + }; + context[@"nativeTraceEndSection"] = ^(NSNumber *tag) { + RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); + }; +#endif +} + - (void)toggleProfilingFlag:(NSNotification *)notification { [self executeBlockOnJavaScriptQueue:^{ From 2b397c50943dffac1f15137595f55dee3d58a369 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 16:36:52 -0700 Subject: [PATCH 036/241] Redo cookie map for nativeTraceBeginAsyncSection Reviewed By: javache Differential Revision: D3528604 fbshipit-source-id: 0feff3a09214af7c9c193733a5ad05d7de0dd21d --- React/Executors/RCTJSCExecutor.mm | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 44e45264264eed..295ea14d842716 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -13,6 +13,7 @@ #import #import #import +#import #import @@ -63,6 +64,16 @@ bool isEmpty() { } }; +#if RCT_PROFILE +@interface RCTCookieMap : NSObject +{ + @package + std::unordered_map _cookieMap; +} +@end +@implementation RCTCookieMap @end +#endif + @interface RCTJavaScriptContext : NSObject @property (nonatomic, strong, readonly) JSContext *context; @@ -129,7 +140,6 @@ @implementation RCTJSCExecutor { RCTJavaScriptContext *_context; NSThread *_javaScriptThread; - CFMutableDictionaryRef _cookieMap; RandomAccessBundleData _randomAccessBundle; JSValueRef _batchedBridgeRef; @@ -327,7 +337,6 @@ - (void)setUp { __weak RCTJSCExecutor *weakSelf = self; - _cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); __weak RCTBridge *weakBridge = _bridge; #ifndef __clang_analyzer__ _bridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); @@ -390,25 +399,6 @@ - (void)setUp }; #if RCT_PROFILE - context[@"nativeTraceBeginAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { - RCTJSCExecutor *strongSelf2 = weakSelf; - if (!strongSelf2) { - return; - } - NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil); - CFDictionarySetValue(strongSelf2->_cookieMap, (const void *)cookie, (const void *)newCookie); - }; - - context[@"nativeTraceEndAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { - RCTJSCExecutor *strongSelf2 = weakSelf; - if (!strongSelf2) { - return; - } - NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(strongSelf2->_cookieMap, (const void *)cookie); - RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, @"JS async", nil); - CFDictionaryRemoveValue(strongSelf2->_cookieMap, (const void *)cookie); - }; - context[@"nativeTraceBeginAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { if (RCTProfileIsProfiling()) { [weakBridge.flowIDMapLock lock]; @@ -480,6 +470,20 @@ + (void)installSynchronousHooksOnContext:(JSContext *)context context[@"nativeTraceEndSection"] = ^(NSNumber *tag) { RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); }; + RCTCookieMap *cookieMap = [RCTCookieMap new]; + context[@"nativeTraceBeginAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { + NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil); + cookieMap->_cookieMap.insert({cookie, newCookie}); + }; + context[@"nativeTraceEndAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) { + NSUInteger newCookie = 0; + const auto &it = cookieMap->_cookieMap.find(cookie); + if (it != cookieMap->_cookieMap.end()) { + newCookie = it->second; + cookieMap->_cookieMap.erase(it); + } + RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, @"JS async", nil); + }; #endif } @@ -521,10 +525,6 @@ - (void)dealloc RCTJSCWrapperRelease(_jscWrapper); _jscWrapper = NULL; } - - if (_cookieMap) { - CFRelease(_cookieMap); - } } - (void)flushedQueue:(RCTJavaScriptCallback)onComplete From 222060b21853117a7862b82b7b3955c69d814982 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Thu, 7 Jul 2016 16:36:53 -0700 Subject: [PATCH 037/241] Remove needless helper method Summary: I think this obscures more than it helps. Remove it. Reviewed By: javache Differential Revision: D3528677 fbshipit-source-id: d90a636a6e34b66563d9a02e255c6ebc4cee1294 --- React/Executors/RCTJSCExecutor.mm | 37 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 295ea14d842716..6b1195ccbef724 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -111,11 +111,6 @@ - (instancetype)initWithJSContext:(JSContext *)context RCT_NOT_IMPLEMENTED(-(instancetype)init) -- (JSGlobalContextRef)ctx -{ - return _context.JSGlobalContextRef; -} - - (BOOL)isValid { return _context != nil; @@ -362,16 +357,16 @@ - (void)setUp strongSelf->_jscWrapper = RCTJSCWrapperCreate(strongSelf->_useCustomJSCLibrary); [strongSelf->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; + JSContext *context = strongSelf.context.context; + if (strongSelf->_jscWrapper->configureJSContextForIOS != NULL) { NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); if (cachesPath) { - strongSelf->_jscWrapper->configureJSContextForIOS(strongSelf.context.ctx, [cachesPath UTF8String]); + strongSelf->_jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]); } } - // Synchronous hooks: - JSContext *context = strongSelf.context.context; [[self class] installSynchronousHooksOnContext:context]; context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) { @@ -432,7 +427,7 @@ - (void)setUp RCTJSCWrapper *jscWrapper = strongSelf2->_jscWrapper; JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString(sourceCode.UTF8String); JSStringRef jsURL = jscWrapper->JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String); - jscWrapper->JSEvaluateScript(strongSelf2->_context.ctx, execJSString, NULL, jsURL, 0, NULL); + jscWrapper->JSEvaluateScript(strongSelf2->_context.context.JSGlobalContextRef, execJSString, NULL, jsURL, 0, NULL); jscWrapper->JSStringRelease(jsURL); jscWrapper->JSStringRelease(execJSString); }; @@ -566,9 +561,10 @@ - (void)_executeJSCall:(NSString *)method JSValueRef errorJSRef = NULL; JSValueRef resultJSRef = NULL; RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; - JSGlobalContextRef contextJSRef = jscWrapper->JSContextGetGlobalContext(strongSelf->_context.ctx); + JSGlobalContextRef ctx = strongSelf->_context.context.JSGlobalContextRef; + JSGlobalContextRef contextJSRef = jscWrapper->JSContextGetGlobalContext(ctx); JSContext *context = strongSelf->_context.context; - JSObjectRef globalObjectJSRef = jscWrapper->JSContextGetGlobalObject(strongSelf->_context.ctx); + JSObjectRef globalObjectJSRef = jscWrapper->JSContextGetGlobalObject(ctx); // get the BatchedBridge object JSValueRef batchedBridgeRef = strongSelf->_batchedBridgeRef; @@ -668,7 +664,8 @@ - (void)executeApplicationScript:(NSData *)script RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes); JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); - JSValueRef result = jscWrapper->JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, bundleURL, 0, &jsError); + JSGlobalContextRef ctx = strongSelf->_context.context.JSGlobalContextRef; + JSValueRef result = jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError); jscWrapper->JSStringRelease(bundleURL); jscWrapper->JSStringRelease(execJSString); @@ -677,7 +674,7 @@ - (void)executeApplicationScript:(NSData *)script if (onComplete) { NSError *error; if (!result) { - error = RCTNSErrorFromJSError(jscWrapper, strongSelf->_context.ctx, jsError); + error = RCTNSErrorFromJSError(jscWrapper, ctx, jsError); } onComplete(error); } @@ -719,7 +716,8 @@ - (void)injectJSONText:(NSString *)script RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; JSStringRef execJSString = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)script); - JSValueRef valueToInject = jscWrapper->JSValueMakeFromJSONString(strongSelf->_context.ctx, execJSString); + JSGlobalContextRef ctx = strongSelf->_context.context.JSGlobalContextRef; + JSValueRef valueToInject = jscWrapper->JSValueMakeFromJSONString(ctx, execJSString); jscWrapper->JSStringRelease(execJSString); if (!valueToInject) { @@ -733,9 +731,9 @@ - (void)injectJSONText:(NSString *)script return; } - JSObjectRef globalObject = jscWrapper->JSContextGetGlobalObject(strongSelf->_context.ctx); + JSObjectRef globalObject = jscWrapper->JSContextGetGlobalObject(ctx); JSStringRef JSName = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)objectName); - jscWrapper->JSObjectSetProperty(strongSelf->_context.ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL); + jscWrapper->JSObjectSetProperty(ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL); jscWrapper->JSStringRelease(JSName); if (onComplete) { onComplete(nil); @@ -764,14 +762,15 @@ static void executeRandomAccessModule(RCTJSCExecutor *executor, uint32_t moduleI JSStringRef code = jscWrapper->JSStringCreateWithUTF8CString(data.get()); JSValueRef jsError = NULL; JSStringRef sourceURL = jscWrapper->JSStringCreateWithUTF8CString(url); - JSValueRef result = jscWrapper->JSEvaluateScript(executor->_context.ctx, code, NULL, sourceURL, 0, &jsError); + JSGlobalContextRef ctx = executor->_context.context.JSGlobalContextRef; + JSValueRef result = jscWrapper->JSEvaluateScript(ctx, code, NULL, sourceURL, 0, &jsError); jscWrapper->JSStringRelease(code); jscWrapper->JSStringRelease(sourceURL); if (!result) { dispatch_async(dispatch_get_main_queue(), ^{ - RCTFatal(RCTNSErrorFromJSError(jscWrapper, executor->_context.ctx, jsError)); + RCTFatal(RCTNSErrorFromJSError(jscWrapper, ctx, jsError)); [executor invalidate]; }); } @@ -891,7 +890,7 @@ - (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error { if (_jscWrapper->JSGlobalContextSetName != NULL) { JSStringRef JSName = _jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)name); - _jscWrapper->JSGlobalContextSetName(_context.ctx, JSName); + _jscWrapper->JSGlobalContextSetName(_context.context.JSGlobalContextRef, JSName); _jscWrapper->JSStringRelease(JSName); } } From 46a685169e66d17c179ed7a83eea36f3988fcd9c Mon Sep 17 00:00:00 2001 From: Jake Murzy Date: Thu, 7 Jul 2016 20:59:44 -0700 Subject: [PATCH 038/241] Use active scene passed in `props` by `NavigationTransitioner` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: With facebook/react-native@c57bac4, `NavigationTransitioner` passes the currently active `scene` in transition `props`. This simplifies the handling of transition changes when rendering overlay. 🍺 Closes https://github.com/facebook/react-native/pull/8298 Differential Revision: D3533447 Pulled By: ericvicenti fbshipit-source-id: df568dc5cf5d57d8948b9f0400a8d693cf5564e1 --- .../NavigationCardStack.js | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js index d04e13373ff3c0..41d2d7154e2075 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js @@ -42,8 +42,6 @@ const ReactComponentWithPureRenderMixin = require('react/lib/ReactComponentWithP const StyleSheet = require('StyleSheet'); const View = require('View'); -const emptyFunction = require('fbjs/lib/emptyFunction'); - const {PropTypes} = React; const {Directions} = NavigationCardStackPanResponder; @@ -70,7 +68,6 @@ type Props = { type DefaultProps = { direction: NavigationGestureDirection, - renderOverlay: ?NavigationSceneRenderer, }; /** @@ -102,7 +99,6 @@ class NavigationCardStack extends React.Component { static defaultProps: DefaultProps = { direction: Directions.HORIZONTAL, - renderOverlay: emptyFunction.thatReturnsNull, }; constructor(props: Props, context: any) { @@ -134,22 +130,14 @@ class NavigationCardStack extends React.Component { _render(props: NavigationTransitionProps): ReactElement { const { - navigationState, - } = props; + renderOverlay + } = this.props; let overlay = null; - const renderOverlay = this.props.renderOverlay; - if (renderOverlay) { - const route = navigationState.routes[navigationState.index]; - - const activeScene = props.scenes.find( - scene => !scene.isStale && scene.route === route ? scene : undefined - ); - overlay = renderOverlay({ ...props, - scene: activeScene + scene: props.scene, }); } @@ -157,8 +145,7 @@ class NavigationCardStack extends React.Component { scene => this._renderScene({ ...props, scene, - }), - this + }) ); return ( From c3f4d79475ac0cf14027e862043a8f4eda93dc04 Mon Sep 17 00:00:00 2001 From: Gant Date: Fri, 8 Jul 2016 01:33:51 -0700 Subject: [PATCH 039/241] changes link file on Android to MainApplication.java for 0.29 update Summary: rnpm aka `react-native link` is broken with Android 0.29 - #8603 This gets it back to working again by checking for new MyApplication.java file, and curtailing the path when needed. Closes https://github.com/facebook/react-native/pull/8612 Differential Revision: D3533960 fbshipit-source-id: 95d799eaebb26ba1d876c88107ccd2af72427f55 --- local-cli/rnpm/core/src/config/android/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/local-cli/rnpm/core/src/config/android/index.js b/local-cli/rnpm/core/src/config/android/index.js index f8277307f1844f..295b3b7d318c09 100644 --- a/local-cli/rnpm/core/src/config/android/index.js +++ b/local-cli/rnpm/core/src/config/android/index.js @@ -33,9 +33,9 @@ exports.projectConfig = function projectConfigAndroid(folder, userConfig) { const packageFolder = userConfig.packageFolder || packageName.replace(/\./g, path.sep); - const mainActivityPath = path.join( + const mainFilePath = path.join( sourceDir, - userConfig.mainActivityPath || `src/main/java/${packageFolder}/MainActivity.java` + userConfig.mainFilePath || `src/main/java/${packageFolder}/MainApplication.java` ); const stringsPath = path.join( @@ -68,7 +68,7 @@ exports.projectConfig = function projectConfigAndroid(folder, userConfig) { buildGradlePath, settingsGradlePath, assetsPath, - mainActivityPath, + mainFilePath, }; }; From 3ddf3db551b9c3973cc4722935d533cab70268ed Mon Sep 17 00:00:00 2001 From: Konstantin Raev Date: Fri, 8 Jul 2016 03:23:41 -0700 Subject: [PATCH 040/241] Fixed objc test runner and bezier test Summary: - kill -9 SERVER_PID does not work for packager currently because it is started as daemon. - And lego tests just hang until they are killed e.g. intern/sandcastle/1952254070/187417721/ - fixed bezier test because it annoyed me with random breaks because of precision Reviewed By: davidaurelio Differential Revision: D3528588 fbshipit-source-id: 87e5b4330fa69bc9a8a7f48e2250f3c2239f2b35 --- Libraries/Animated/src/__tests__/bezier-test.js | 16 ++++++++-------- scripts/objc-test.sh | 9 ++------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Libraries/Animated/src/__tests__/bezier-test.js b/Libraries/Animated/src/__tests__/bezier-test.js index 4b38bae78c8a3a..ef1bb68c2d828b 100644 --- a/Libraries/Animated/src/__tests__/bezier-test.js +++ b/Libraries/Animated/src/__tests__/bezier-test.js @@ -5,13 +5,13 @@ var bezier = require('bezier'); var identity = function (x) { return x; }; -function assertClose (a, b, precision) { - expect(a).toBeCloseTo(b, 3); +function assertClose (a, b, precision = 3) { + expect(a).toBeCloseTo(b, precision); } function makeAssertCloseWithPrecision (precision) { - return function (a, b, message) { - assertClose(a, b, message, precision); + return function (a, b) { + assertClose(a, b, precision); }; } @@ -19,7 +19,7 @@ function allEquals (be1, be2, samples, assertion) { if (!assertion) assertion = assertClose; for (var i=0; i<=samples; ++i) { var x = i / samples; - assertion(be1(x), be2(x), 'comparing '+be1+' and '+be2+' for value '+x); + assertion(be1(x), be2(x)); } } @@ -64,7 +64,7 @@ describe('bezier', function(){ var easing = bezier(a, b, c, d); var projected = bezier(b, a, d, c); var composed = function (x) { return projected(easing(x)); }; - allEquals(identity, composed, 100, makeAssertCloseWithPrecision(0.05)); + allEquals(identity, composed, 100, makeAssertCloseWithPrecision(2)); }); }); }); @@ -81,7 +81,7 @@ describe('bezier', function(){ repeat(10)(function () { var a = Math.random(), b = 2*Math.random()-0.5, c = 1-a, d = 1-b; var easing = bezier(a, b, c, d); - assertClose(easing(0.5), 0.5, easing+'(0.5) should be 0.5'); + assertClose(easing(0.5), 0.5); }); }); it('should be symetrical', function () { @@ -89,7 +89,7 @@ describe('bezier', function(){ var a = Math.random(), b = 2*Math.random()-0.5, c = 1-a, d = 1-b; var easing = bezier(a, b, c, d); var sym = function (x) { return 1 - easing(1-x); }; - allEquals(easing, sym, 100); + allEquals(easing, sym, 100, makeAssertCloseWithPrecision(2)); }); }); }); diff --git a/scripts/objc-test.sh b/scripts/objc-test.sh index c4f553377379f6..f651b81d9297a3 100755 --- a/scripts/objc-test.sh +++ b/scripts/objc-test.sh @@ -16,16 +16,11 @@ function cleanup { WATCHMAN_LOGS=/usr/local/Cellar/watchman/3.1/var/run/watchman/$USER.log [ -f $WATCHMAN_LOGS ] && cat $WATCHMAN_LOGS fi - [ $SERVER_PID ] && kill -9 $SERVER_PID + # kill whatever is occupying port 8081 + lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill } trap cleanup EXIT -if [ -z "$TRAVIS" ]; then - # Run the packager process directly - node ./local-cli/cli.js start & - SERVER_PID=$! -fi - XCODE_PROJECT="Examples/UIExplorer/UIExplorer.xcodeproj" XCODE_SCHEME="UIExplorer" XCODE_SDK="iphonesimulator" From 89a53b687c10cf1105ea010684e5da7eff2d3ed4 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 8 Jul 2016 03:46:41 -0700 Subject: [PATCH 041/241] Use AT_MOST measurespec when onyl max dimension is defined Summary: https://github.com/facebook/css-layout/pull/200 Make use of max dimension styles to allow root to be measured with AT_MOST measure mode Reviewed By: IanChilds Differential Revision: D3513505 fbshipit-source-id: 169f49717e896eb6270b52fb7115ce005aa0e3a8 --- React/Layout/Layout.c | 31 +++++++++++++------ React/Layout/README | 2 +- .../com/facebook/csslayout/LayoutEngine.java | 27 +++++++++++----- .../main/java/com/facebook/csslayout/README | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index da1e2559f43704..22412a8d5c2701 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -7,7 +7,7 @@ */ // NOTE: this file is auto-copied from https://github.com/facebook/css-layout -// @generated SignedSource<<0c8bd7e17fc12884003809cf282b0988>> +// @generated SignedSource<<484d0d17453521463896ce24d946412b>> #include @@ -1786,17 +1786,28 @@ void layoutNode(css_node_t* node, float availableWidth, float availableHeight, c // parameters don't change. gCurrentGenerationCount++; - // If the caller didn't specify a height/width, use the dimensions - // specified in the style. - if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { - availableWidth = node->style.dimensions[CSS_WIDTH] + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); - } - if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - availableHeight = node->style.dimensions[CSS_HEIGHT] + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + css_measure_mode_t widthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + css_measure_mode_t heightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + + if (!isUndefined(availableWidth)) { + widthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } else if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + availableWidth = node->style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + widthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } else if (node->style.maxDimensions[CSS_WIDTH] >= 0.0) { + availableWidth = node->style.maxDimensions[CSS_WIDTH]; + widthMeasureMode = CSS_MEASURE_MODE_AT_MOST; } - css_measure_mode_t widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; - css_measure_mode_t heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + if (!isUndefined(availableHeight)) { + heightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } else if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + availableHeight = node->style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + heightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } else if (node->style.maxDimensions[CSS_HEIGHT] >= 0.0) { + availableHeight = node->style.maxDimensions[CSS_HEIGHT]; + heightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial")) { diff --git a/React/Layout/README b/React/Layout/README index e04447bbc37587..9d85b26ff7a12f 100644 --- a/React/Layout/README +++ b/React/Layout/README @@ -1,7 +1,7 @@ The source of truth for css-layout is: https://github.com/facebook/css-layout The code here should be kept in sync with GitHub. -HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/383d8a6b3dcbdb978e012e29040e1a43157765c6 +HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/a1f36b53f5464c8ee7abc311765dc3ecb1b879c6 There is generated code in: - README (this file) diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java b/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java index 042b253660a4d9..582fc44aa43316 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java @@ -7,7 +7,7 @@ */ // NOTE: this file is auto-copied from https://github.com/facebook/css-layout -// @generated SignedSource<> +// @generated SignedSource<> package com.facebook.csslayout; @@ -234,20 +234,31 @@ private static boolean isMeasureDefined(CSSNode node) { // parameters don't change. layoutContext.currentGenerationCount++; - // If the caller didn't specify a height/width, use the dimensions - // specified in the style. - if (Float.isNaN(availableWidth) && node.style.dimensions[DIMENSION_WIDTH] >= 0.0) { + CSSMeasureMode widthMeasureMode = CSSMeasureMode.UNDEFINED; + CSSMeasureMode heightMeasureMode = CSSMeasureMode.UNDEFINED; + + if (!Float.isNaN(availableWidth)) { + widthMeasureMode = CSSMeasureMode.EXACTLY; + } else if (node.style.dimensions[DIMENSION_WIDTH] >= 0.0) { float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); availableWidth = node.style.dimensions[DIMENSION_WIDTH] + marginAxisRow; + widthMeasureMode = CSSMeasureMode.EXACTLY; + } else if (node.style.maxWidth >= 0.0) { + availableWidth = node.style.maxWidth; + widthMeasureMode = CSSMeasureMode.AT_MOST; } - if (Float.isNaN(availableHeight) && node.style.dimensions[DIMENSION_HEIGHT] >= 0.0) { + + if (!Float.isNaN(availableHeight)) { + heightMeasureMode = CSSMeasureMode.EXACTLY; + } else if (node.style.dimensions[DIMENSION_HEIGHT] >= 0.0) { float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); availableHeight = node.style.dimensions[DIMENSION_HEIGHT] + marginAxisColumn; + heightMeasureMode = CSSMeasureMode.EXACTLY; + } else if (node.style.maxHeight >= 0.0) { + availableHeight = node.style.maxHeight; + heightMeasureMode = CSSMeasureMode.AT_MOST; } - CSSMeasureMode widthMeasureMode = Float.isNaN(availableWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; - CSSMeasureMode heightMeasureMode = Float.isNaN(availableHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; - if (layoutNodeInternal(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial")) { setPosition(node, node.layout.direction); } diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/README b/ReactAndroid/src/main/java/com/facebook/csslayout/README index e04447bbc37587..9d85b26ff7a12f 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/README +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/README @@ -1,7 +1,7 @@ The source of truth for css-layout is: https://github.com/facebook/css-layout The code here should be kept in sync with GitHub. -HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/383d8a6b3dcbdb978e012e29040e1a43157765c6 +HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/a1f36b53f5464c8ee7abc311765dc3ecb1b879c6 There is generated code in: - README (this file) From 29f9be6d9c83f7415b84d66988834ce8c895c4e1 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Fri, 8 Jul 2016 09:08:34 -0700 Subject: [PATCH 042/241] Remove needless weak self in RCTJSCExecutor Summary: There's no reason for this; setUp is once-and-done. Probably just trying to ignore the incorrect clang warning. Reviewed By: javache Differential Revision: D3528494 fbshipit-source-id: e4f986df8d097e4720dfd4a51e7fb6c9c9b5108f --- React/Executors/RCTJSCExecutor.mm | 71 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 6b1195ccbef724..c96b81d886e319 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -330,15 +330,12 @@ - (JSContext *)underlyingJSContext - (void)setUp { - __weak RCTJSCExecutor *weakSelf = self; - - __weak RCTBridge *weakBridge = _bridge; +#if RCT_PROFILE #ifndef __clang_analyzer__ _bridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); #endif _bridge.flowIDMapLock = [NSLock new]; -#if RCT_PROFILE for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleProfilingFlag:) @@ -348,52 +345,54 @@ - (void)setUp #endif [self executeBlockOnJavaScriptQueue:^{ - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid) { + if (!self.valid) { return; } - [strongSelf->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary]; - strongSelf->_jscWrapper = RCTJSCWrapperCreate(strongSelf->_useCustomJSCLibrary); - [strongSelf->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; + [self->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary]; + self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary); + [self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; - JSContext *context = strongSelf.context.context; + JSContext *context = self.context.context; - if (strongSelf->_jscWrapper->configureJSContextForIOS != NULL) { + if (self->_jscWrapper->configureJSContextForIOS != NULL) { NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); if (cachesPath) { - strongSelf->_jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]); + self->_jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]); } } [[self class] installSynchronousHooksOnContext:context]; + __weak RCTJSCExecutor *weakSelf = self; + context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) { - RCTJSCExecutor *strongSelf2 = weakSelf; - if (!strongSelf2.valid) { + RCTJSCExecutor *strongSelf = weakSelf; + if (!strongSelf.valid) { return nil; } RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeRequireModuleConfig", nil); - NSArray *config = [strongSelf2->_bridge configForModuleName:moduleName]; + NSArray *config = [strongSelf->_bridge configForModuleName:moduleName]; NSString *result = config ? RCTJSONStringify(config, NULL) : nil; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call,config", @{ @"moduleName": moduleName }); return result; }; context[@"nativeFlushQueueImmediate"] = ^(NSArray *calls){ - RCTJSCExecutor *strongSelf2 = weakSelf; - if (!strongSelf2.valid || !calls) { + RCTJSCExecutor *strongSelf = weakSelf; + if (!strongSelf.valid || !calls) { return; } RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeFlushQueueImmediate", nil); - [strongSelf2->_bridge handleBuffer:calls batchEnded:NO]; + [strongSelf->_bridge handleBuffer:calls batchEnded:NO]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); }; #if RCT_PROFILE + __weak RCTBridge *weakBridge = self->_bridge; context[@"nativeTraceBeginAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { if (RCTProfileIsProfiling()) { [weakBridge.flowIDMapLock lock]; @@ -415,19 +414,19 @@ - (void)setUp #endif #if RCT_DEV - RCTInstallJSCProfiler(strongSelf->_bridge, context.JSGlobalContextRef); + RCTInstallJSCProfiler(self->_bridge, context.JSGlobalContextRef); // Inject handler used by HMR context[@"nativeInjectHMRUpdate"] = ^(NSString *sourceCode, NSString *sourceCodeURL) { - RCTJSCExecutor *strongSelf2 = weakSelf; - if (!strongSelf2.valid) { + RCTJSCExecutor *strongSelf = weakSelf; + if (!strongSelf.valid) { return; } - RCTJSCWrapper *jscWrapper = strongSelf2->_jscWrapper; + RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString(sourceCode.UTF8String); JSStringRef jsURL = jscWrapper->JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String); - jscWrapper->JSEvaluateScript(strongSelf2->_context.context.JSGlobalContextRef, execJSString, NULL, jsURL, 0, NULL); + jscWrapper->JSEvaluateScript(strongSelf->_context.context.JSGlobalContextRef, execJSString, NULL, jsURL, 0, NULL); jscWrapper->JSStringRelease(jsURL); jscWrapper->JSStringRelease(execJSString); }; @@ -782,29 +781,27 @@ - (void)registerNativeRequire [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount]; [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; - __weak RCTJSCExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:^{ - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf.valid) { + if (!self.valid) { return; } - JSContext *context = strongSelf.context.context; - context[@"nativeRequire"] = ^(NSNumber *moduleID) { - RCTJSCExecutor *strongSelf2 = weakSelf; - if (!strongSelf2 || !moduleID) { + __weak RCTJSCExecutor *weakSelf = self; + self.context.context[@"nativeRequire"] = ^(NSNumber *moduleID) { + RCTJSCExecutor *strongSelf = weakSelf; + if (!strongSelf || !moduleID) { return; } - [strongSelf2->_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; - [strongSelf2->_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; + [strongSelf->_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; + [strongSelf->_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [@"nativeRequire_" stringByAppendingFormat:@"%@", moduleID], nil); const uint32_t ID = [moduleID unsignedIntValue]; - if (ID < strongSelf2->_randomAccessBundle.numTableEntries) { - ModuleData *moduleData = &strongSelf2->_randomAccessBundle.table[ID]; + if (ID < strongSelf->_randomAccessBundle.numTableEntries) { + ModuleData *moduleData = &strongSelf->_randomAccessBundle.table[ID]; const uint32_t size = NSSwapLittleIntToHost(moduleData->size); // sparse entry in the table -- module does not exist or is contained in the startup section @@ -812,12 +809,12 @@ - (void)registerNativeRequire return; } - [strongSelf2->_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; - executeRandomAccessModule(strongSelf2, ID, NSSwapLittleIntToHost(moduleData->offset), size); + [strongSelf->_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; + executeRandomAccessModule(strongSelf, ID, NSSwapLittleIntToHost(moduleData->offset), size); } RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); - [strongSelf2->_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; + [strongSelf->_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; }; }]; } From 1ebd9c5deaa453a56e73f4fc34165f08de18bd79 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Fri, 8 Jul 2016 09:08:36 -0700 Subject: [PATCH 043/241] Create JSContext inside setUp Summary: In practice, it *MUST* be the call to `self.context` within `setUp` that triggers the creation of the context: - It can't come before that point because `_jscWrapper` has not been set up yet - It can't come after that point since `self.context` would create the context there. Just move the creation to be inline, enforced by assert. This makes it easier to reason about where the context is created, and easier to change how it is created later. Reviewed By: javache Differential Revision: D3529843 fbshipit-source-id: 8ed5a9861ebefd4b9e0f7155db8587dcf0442b7a --- React/Executors/RCTJSCExecutor.mm | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index c96b81d886e319..a9d2e60b8c64cd 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -307,19 +307,10 @@ - (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary - (RCTJavaScriptContext *)context { RCTAssertThread(_javaScriptThread, @"Must be called on JS thread."); - if (!self.isValid) { return nil; } - - if (!_context) { - JSContext *context = [_jscWrapper->JSContext new]; - _context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:_javaScriptThread]; - - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification - object:context]; - } - + RCTAssert(_context != nil, @"Fetching context while valid, but before it is created"); return _context; } @@ -353,7 +344,11 @@ - (void)setUp self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary); [self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; - JSContext *context = self.context.context; + RCTAssert(self->_context == nil, @"Didn't expect to set up twice"); + JSContext *context = [self->_jscWrapper->JSContext new]; + self->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:self->_javaScriptThread]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification + object:context]; if (self->_jscWrapper->configureJSContextForIOS != NULL) { NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; From ca663839415f4e28c2ff067d22799bd6066d9148 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 8 Jul 2016 09:22:45 -0700 Subject: [PATCH 044/241] Correctly size cross axis when measuring flex basis Summary: https://github.com/facebook/css-layout/pull/199 - Nodes were measured with the assumption of being text nodes (height depends on width) when determining flex basis. This is not always true. Even when we are just interested in the main axis (flex basis) we need to correctly constrain the cross axis. - Some tests were wrong. Measuring texts.big and expecting it to have textSizes.smallHeight which doesn't make a lot of sense. Reviewed By: vjeux Differential Revision: D3510163 fbshipit-source-id: ee53b548dd078005fdd153d279e4c7fef3dd02d0 --- React/Layout/Layout.c | 89 ++++++++++--------- React/Layout/README | 2 +- .../com/facebook/csslayout/LayoutEngine.java | 89 ++++++++++--------- .../main/java/com/facebook/csslayout/README | 2 +- 4 files changed, 96 insertions(+), 86 deletions(-) diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index 22412a8d5c2701..d9bc697fc9b4d2 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -7,7 +7,7 @@ */ // NOTE: this file is auto-copied from https://github.com/facebook/css-layout -// @generated SignedSource<<484d0d17453521463896ce24d946412b>> +// @generated SignedSource<<8af322a110307b3277db5b00573da822>> #include @@ -829,56 +829,61 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab child->layout.flex_basis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); } else { - // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). childWidth = CSS_UNDEFINED; childHeight = CSS_UNDEFINED; childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { - childWidth = child->style.dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); - childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } - if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { - childHeight = child->style.dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); - childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } - - // According to the spec, if the main size is not definite and the - // child's inline axis is parallel to the main axis (i.e. it's - // horizontal), the child should be sized using "UNDEFINED" in - // the main size. Otherwise use "AT_MOST" in the cross axis. - if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { - childWidth = availableInnerWidth; - childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; - } - - // The W3C spec doesn't say anything about the 'overflow' property, - // but all major browsers appear to implement the following logic. - if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { - if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { - childHeight = availableInnerHeight; + // Main axis + if (isMainAxisRow) { + if (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED || isUndefined(availableInnerMainDim)) { + childWidth = CSS_UNDEFINED; + childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + } else { + childWidth = availableInnerMainDim; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } else if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED || isUndefined(availableInnerMainDim)) { + childHeight = CSS_UNDEFINED; + childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + } else { + childHeight = availableInnerMainDim; childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; } } - // If child has no defined size in the cross axis and is set to stretch, set the cross - // axis to be measured exactly with the available inner width - if (!isMainAxisRow && - !isUndefined(availableInnerWidth) && - !isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW) && - widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && - getAlignItem(node, child) == CSS_ALIGN_STRETCH) { - childWidth = availableInnerWidth; - childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } - if (isMainAxisRow && - !isUndefined(availableInnerHeight) && - !isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN) && - heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && - getAlignItem(node, child) == CSS_ALIGN_STRETCH) { - childHeight = availableInnerHeight; - childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + // Cross axis + if (isMainAxisRow) { + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (!isUndefined(availableInnerCrossDim) && + !isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN) && + heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && + getAlignItem(node, child) == CSS_ALIGN_STRETCH) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } else if (!isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childHeight = child->style.dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } + } else { + if (!isUndefined(availableInnerCrossDim) && + !isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW) && + widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && + getAlignItem(node, child) == CSS_ALIGN_STRETCH) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } else if (!isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childWidth = child->style.dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } } // Measure the child diff --git a/React/Layout/README b/React/Layout/README index 9d85b26ff7a12f..5a2ca3b1e73de9 100644 --- a/React/Layout/README +++ b/React/Layout/README @@ -1,7 +1,7 @@ The source of truth for css-layout is: https://github.com/facebook/css-layout The code here should be kept in sync with GitHub. -HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/a1f36b53f5464c8ee7abc311765dc3ecb1b879c6 +HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/ca34ff44460bce122d02b27c0409a825f8de90b1 There is generated code in: - README (this file) diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java b/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java index 582fc44aa43316..735eec68703ee6 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java @@ -7,7 +7,7 @@ */ // NOTE: this file is auto-copied from https://github.com/facebook/css-layout -// @generated SignedSource<> +// @generated SignedSource<<9805b148b88d298edb7fcd7c13738ede>> package com.facebook.csslayout; @@ -726,56 +726,61 @@ private static void layoutNodeImpl( child.layout.flexBasis = Math.max(0, ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))); } else { - // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). childWidth = CSSConstants.UNDEFINED; childHeight = CSSConstants.UNDEFINED; childWidthMeasureMode = CSSMeasureMode.UNDEFINED; childHeightMeasureMode = CSSMeasureMode.UNDEFINED; - if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { - childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); - childWidthMeasureMode = CSSMeasureMode.EXACTLY; - } - if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); - childHeightMeasureMode = CSSMeasureMode.EXACTLY; - } - - // According to the spec, if the main size is not definite and the - // child's inline axis is parallel to the main axis (i.e. it's - // horizontal), the child should be sized using "UNDEFINED" in - // the main size. Otherwise use "AT_MOST" in the cross axis. - if (!isMainAxisRow && Float.isNaN(childWidth) && !Float.isNaN(availableInnerWidth)) { - childWidth = availableInnerWidth; - childWidthMeasureMode = CSSMeasureMode.AT_MOST; - } - - // The W3C spec doesn't say anything about the 'overflow' property, - // but all major browsers appear to implement the following logic. - if (node.style.overflow == CSSOverflow.HIDDEN) { - if (isMainAxisRow && Float.isNaN(childHeight) && !Float.isNaN(availableInnerHeight)) { - childHeight = availableInnerHeight; + // Main axis + if (isMainAxisRow) { + if (widthMeasureMode == CSSMeasureMode.UNDEFINED || Float.isNaN(availableInnerMainDim)) { + childWidth = CSSConstants.UNDEFINED; + childWidthMeasureMode = CSSMeasureMode.UNDEFINED; + } else { + childWidth = availableInnerMainDim; + childWidthMeasureMode = CSSMeasureMode.AT_MOST; + } + } else if (node.style.overflow == CSSOverflow.HIDDEN) { + if (heightMeasureMode == CSSMeasureMode.UNDEFINED || Float.isNaN(availableInnerMainDim)) { + childHeight = CSSConstants.UNDEFINED; + childHeightMeasureMode = CSSMeasureMode.UNDEFINED; + } else { + childHeight = availableInnerMainDim; childHeightMeasureMode = CSSMeasureMode.AT_MOST; } } - // If child has no defined size in the cross axis and is set to stretch, set the cross - // axis to be measured exactly with the available inner width - if (!isMainAxisRow && - !Float.isNaN(availableInnerWidth) && - !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0) && - widthMeasureMode == CSSMeasureMode.EXACTLY && - getAlignItem(node, child) == CSSAlign.STRETCH) { - childWidth = availableInnerWidth; - childWidthMeasureMode = CSSMeasureMode.EXACTLY; - } - if (isMainAxisRow && - !Float.isNaN(availableInnerHeight) && - !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) && - heightMeasureMode == CSSMeasureMode.EXACTLY && - getAlignItem(node, child) == CSSAlign.STRETCH) { - childHeight = availableInnerHeight; - childHeightMeasureMode = CSSMeasureMode.EXACTLY; + // Cross axis + if (isMainAxisRow) { + if (node.style.overflow == CSSOverflow.HIDDEN) { + if (!Float.isNaN(availableInnerCrossDim) && + !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) && + heightMeasureMode == CSSMeasureMode.EXACTLY && + getAlignItem(node, child) == CSSAlign.STRETCH) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = CSSMeasureMode.EXACTLY; + } else if (!(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = Float.isNaN(childHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.AT_MOST; + } else { + childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.EXACTLY; + } + } + } else { + if (!Float.isNaN(availableInnerCrossDim) && + !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0) && + widthMeasureMode == CSSMeasureMode.EXACTLY && + getAlignItem(node, child) == CSSAlign.STRETCH) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + } else if (!(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = Float.isNaN(childWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.AT_MOST; + } else { + childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + } } // Measure the child diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/README b/ReactAndroid/src/main/java/com/facebook/csslayout/README index 9d85b26ff7a12f..5a2ca3b1e73de9 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/README +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/README @@ -1,7 +1,7 @@ The source of truth for css-layout is: https://github.com/facebook/css-layout The code here should be kept in sync with GitHub. -HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/a1f36b53f5464c8ee7abc311765dc3ecb1b879c6 +HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/ca34ff44460bce122d02b27c0409a825f8de90b1 There is generated code in: - README (this file) From cadc753e4c2c364d78dd2fa69499832ab52c65cd Mon Sep 17 00:00:00 2001 From: Mengjue Wang Date: Fri, 8 Jul 2016 10:52:52 -0700 Subject: [PATCH 045/241] Change NavigatorSceneConfig for RTL Summary: For RTL experiment, we need to swap all the Left and Right gesture and animation. Provide RTL support for Navigator in RN. Reviewed By: hedgerwang Differential Revision: D3519811 fbshipit-source-id: b53d9bf901ec056614658b627823d2225bdc82b1 --- .../Navigator/NavigatorSceneConfigs.js | 112 ++++++++++++++---- 1 file changed, 92 insertions(+), 20 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index c26396dd6e3ea1..62c7acf9a1dfd6 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -1,5 +1,10 @@ /** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * 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. * * Facebook, Inc. ("Facebook") owns all right, title and interest, including * all intellectual property and other proprietary rights, in and to the React @@ -31,6 +36,9 @@ var PixelRatio = require('PixelRatio'); var buildStyleInterpolator = require('buildStyleInterpolator'); +var I18nManager = require('NativeModules').I18nManager || {}; +var IS_RTL = I18nManager.isRTL; + var SCREEN_WIDTH = Dimensions.get('window').width; var SCREEN_HEIGHT = Dimensions.get('window').height; @@ -50,6 +58,14 @@ var ToTheLeftIOS = { }, }; +var ToTheRightIOS = { + ...ToTheLeftIOS, + transformTranslate: { + from: {x: 0, y: 0, z: 0}, + to: {x: SCREEN_WIDTH * 0.3, y: 0, z: 0}, + }, +}; + var FadeToTheLeft = { // Rotate *requires* you to break out each individual component of // rotation (x, y, z, w) @@ -126,7 +142,7 @@ var FadeToTheRight = { translateX: { from: 0, to: Math.round(SCREEN_WIDTH * 0.3), - } + }, }; var FadeIn = { @@ -179,6 +195,32 @@ var ToTheLeft = { }, }; +var ToTheRight = { + transformTranslate: { + from: {x: 0, y: 0, z: 0}, + to: {x: Dimensions.get('window').width, y: 0, z: 0}, + min: 0, + max: 1, + type: 'linear', + extrapolate: true, + round: PixelRatio.get(), + }, + opacity: { + value: 1.0, + type: 'constant', + }, + + translateX: { + from: 0, + to: Dimensions.get('window').width, + min: 0, + max: 1, + type: 'linear', + extrapolate: true, + round: PixelRatio.get(), + }, +}; + var ToTheUp = { transformTranslate: { from: {x: 0, y: 0, z: 0}, @@ -500,10 +542,40 @@ var BaseUpDownGesture = { direction: 'up-to-down', }; +// For RTL experiment, we need to swap all the Left and Right gesture and animation. +// So we create a direction mapping for both LTR and RTL, and change left/right to start/end. +let directionMapping = { + ToTheStartIOS: ToTheLeftIOS, + ToTheEndIOS: ToTheRightIOS, + FadeToTheStart: FadeToTheLeft, + FadeToTheEnd: FadeToTheRight, + ToTheStart: ToTheLeft, + ToTheEnd: ToTheRight, + FromTheStart: FromTheLeft, + FromTheEnd: FromTheRight, + BaseStartToEndGesture: BaseLeftToRightGesture, + BaseEndToStartGesture: BaseRightToLeftGesture, +}; + +if (IS_RTL) { + directionMapping = { + ToTheStartIOS: ToTheRightIOS, + ToTheEndIOS: ToTheLeftIOS, + FadeToTheStart: FadeToTheRight, + FadeToTheEnd: FadeToTheLeft, + ToTheStart: ToTheRight, + ToTheEnd: ToTheLeft, + FromTheStart: FromTheRight, + FromTheEnd: FromTheLeft, + BaseStartToEndGesture: BaseRightToLeftGesture, + BaseEndToStartGesture: BaseLeftToRightGesture, + }; +} + var BaseConfig = { // A list of all gestures that are enabled on this scene gestures: { - pop: BaseLeftToRightGesture, + pop: directionMapping.BaseStartToEndGesture, }, // Rebound spring parameters when transitioning FROM this scene @@ -515,8 +587,8 @@ var BaseConfig = { // Animation interpolators for horizontal transitioning: animationInterpolators: { - into: buildStyleInterpolator(FromTheRight), - out: buildStyleInterpolator(FadeToTheLeft), + into: buildStyleInterpolator(directionMapping.FromTheEnd), + out: buildStyleInterpolator(directionMapping.FadeToTheStart), }, }; @@ -524,8 +596,8 @@ var NavigatorSceneConfigs = { PushFromRight: { ...BaseConfig, animationInterpolators: { - into: buildStyleInterpolator(FromTheRight), - out: buildStyleInterpolator(ToTheLeftIOS), + into: buildStyleInterpolator(directionMapping.FromTheEnd), + out: buildStyleInterpolator(directionMapping.ToTheStartIOS), }, }, FloatFromRight: { @@ -535,18 +607,18 @@ var NavigatorSceneConfigs = { FloatFromLeft: { ...BaseConfig, gestures: { - pop: BaseRightToLeftGesture, + pop: directionMapping.BaseEndToStartGesture, }, animationInterpolators: { - into: buildStyleInterpolator(FromTheLeft), - out: buildStyleInterpolator(FadeToTheRight), + into: buildStyleInterpolator(directionMapping.FromTheStart), + out: buildStyleInterpolator(directionMapping.FadeToTheEnd), }, }, FloatFromBottom: { ...BaseConfig, gestures: { pop: { - ...BaseLeftToRightGesture, + ...directionMapping.BaseStartToEndGesture, edgeHitWidth: 150, direction: 'top-to-bottom', fullDistance: SCREEN_HEIGHT, @@ -579,43 +651,43 @@ var NavigatorSceneConfigs = { ...BaseConfig, gestures: { jumpBack: { - ...BaseLeftToRightGesture, + ...directionMapping.BaseStartToEndGesture, overswipe: BaseOverswipeConfig, edgeHitWidth: null, isDetachable: true, }, jumpForward: { - ...BaseRightToLeftGesture, + ...directionMapping.BaseEndToStartGesture, overswipe: BaseOverswipeConfig, edgeHitWidth: null, isDetachable: true, }, }, animationInterpolators: { - into: buildStyleInterpolator(FromTheRight), - out: buildStyleInterpolator(ToTheLeft), + into: buildStyleInterpolator(directionMapping.FromTheEnd), + out: buildStyleInterpolator(directionMapping.ToTheStart), }, }, HorizontalSwipeJumpFromRight: { ...BaseConfig, gestures: { jumpBack: { - ...BaseRightToLeftGesture, + ...directionMapping.BaseEndToStartGesture, overswipe: BaseOverswipeConfig, edgeHitWidth: null, isDetachable: true, }, jumpForward: { - ...BaseLeftToRightGesture, + ...directionMapping.BaseStartToEndGesture, overswipe: BaseOverswipeConfig, edgeHitWidth: null, isDetachable: true, }, - pop: BaseRightToLeftGesture, + pop: directionMapping.BaseEndToStartGesture, }, animationInterpolators: { - into: buildStyleInterpolator(FromTheLeft), - out: buildStyleInterpolator(FadeToTheRight), + into: buildStyleInterpolator(directionMapping.FromTheStart), + out: buildStyleInterpolator(directionMapping.FadeToTheEnd), }, }, VerticalUpSwipeJump: { From 7e5fc179839d1f0e5b82df307e9a0ef19f2874b1 Mon Sep 17 00:00:00 2001 From: Mengjue Wang Date: Fri, 8 Jul 2016 12:02:44 -0700 Subject: [PATCH 046/241] Provide RTL support for SwipeableRow Summary: Flipped the gesture for SwipeableRow Provide RTL support for SwipeableRow. When developer set RTL layout in their app, all the animation and gesture will change to RTL status. Reviewed By: fkgozali Differential Revision: D3532919 fbshipit-source-id: 76fc94cdaa8457350e9bbe3366aa714c9eb45245 --- .../Experimental/SwipeableRow/SwipeableRow.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRow.js b/Libraries/Experimental/SwipeableRow/SwipeableRow.js index 39ec4b21902578..23a608902371a1 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableRow.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableRow.js @@ -34,6 +34,9 @@ const {PropTypes} = React; const emptyFunction = require('fbjs/lib/emptyFunction'); +const I18nManager = require('NativeModules').I18nManager || {}; +const IS_RTL = I18nManager.isRTL; + // NOTE: Eventually convert these consts to an input object of configurations // Position of the left of the swipable item when closed @@ -240,7 +243,8 @@ const SwipeableRow = React.createClass({ }, _isSwipingRightFromClosed(gestureState: Object): boolean { - return this._previousLeft === CLOSED_LEFT_POSITION && gestureState.dx > 0; + const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx; + return this._previousLeft === CLOSED_LEFT_POSITION && gestureStateDx > 0; }, _swipeFullSpeed(gestureState: Object): void { @@ -259,9 +263,10 @@ const SwipeableRow = React.createClass({ * swiping is available, but swiping right does not do anything * functionally. */ + const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx; return ( this._isSwipingRightFromClosed(gestureState) && - gestureState.dx > RIGHT_SWIPE_THRESHOLD + gestureStateDx > RIGHT_SWIPE_THRESHOLD ); }, @@ -290,7 +295,8 @@ const SwipeableRow = React.createClass({ }, _animateToOpenPosition(): void { - this._animateTo(-this.props.maxSwipeDistance); + const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance; + this._animateTo(-maxSwipeDistance); }, _animateToClosedPosition(duration: number = SWIPE_DURATION): void { @@ -306,8 +312,9 @@ const SwipeableRow = React.createClass({ * When swiping right, we want to bounce back past closed position on release * so users know they should swipe right to get content. */ + const swipeBounceBackDistance = IS_RTL ? -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE : RIGHT_SWIPE_BOUNCE_BACK_DISTANCE; this._animateTo( - -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE, + -swipeBounceBackDistance, duration, this._animateToClosedPositionDuringBounce, ); @@ -330,8 +337,7 @@ const SwipeableRow = React.createClass({ }, _handlePanResponderEnd(event: Object, gestureState: Object): void { - const horizontalDistance = gestureState.dx; - + const horizontalDistance = IS_RTL ? -gestureState.dx : gestureState.dx; if (this._isSwipingRightFromClosed(gestureState)) { this.props.onOpen(); this._animateBounceBack(RIGHT_SWIPE_BOUNCE_BACK_DURATION); From 7ee2f48451338c840e479fb23dd57e5fe90b0e2a Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Fri, 8 Jul 2016 12:19:21 -0700 Subject: [PATCH 047/241] Remove left-over ctx property from RCTJavaScriptContext Summary: This was left over from a previous change. Reviewed By: javache Differential Revision: D3534376 fbshipit-source-id: 6932364d1c32d8fbdf56c642893f9ea5e6dc1fee --- React/Executors/RCTJSCExecutor.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index a9d2e60b8c64cd..845c4d96faf7a5 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -77,7 +77,6 @@ @implementation RCTCookieMap @end @interface RCTJavaScriptContext : NSObject @property (nonatomic, strong, readonly) JSContext *context; -@property (nonatomic, assign, readonly) JSGlobalContextRef ctx; - (instancetype)initWithJSContext:(JSContext *)context onThread:(NSThread *)javaScriptThread NS_DESIGNATED_INITIALIZER; From 294173a4273cec9487a2c76465dde2d8c7a699b7 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Fri, 8 Jul 2016 12:19:26 -0700 Subject: [PATCH 048/241] Organize ivars in RCTJSCExecutor Reviewed By: javache Differential Revision: D3534380 fbshipit-source-id: 364213f24256602471a5b3cd1afe6c7be4aab743 --- React/Executors/RCTJSCExecutor.mm | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 845c4d96faf7a5..349b0cbe0ff81f 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -132,16 +132,18 @@ - (void)invalidate @implementation RCTJSCExecutor { - RCTJavaScriptContext *_context; + // Set at init time: + BOOL _useCustomJSCLibrary; NSThread *_javaScriptThread; - RandomAccessBundleData _randomAccessBundle; - JSValueRef _batchedBridgeRef; - + // Set at setUp time: + RCTPerformanceLogger *_performanceLogger; RCTJSCWrapper *_jscWrapper; - BOOL _useCustomJSCLibrary; + RCTJavaScriptContext *_context; - RCTPerformanceLogger *_performanceLogger; + // Set as needed: + RandomAccessBundleData _randomAccessBundle; + JSValueRef _batchedBridgeRef; } @synthesize valid = _valid; From 39cb110c5b67cffeb855332304c4f4731a828ce8 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Fri, 8 Jul 2016 12:19:27 -0700 Subject: [PATCH 049/241] Add the ability to pre-create the JavaScript thread with RCTJSCExecutor Summary: This can be used to create a JavaScript thread and `JSContext` in advance, then supply them to the `RCTJSCExecutor` at creation time later. Reviewed By: javache Differential Revision: D3534553 fbshipit-source-id: 99ccf711928cd12e84c9fbe142c6d19a7af55d07 --- React/Executors/RCTJSCExecutor.h | 16 ++++ React/Executors/RCTJSCExecutor.mm | 145 +++++++++++++++++++++++------- 2 files changed, 130 insertions(+), 31 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.h b/React/Executors/RCTJSCExecutor.h index f450277c005b6a..474db76e2c97f5 100644 --- a/React/Executors/RCTJSCExecutor.h +++ b/React/Executors/RCTJSCExecutor.h @@ -25,6 +25,17 @@ RCT_EXTERN NSString *const RCTJSCThreadName; */ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification; +/** + * @experimental + * May be used to pre-create the JSContext to make RCTJSCExecutor creation less costly. + * Avoid using this; it's experimental and is not likely to be supported long-term. + */ +@interface RCTJSContextProvider : NSObject + +- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary; + +@end + /** * Uses a JavaScriptCore context as the execution engine. */ @@ -43,6 +54,11 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification; */ - (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary; +/** + * Pass a RCTJSContextProvider object to use an NSThread/JSContext pair that have already been created. + */ +- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider; + /** * Create a NSError from a JSError object. * diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 349b0cbe0ff81f..9e6579f2d370ae 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -74,6 +74,18 @@ @interface RCTCookieMap : NSObject @implementation RCTCookieMap @end #endif +struct RCTJSContextData { + BOOL useCustomJSCLibrary; + NSThread *javaScriptThread; + JSContext *context; + RCTJSCWrapper *jscWrapper; +}; + +@interface RCTJSContextProvider () +/** May only be called once, or deadlock will result. */ +- (RCTJSContextData)data; +@end + @interface RCTJavaScriptContext : NSObject @property (nonatomic, strong, readonly) JSContext *context; @@ -271,6 +283,21 @@ + (void)runRunLoopThread } } +static NSThread *newJavaScriptThread(void) +{ + NSThread *javaScriptThread = [[NSThread alloc] initWithTarget:[RCTJSCExecutor class] + selector:@selector(runRunLoopThread) + object:nil]; + javaScriptThread.name = RCTJSCThreadName; + if ([javaScriptThread respondsToSelector:@selector(setQualityOfService:)]) { + [javaScriptThread setQualityOfService:NSOperationQualityOfServiceUserInteractive]; + } else { + javaScriptThread.threadPriority = [NSThread mainThread].threadPriority; + } + [javaScriptThread start]; + return javaScriptThread; +} + - (void)setBridge:(RCTBridge *)bridge { _bridge = bridge; @@ -287,21 +314,22 @@ - (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary if (self = [super init]) { _useCustomJSCLibrary = useCustomJSCLibrary; _valid = YES; + _javaScriptThread = newJavaScriptThread(); + } - _javaScriptThread = [[NSThread alloc] initWithTarget:[self class] - selector:@selector(runRunLoopThread) - object:nil]; - _javaScriptThread.name = RCTJSCThreadName; - - if ([_javaScriptThread respondsToSelector:@selector(setQualityOfService:)]) { - [_javaScriptThread setQualityOfService:NSOperationQualityOfServiceUserInteractive]; - } else { - _javaScriptThread.threadPriority = [NSThread mainThread].threadPriority; - } + return self; +} - [_javaScriptThread start]; +- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider +{ + if (self = [super init]) { + const RCTJSContextData data = JSContextProvider.data; + _useCustomJSCLibrary = data.useCustomJSCLibrary; + _valid = YES; + _javaScriptThread = data.javaScriptThread; + _jscWrapper = data.jscWrapper; + _context = [[RCTJavaScriptContext alloc] initWithJSContext:data.context onThread:_javaScriptThread]; } - return self; } @@ -341,26 +369,25 @@ - (void)setUp return; } - [self->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary]; - self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary); - [self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; - - RCTAssert(self->_context == nil, @"Didn't expect to set up twice"); - JSContext *context = [self->_jscWrapper->JSContext new]; - self->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:self->_javaScriptThread]; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification - object:context]; - - if (self->_jscWrapper->configureJSContextForIOS != NULL) { - NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; - RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); - if (cachesPath) { - self->_jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]); - } + JSContext *context = nil; + if (self->_jscWrapper) { + RCTAssert(self->_context != nil, @"If wrapper was pre-initialized, context should be too"); + context = self->_context.context; + } else { + [self->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary]; + self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary); + [self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary]; + + RCTAssert(self->_context == nil, @"Didn't expect to set up twice"); + context = [self->_jscWrapper->JSContext new]; + self->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:self->_javaScriptThread]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification + object:context]; + + configureCacheOnContext(context, self->_jscWrapper); + installBasicSynchronousHooksOnContext(context); } - [[self class] installSynchronousHooksOnContext:context]; - __weak RCTJSCExecutor *weakSelf = self; context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) { @@ -430,7 +457,20 @@ - (void)setUp }]; } -+ (void)installSynchronousHooksOnContext:(JSContext *)context +/** If configureJSContextForIOS is available on jscWrapper, calls it with the correct parameters. */ +static void configureCacheOnContext(JSContext *context, RCTJSCWrapper *jscWrapper) +{ + if (jscWrapper->configureJSContextForIOS != NULL) { + NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; + RCTAssert(cachesPath != nil, @"cachesPath should not be nil"); + if (cachesPath) { + jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]); + } + } +} + +/** Installs synchronous hooks that don't require a weak reference back to the RCTJSCExecutor. */ +static void installBasicSynchronousHooksOnContext(JSContext *context) { context[@"noop"] = ^{}; context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) { @@ -889,3 +929,46 @@ - (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error } @end + +@implementation RCTJSContextProvider +{ + dispatch_semaphore_t _semaphore; + BOOL _useCustomJSCLibrary; + NSThread *_javaScriptThread; + JSContext *_context; + RCTJSCWrapper *_jscWrapper; +} + +- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary +{ + if (self = [super init]) { + _semaphore = dispatch_semaphore_create(0); + _useCustomJSCLibrary = useCustomJSCLibrary; + _javaScriptThread = newJavaScriptThread(); + [self performSelector:@selector(_createContext) onThread:_javaScriptThread withObject:nil waitUntilDone:NO]; + } + return self; +} + +- (void)_createContext +{ + _jscWrapper = RCTJSCWrapperCreate(_useCustomJSCLibrary); + _context = [_jscWrapper->JSContext new]; + configureCacheOnContext(_context, _jscWrapper); + installBasicSynchronousHooksOnContext(_context); + dispatch_semaphore_signal(_semaphore); +} + +- (RCTJSContextData)data +{ + // Be sure this method is only called once, otherwise it will hang here forever: + dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); + return { + .useCustomJSCLibrary = _useCustomJSCLibrary, + .javaScriptThread = _javaScriptThread, + .context = _context, + .jscWrapper = _jscWrapper, + }; +} + +@end From 2426b3b5ec1e028ef5f9ddb253d0c0c774834a48 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Fri, 8 Jul 2016 12:29:23 -0700 Subject: [PATCH 050/241] Prevent tests with invalid UTF-8 from failing when jest reads them from the cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: The utf-8 test was failing when run from Jest’s cache, because it contained unicode escape sequences for stray high and low surrogates. Somewhere in the process of transpiling, caching on disk, and reading from the cache, those stray surrogates are changed to U+FFFD (REPLACEMENT CHARACTER / �). This diffs changes the method of creating these strings from literals to `String.fromCharCode`, which survives the process as intended. Reviewed By: bestander Differential Revision: D3534711 fbshipit-source-id: 365bace77a1f914e6e2fbb3430b3e0ea6cec5e83 --- Libraries/Utilities/__tests__/utf8-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/Utilities/__tests__/utf8-test.js b/Libraries/Utilities/__tests__/utf8-test.js index 51dd50aaae1622..ec91f020588d73 100644 --- a/Libraries/Utilities/__tests__/utf8-test.js +++ b/Libraries/Utilities/__tests__/utf8-test.js @@ -50,13 +50,13 @@ }); it('allows for stray high surrogates', () => { - const arrayBuffer = encode('a\ud8c6b'); + const arrayBuffer = encode(String.fromCharCode(0x61, 0xd8c6, 0x62)); expect(new Uint8Array(arrayBuffer)).toEqual( new Uint8Array([0x61, 0xed, 0xa3, 0x86, 0x62])); }); it('allows for stray low surrogates', () => { - const arrayBuffer = encode('a\ude19b'); + const arrayBuffer = encode(String.fromCharCode(0x61, 0xde19, 0x62)); expect(new Uint8Array(arrayBuffer)).toEqual( new Uint8Array([0x61, 0xed, 0xb8, 0x99, 0x62])); }); From dd06b74157efa0ebd4980eb79d92b3f0e4be35d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Gregorczyk?= Date: Fri, 8 Jul 2016 13:52:31 -0700 Subject: [PATCH 051/241] Make SoLoader an external dependency Reviewed By: bestander Differential Revision: D3535233 fbshipit-source-id: 9fddb654123a7606d46069a98e2f68dec7f520fa --- ReactAndroid/build.gradle | 1 + .../com/facebook/soloader/ApkSoSource.java | 171 ----------- .../soloader/java/com/facebook/soloader/BUCK | 25 +- .../facebook/soloader/DirectorySoSource.java | 76 ----- .../java/com/facebook/soloader/Elf32_Dyn.java | 14 - .../com/facebook/soloader/Elf32_Ehdr.java | 26 -- .../com/facebook/soloader/Elf32_Phdr.java | 20 -- .../com/facebook/soloader/Elf32_Shdr.java | 22 -- .../java/com/facebook/soloader/Elf64_Dyn.java | 14 - .../com/facebook/soloader/Elf64_Ehdr.java | 26 -- .../com/facebook/soloader/Elf64_Phdr.java | 20 -- .../com/facebook/soloader/Elf64_Shdr.java | 22 -- .../com/facebook/soloader/ExoSoSource.java | 177 ----------- .../com/facebook/soloader/FileLocker.java | 48 --- .../java/com/facebook/soloader/MinElf.java | 282 ------------------ .../com/facebook/soloader/NativeLibrary.java | 93 ------ .../com/facebook/soloader/NoopSoSource.java | 28 -- .../java/com/facebook/soloader/SoLoader.java | 237 --------------- .../java/com/facebook/soloader/SoSource.java | 57 ---- .../java/com/facebook/soloader/SysUtil.java | 205 ------------- .../java/com/facebook/soloader/genstructs.sh | 35 --- .../java/com/facebook/soloader/soloader.pro | 6 - 22 files changed, 9 insertions(+), 1596 deletions(-) delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ApkSoSource.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/DirectorySoSource.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Dyn.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Ehdr.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Phdr.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Shdr.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Dyn.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Ehdr.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Phdr.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Shdr.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ExoSoSource.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/FileLocker.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/MinElf.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NativeLibrary.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NoopSoSource.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoLoader.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoSource.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SysUtil.java delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/genstructs.sh delete mode 100644 ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/soloader.pro diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 407316d02102bf..7a142cefe04c2c 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -266,6 +266,7 @@ dependencies { compile 'com.android.support:recyclerview-v7:23.0.1' compile 'com.facebook.fresco:fresco:0.11.0' compile 'com.facebook.fresco:imagepipeline-okhttp3:0.11.0' + compile 'com.facebook.soloader:soloader:0.1.0' compile 'com.fasterxml.jackson.core:jackson-core:2.2.3' compile 'com.google.code.findbugs:jsr305:3.0.0' compile 'com.squareup.okhttp3:okhttp:3.2.0' diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ApkSoSource.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ApkSoSource.java deleted file mode 100644 index eba1eeaf586503..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ApkSoSource.java +++ /dev/null @@ -1,171 +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. - */ - -package com.facebook.soloader; - -import java.io.File; -import java.io.IOException; -import android.content.Context; - -import java.util.jar.JarFile; -import java.util.jar.JarEntry; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import android.os.Build; -import android.system.Os; -import android.system.ErrnoException; - -import java.util.HashMap; -import java.util.Map; -import java.util.Enumeration; - -import java.io.InputStream; -import java.io.FileOutputStream; - -import android.util.Log; - -/** - * {@link SoSource} that extracts libraries from an APK to the filesystem. - */ -public class ApkSoSource extends DirectorySoSource { - - private static final String TAG = SoLoader.TAG; - private static final boolean DEBUG = SoLoader.DEBUG; - - /** - * Make a new ApkSoSource that extracts DSOs from our APK instead of relying on the system to do - * the extraction for us. - * - * @param context Application context - */ - public ApkSoSource(Context context) throws IOException { - // - // Initialize a normal DirectorySoSource that will load from our extraction directory. At this - // point, the directory may be empty or contain obsolete libraries, but that's okay. - // - - super(SysUtil.createLibsDirectory(context), DirectorySoSource.RESOLVE_DEPENDENCIES); - - // - // Synchronize the contents of that directory with the library payload in our APK, deleting and - // extracting as needed. - // - - try (JarFile apk = new JarFile(context.getApplicationInfo().publicSourceDir)) { - File libsDir = super.soDirectory; - - if (DEBUG) { - Log.v(TAG, "synchronizing log directory: " + libsDir); - } - - Map providedLibraries = findProvidedLibraries(apk); - try (FileLocker lock = SysUtil.lockLibsDirectory(context)) { - // Delete files in libsDir that we don't provide or that are out of date. Forget about any - // libraries that are up-to-date already so we don't unpack them below. - File extantFiles[] = libsDir.listFiles(); - for (int i = 0; i < extantFiles.length; ++i) { - File extantFile = extantFiles[i]; - - if (DEBUG) { - Log.v(TAG, "considering libdir file: " + extantFile); - } - - String name = extantFile.getName(); - SoInfo so = providedLibraries.get(name); - boolean shouldDelete = - (so == null || - so.entry.getSize() != extantFile.length() || - so.entry.getTime() != extantFile.lastModified()); - boolean upToDate = (so != null && !shouldDelete); - - if (shouldDelete) { - if (DEBUG) { - Log.v(TAG, "deleting obsolete or unexpected file: " + extantFile); - } - SysUtil.deleteOrThrow(extantFile); - } - - if (upToDate) { - if (DEBUG) { - Log.v(TAG, "found up-to-date library: " + extantFile); - } - providedLibraries.remove(name); - } - } - - // Now extract any libraries left in providedLibraries; we removed all the up-to-date ones. - for (SoInfo so : providedLibraries.values()) { - JarEntry entry = so.entry; - try (InputStream is = apk.getInputStream(entry)) { - if (DEBUG) { - Log.v(TAG, "extracting library: " + so.soName); - } - SysUtil.reliablyCopyExecutable( - is, - new File(libsDir, so.soName), - entry.getSize(), - entry.getTime()); - } - - SysUtil.freeCopyBuffer(); - } - } - } - } - - /** - * Find the shared libraries provided in this APK and supported on this system. Each returend - * SoInfo points to the most preferred version of that library bundled with the given APK: for - * example, if we're on an armv7-a system and we have both arm and armv7-a versions of libfoo, the - * returned entry for libfoo points to the armv7-a version of libfoo. - * - * The caller owns the returned value and may mutate it. - * - * @param apk Opened application APK file - * @return Map of sonames to SoInfo instances - */ - private static Map findProvidedLibraries(JarFile apk) { - // Subgroup 1: ABI. Subgroup 2: soname. - Pattern libPattern = Pattern.compile("^lib/([^/]+)/([^/]+\\.so)$"); - HashMap providedLibraries = new HashMap<>(); - String[] supportedAbis = SysUtil.getSupportedAbis(); - Enumeration entries = apk.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - Matcher m = libPattern.matcher(entry.getName()); - if (m.matches()) { - String libraryAbi = m.group(1); - String soName = m.group(2); - int abiScore = SysUtil.findAbiScore(supportedAbis, libraryAbi); - if (abiScore >= 0) { - SoInfo so = providedLibraries.get(soName); - if (so == null || abiScore < so.abiScore) { - providedLibraries.put(soName, new SoInfo(soName, entry, abiScore)); - } - } - } - } - - return providedLibraries; - } - - private static final class SoInfo { - public final String soName; - public final JarEntry entry; - public final int abiScore; - - SoInfo(String soName, JarEntry entry, int abiScore) { - this.soName = soName; - this.entry = entry; - this.abiScore = abiScore; - } - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK index 322ba2204e2016..f9c872b0f87f4c 100644 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK +++ b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK @@ -1,20 +1,11 @@ -include_defs('//ReactAndroid/DEFS') - -android_library( - name = 'soloader', - srcs = glob(['*.java']), - proguard_config = 'soloader.pro', - deps = [ - react_native_dep('third-party/java/jsr-305:jsr-305'), - # Be very careful adding new dependencies here, because this code - # has to run very early in the app startup process. - # Definitely do *not* depend on lib-base or guava. - ], - visibility = [ - 'PUBLIC', - ], +android_prebuilt_aar( + name = 'soloader', + aar = ':soloader-binary-aar', + visibility = ['PUBLIC'], ) -project_config( - src_target = ':soloader', +remote_file( + name = 'soloader-binary-aar', + url = 'mvn:com.facebook.soloader:soloader:aar:0.1.0', + sha1 = '918573465c94c6bc9bad48ef259f1e0cd6543c1b', ) diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/DirectorySoSource.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/DirectorySoSource.java deleted file mode 100644 index 47cdb02320ddc6..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/DirectorySoSource.java +++ /dev/null @@ -1,76 +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. - */ - -package com.facebook.soloader; - -import java.io.File; -import java.io.IOException; - -/** - * {@link SoSource} that finds shared libraries in a given directory. - */ -public class DirectorySoSource extends SoSource { - - public static final int RESOLVE_DEPENDENCIES = 1; - public static final int ON_LD_LIBRARY_PATH = 2; - - protected final File soDirectory; - private final int flags; - - /** - * Make a new DirectorySoSource. If {@code flags} contains {@code RESOLVE_DEPENDENCIES}, - * recursively load dependencies for shared objects loaded from this directory. (We shouldn't - * need to resolve dependencies for libraries loaded from system directories: the dynamic linker - * is smart enough to do it on its own there.) - */ - public DirectorySoSource(File soDirectory, int flags) { - this.soDirectory = soDirectory; - this.flags = flags; - } - - @Override - public int loadLibrary(String soName, int loadFlags) throws IOException { - File soFile = new File(soDirectory, soName); - if (!soFile.exists()) { - return LOAD_RESULT_NOT_FOUND; - } - - if ((loadFlags & LOAD_FLAG_ALLOW_IMPLICIT_PROVISION) != 0 && - (flags & ON_LD_LIBRARY_PATH) != 0) { - return LOAD_RESULT_IMPLICITLY_PROVIDED; - } - - if ((flags & RESOLVE_DEPENDENCIES) != 0) { - String dependencies[] = MinElf.extract_DT_NEEDED(soFile); - for (int i = 0; i < dependencies.length; ++i) { - String dependency = dependencies[i]; - if (dependency.startsWith("/")) { - continue; - } - - SoLoader.loadLibraryBySoName( - dependency, - (loadFlags | LOAD_FLAG_ALLOW_IMPLICIT_PROVISION)); - } - } - - System.load(soFile.getAbsolutePath()); - return LOAD_RESULT_LOADED; - } - - @Override - public File unpackLibrary(String soName) throws IOException { - File soFile = new File(soDirectory, soName); - if (soFile.exists()) { - return soFile; - } - - return null; - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Dyn.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Dyn.java deleted file mode 100644 index a9ec0713dd1722..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Dyn.java +++ /dev/null @@ -1,14 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf32_Dyn { - public static final int d_tag = 0x0; - public static final int d_un = 0x4; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Ehdr.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Ehdr.java deleted file mode 100644 index a398ffe7898f3d..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Ehdr.java +++ /dev/null @@ -1,26 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf32_Ehdr { - public static final int e_ident = 0x0; - public static final int e_type = 0x10; - public static final int e_machine = 0x12; - public static final int e_version = 0x14; - public static final int e_entry = 0x18; - public static final int e_phoff = 0x1c; - public static final int e_shoff = 0x20; - public static final int e_flags = 0x24; - public static final int e_ehsize = 0x28; - public static final int e_phentsize = 0x2a; - public static final int e_phnum = 0x2c; - public static final int e_shentsize = 0x2e; - public static final int e_shnum = 0x30; - public static final int e_shstrndx = 0x32; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Phdr.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Phdr.java deleted file mode 100644 index 95e2c27b29555b..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Phdr.java +++ /dev/null @@ -1,20 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf32_Phdr { - public static final int p_type = 0x0; - public static final int p_offset = 0x4; - public static final int p_vaddr = 0x8; - public static final int p_paddr = 0xc; - public static final int p_filesz = 0x10; - public static final int p_memsz = 0x14; - public static final int p_flags = 0x18; - public static final int p_align = 0x1c; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Shdr.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Shdr.java deleted file mode 100644 index 35fc8599cd0880..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf32_Shdr.java +++ /dev/null @@ -1,22 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf32_Shdr { - public static final int sh_name = 0x0; - public static final int sh_type = 0x4; - public static final int sh_flags = 0x8; - public static final int sh_addr = 0xc; - public static final int sh_offset = 0x10; - public static final int sh_size = 0x14; - public static final int sh_link = 0x18; - public static final int sh_info = 0x1c; - public static final int sh_addralign = 0x20; - public static final int sh_entsize = 0x24; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Dyn.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Dyn.java deleted file mode 100644 index 89f2ddbdf13e37..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Dyn.java +++ /dev/null @@ -1,14 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf64_Dyn { - public static final int d_tag = 0x0; - public static final int d_un = 0x8; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Ehdr.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Ehdr.java deleted file mode 100644 index 4f6fa44ce28f7a..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Ehdr.java +++ /dev/null @@ -1,26 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf64_Ehdr { - public static final int e_ident = 0x0; - public static final int e_type = 0x10; - public static final int e_machine = 0x12; - public static final int e_version = 0x14; - public static final int e_entry = 0x18; - public static final int e_phoff = 0x20; - public static final int e_shoff = 0x28; - public static final int e_flags = 0x30; - public static final int e_ehsize = 0x34; - public static final int e_phentsize = 0x36; - public static final int e_phnum = 0x38; - public static final int e_shentsize = 0x3a; - public static final int e_shnum = 0x3c; - public static final int e_shstrndx = 0x3e; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Phdr.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Phdr.java deleted file mode 100644 index b6436cbcb08e8a..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Phdr.java +++ /dev/null @@ -1,20 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf64_Phdr { - public static final int p_type = 0x0; - public static final int p_flags = 0x4; - public static final int p_offset = 0x8; - public static final int p_vaddr = 0x10; - public static final int p_paddr = 0x18; - public static final int p_filesz = 0x20; - public static final int p_memsz = 0x28; - public static final int p_align = 0x30; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Shdr.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Shdr.java deleted file mode 100644 index 36e8693d467096..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/Elf64_Shdr.java +++ /dev/null @@ -1,22 +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. - */ -// AUTOMATICALLY GENERATED CODE. Regenerate with genstructs.sh. -package com.facebook.soloader; -public final class Elf64_Shdr { - public static final int sh_name = 0x0; - public static final int sh_type = 0x4; - public static final int sh_flags = 0x8; - public static final int sh_addr = 0x10; - public static final int sh_offset = 0x18; - public static final int sh_size = 0x20; - public static final int sh_link = 0x28; - public static final int sh_info = 0x2c; - public static final int sh_addralign = 0x30; - public static final int sh_entsize = 0x38; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ExoSoSource.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ExoSoSource.java deleted file mode 100644 index 1520aa1c962ca2..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/ExoSoSource.java +++ /dev/null @@ -1,177 +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. - */ - -package com.facebook.soloader; - -import java.io.File; -import java.io.IOException; -import android.content.Context; - -import java.util.jar.JarFile; -import java.util.jar.JarEntry; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import android.os.Build; -import android.system.Os; -import android.system.ErrnoException; - -import java.util.HashMap; -import java.util.Map; -import java.util.Enumeration; - -import java.io.InputStream; -import java.io.FileOutputStream; -import java.io.FileInputStream; -import java.io.BufferedReader; -import java.io.FileReader; - -import android.util.Log; - -/** - * {@link SoSource} that retrieves libraries from an exopackage repository. - */ -public class ExoSoSource extends DirectorySoSource { - - private static final String TAG = SoLoader.TAG; - private static final boolean DEBUG = SoLoader.DEBUG; - - /** - * @param context Application context - */ - public ExoSoSource(Context context) throws IOException { - // - // Initialize a normal DirectorySoSource that will load from our extraction directory. At this - // point, the directory may be empty or contain obsolete libraries, but that's okay. - // - - super(SysUtil.createLibsDirectory(context), DirectorySoSource.RESOLVE_DEPENDENCIES); - - // - // Synchronize the contents of that directory with the library payload in our APK, deleting and - // extracting as needed. - // - - File libsDir = super.soDirectory; - - if (DEBUG) { - Log.v(TAG, "synchronizing log directory: " + libsDir); - } - - Map providedLibraries = findProvidedLibraries(context); - try (FileLocker lock = SysUtil.lockLibsDirectory(context)) { - // Delete files in libsDir that we don't provide or that are out of date. Forget about any - // libraries that are up-to-date already so we don't unpack them below. - File extantFiles[] = libsDir.listFiles(); - for (int i = 0; i < extantFiles.length; ++i) { - File extantFile = extantFiles[i]; - - if (DEBUG) { - Log.v(TAG, "considering libdir file: " + extantFile); - } - - String name = extantFile.getName(); - File sourceFile = providedLibraries.get(name); - boolean shouldDelete = - (sourceFile == null || - sourceFile.length() != extantFile.length() || - sourceFile.lastModified() != extantFile.lastModified()); - boolean upToDate = (sourceFile != null && !shouldDelete); - - if (shouldDelete) { - if (DEBUG) { - Log.v(TAG, "deleting obsolete or unexpected file: " + extantFile); - } - SysUtil.deleteOrThrow(extantFile); - } - - if (upToDate) { - if (DEBUG) { - Log.v(TAG, "found up-to-date library: " + extantFile); - } - providedLibraries.remove(name); - } - } - - // Now extract any libraries left in providedLibraries; we removed all the up-to-date ones. - for (String soName : providedLibraries.keySet()) { - File sourceFile = providedLibraries.get(soName); - try (InputStream is = new FileInputStream(sourceFile)) { - if (DEBUG) { - Log.v(TAG, "extracting library: " + soName); - } - SysUtil.reliablyCopyExecutable( - is, - new File(libsDir, soName), - sourceFile.length(), - sourceFile.lastModified()); - } - - SysUtil.freeCopyBuffer(); - } - } - } - - /** - * Find the shared libraries provided through the exopackage directory and supported on this - * system. Each returend SoInfo points to the most preferred version of that library included in - * our exopackage directory: for example, if we're on an armv7-a system and we have both arm and - * armv7-a versions of libfoo, the returned entry for libfoo points to the armv7-a version of - * libfoo. - * - * The caller owns the returned value and may mutate it. - * - * @param context Application context - * @return Map of sonames to providing files - */ - private static Map findProvidedLibraries(Context context) throws IOException { - File exoDir = new File( - "/data/local/tmp/exopackage/" - + context.getPackageName() - + "/native-libs/"); - - HashMap providedLibraries = new HashMap<>(); - for (String abi : SysUtil.getSupportedAbis()) { - File abiDir = new File(exoDir, abi); - if (!abiDir.isDirectory()) { - continue; - } - - File metadata = new File(abiDir, "metadata.txt"); - if (!metadata.isFile()) { - continue; - } - - try (FileReader fr = new FileReader(metadata); - BufferedReader br = new BufferedReader(fr)) { - String line; - while ((line = br.readLine()) != null) { - if (line.length() == 0) { - continue; - } - - int sep = line.indexOf(' '); - if (sep == -1) { - throw new RuntimeException("illegal line in exopackage metadata: [" + line + "]"); - } - - String soName = line.substring(0, sep) + ".so"; - String backingFile = line.substring(sep + 1); - - if (!providedLibraries.containsKey(soName)) { - providedLibraries.put(soName, new File(abiDir, backingFile)); - } - } - } - } - - return providedLibraries; - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/FileLocker.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/FileLocker.java deleted file mode 100644 index 96a11f994810a8..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/FileLocker.java +++ /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. - */ - -package com.facebook.soloader; -import java.io.FileOutputStream; -import java.io.File; -import java.io.IOException; -import java.nio.channels.FileLock; -import java.io.Closeable; - -public final class FileLocker implements Closeable { - - private final FileOutputStream mLockFileOutputStream; - private final FileLock mLock; - - public static FileLocker lock(File lockFile) throws IOException { - return new FileLocker(lockFile); - } - - private FileLocker(File lockFile) throws IOException { - mLockFileOutputStream = new FileOutputStream(lockFile); - FileLock lock = null; - try { - lock = mLockFileOutputStream.getChannel().lock(); - } finally { - if (lock == null) { - mLockFileOutputStream.close(); - } - } - - mLock = lock; - } - - @Override - public void close() throws IOException { - try { - mLock.release(); - } finally { - mLockFileOutputStream.close(); - } - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/MinElf.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/MinElf.java deleted file mode 100644 index 0477ad71dc996f..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/MinElf.java +++ /dev/null @@ -1,282 +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. - */ - -package com.facebook.soloader; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.File; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; - -/** - * Extract SoLoader boottsrap information from an ELF file. This is not a general purpose ELF - * library. - * - * See specification at http://www.sco.com/developers/gabi/latest/contents.html. You will not be - * able to verify the operation of the functions below without having read the ELF specification. - */ -public final class MinElf { - - public static final int ELF_MAGIC = 0x464c457f; - - public static final int DT_NULL = 0; - public static final int DT_NEEDED = 1; - public static final int DT_STRTAB = 5; - - public static final int PT_LOAD = 1; - public static final int PT_DYNAMIC = 2; - - public static final int PN_XNUM = 0xFFFF; - - public static String[] extract_DT_NEEDED(File elfFile) throws IOException { - FileInputStream is = new FileInputStream(elfFile); - try { - return extract_DT_NEEDED(is.getChannel()); - } finally { - is.close(); // Won't throw - } - } - - /** - * Treating {@code bb} as an ELF file, extract all the DT_NEEDED entries from its dynamic section. - * - * @param fc FileChannel referring to ELF file - * @return Array of strings, one for each DT_NEEDED entry, in file order - */ - public static String[] extract_DT_NEEDED(FileChannel fc) - throws IOException { - - // - // All constants below are fixed by the ELF specification and are the offsets of fields within - // the elf.h data structures. - // - - ByteBuffer bb = ByteBuffer.allocate(8 /* largest read unit */); - - // Read ELF header. - - bb.order(ByteOrder.LITTLE_ENDIAN); - if (getu32(fc, bb, Elf32_Ehdr.e_ident) != ELF_MAGIC) { - throw new ElfError("file is not ELF"); - } - - boolean is32 = (getu8(fc, bb, Elf32_Ehdr.e_ident + 0x4) == 1); - if (getu8(fc, bb, Elf32_Ehdr.e_ident + 0x5) == 2) { - bb.order(ByteOrder.BIG_ENDIAN); - } - - // Offsets above are identical in 32- and 64-bit cases. - - // Find the offset of the dynamic linking information. - - long e_phoff = is32 - ? getu32(fc, bb, Elf32_Ehdr.e_phoff) - : get64(fc, bb, Elf64_Ehdr.e_phoff); - - long e_phnum = is32 - ? getu16(fc, bb, Elf32_Ehdr.e_phnum) - : getu16(fc, bb, Elf64_Ehdr.e_phnum); - - int e_phentsize = is32 - ? getu16(fc, bb, Elf32_Ehdr.e_phentsize) - : getu16(fc, bb, Elf64_Ehdr.e_phentsize); - - if (e_phnum == PN_XNUM) { // Overflowed into section[0].sh_info - - long e_shoff = is32 - ? getu32(fc, bb, Elf32_Ehdr.e_shoff) - : get64(fc, bb, Elf64_Ehdr.e_shoff); - - long sh_info = is32 - ? getu32(fc, bb, e_shoff + Elf32_Shdr.sh_info) - : getu32(fc, bb, e_shoff + Elf64_Shdr.sh_info); - - e_phnum = sh_info; - } - - long dynStart = 0; - long phdr = e_phoff; - - for (long i = 0; i < e_phnum; ++i) { - long p_type = is32 - ? getu32(fc, bb, phdr + Elf32_Phdr.p_type) - : getu32(fc, bb, phdr + Elf64_Phdr.p_type); - - if (p_type == PT_DYNAMIC) { - long p_offset = is32 - ? getu32(fc, bb, phdr + Elf32_Phdr.p_offset) - : get64(fc, bb, phdr + Elf64_Phdr.p_offset); - - dynStart = p_offset; - break; - } - - phdr += e_phentsize; - } - - if (dynStart == 0) { - throw new ElfError("ELF file does not contain dynamic linking information"); - } - - // Walk the items in the dynamic section, counting the DT_NEEDED entries. Also remember where - // the string table for those entries lives. That table is a pointer, which we translate to an - // offset below. - - long d_tag; - int nr_DT_NEEDED = 0; - long dyn = dynStart; - long ptr_DT_STRTAB = 0; - - do { - d_tag = is32 - ? getu32(fc, bb, dyn + Elf32_Dyn.d_tag) - : get64(fc, bb, dyn + Elf64_Dyn.d_tag); - - if (d_tag == DT_NEEDED) { - if (nr_DT_NEEDED == Integer.MAX_VALUE) { - throw new ElfError("malformed DT_NEEDED section"); - } - - nr_DT_NEEDED += 1; - } else if (d_tag == DT_STRTAB) { - ptr_DT_STRTAB = is32 - ? getu32(fc, bb, dyn + Elf32_Dyn.d_un) - : get64(fc, bb, dyn + Elf64_Dyn.d_un); - } - - dyn += is32 ? 8 : 16; - } while (d_tag != DT_NULL); - - if (ptr_DT_STRTAB == 0) { - throw new ElfError("Dynamic section string-table not found"); - } - - // Translate the runtime string table pointer we found above to a file offset. - - long off_DT_STRTAB = 0; - phdr = e_phoff; - - for (int i = 0; i < e_phnum; ++i) { - long p_type = is32 - ? getu32(fc, bb, phdr + Elf32_Phdr.p_type) - : getu32(fc, bb, phdr + Elf64_Phdr.p_type); - - if (p_type == PT_LOAD) { - long p_vaddr = is32 - ? getu32(fc, bb, phdr + Elf32_Phdr.p_vaddr) - : get64(fc, bb, phdr + Elf64_Phdr.p_vaddr); - - long p_memsz = is32 - ? getu32(fc, bb, phdr + Elf32_Phdr.p_memsz) - : get64(fc, bb, phdr + Elf64_Phdr.p_memsz); - - if (p_vaddr <= ptr_DT_STRTAB && ptr_DT_STRTAB < p_vaddr + p_memsz) { - long p_offset = is32 - ? getu32(fc, bb, phdr + Elf32_Phdr.p_offset) - : get64(fc, bb, phdr + Elf64_Phdr.p_offset); - - off_DT_STRTAB = p_offset + (ptr_DT_STRTAB - p_vaddr); - break; - } - } - - phdr += e_phentsize; - } - - if (off_DT_STRTAB == 0) { - throw new ElfError("did not find file offset of DT_STRTAB table"); - } - - String[] needed = new String[nr_DT_NEEDED]; - - nr_DT_NEEDED = 0; - dyn = dynStart; - - do { - d_tag = is32 - ? getu32(fc, bb, dyn + Elf32_Dyn.d_tag) - : get64(fc, bb, dyn + Elf64_Dyn.d_tag); - - if (d_tag == DT_NEEDED) { - long d_val = is32 - ? getu32(fc, bb, dyn + Elf32_Dyn.d_un) - : get64(fc, bb, dyn + Elf64_Dyn.d_un); - - needed[nr_DT_NEEDED] = getSz(fc, bb, off_DT_STRTAB + d_val); - if (nr_DT_NEEDED == Integer.MAX_VALUE) { - throw new ElfError("malformed DT_NEEDED section"); - } - - nr_DT_NEEDED += 1; - } - - dyn += is32 ? 8 : 16; - } while (d_tag != DT_NULL); - - if (nr_DT_NEEDED != needed.length) { - throw new ElfError("malformed DT_NEEDED section"); - } - - return needed; - } - - private static String getSz(FileChannel fc, ByteBuffer bb, long offset) - throws IOException { - StringBuilder sb = new StringBuilder(); - short b; - while ((b = getu8(fc, bb, offset++)) != 0) { - sb.append((char) b); - } - - return sb.toString(); - } - - private static void read(FileChannel fc, ByteBuffer bb, int sz, long offset) - throws IOException { - bb.position(0); - bb.limit(sz); - if (fc.read(bb, offset) != sz) { - throw new ElfError("ELF file truncated"); - } - - bb.position(0); - } - - private static long get64(FileChannel fc, ByteBuffer bb, long offset) - throws IOException { - read(fc, bb, 8, offset); - return bb.getLong(); - } - - private static long getu32(FileChannel fc, ByteBuffer bb, long offset) - throws IOException { - read(fc, bb, 4, offset); - return bb.getInt() & 0xFFFFFFFFL; // signed -> unsigned - } - - private static int getu16(FileChannel fc, ByteBuffer bb, long offset) - throws IOException { - read(fc, bb, 2, offset); - return bb.getShort() & (int) 0xFFFF; // signed -> unsigned - } - - private static short getu8(FileChannel fc, ByteBuffer bb, long offset) - throws IOException { - read(fc, bb, 1, offset); - return (short) (bb.get() & 0xFF); // signed -> unsigned - } - - private static class ElfError extends RuntimeException { - ElfError(String why) { - super(why); - } - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NativeLibrary.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NativeLibrary.java deleted file mode 100644 index 7277474d6acce3..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NativeLibrary.java +++ /dev/null @@ -1,93 +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. - */ - -package com.facebook.soloader; - -import java.util.List; - -import android.util.Log; - -/** - * This is the base class for all the classes representing certain native library. - * For loading native libraries we should always inherit from this class and provide relevant - * information (libraries to load, code to test native call, dependencies?). - *

- * This instances should be singletons provided by DI. - *

- * This is a basic template but could be improved if we find the need. - */ -public abstract class NativeLibrary { - private static final String TAG = NativeLibrary.class.getName(); - - private final Object mLock; - private List mLibraryNames; - private Boolean mLoadLibraries; - private boolean mLibrariesLoaded; - private volatile UnsatisfiedLinkError mLinkError; - - protected NativeLibrary(List libraryNames) { - mLock = new Object(); - mLoadLibraries = true; - mLibrariesLoaded = false; - mLinkError = null; - mLibraryNames = libraryNames; - } - - /** - * safe loading of native libs - * @return true if native libs loaded properly, false otherwise - */ - public boolean loadLibraries() { - synchronized (mLock) { - if (mLoadLibraries == false) { - return mLibrariesLoaded; - } - try { - for (String name: mLibraryNames) { - SoLoader.loadLibrary(name); - } - initialNativeCheck(); - mLibrariesLoaded = true; - mLibraryNames = null; - } catch (UnsatisfiedLinkError error) { - Log.e(TAG, "Failed to load native lib: ", error); - mLinkError = error; - mLibrariesLoaded = false; - } - mLoadLibraries = false; - return mLibrariesLoaded; - } - } - - /** - * loads libraries (if not loaded yet), throws on failure - * @throws UnsatisfiedLinkError - */ - - public void ensureLoaded() throws UnsatisfiedLinkError { - if (!loadLibraries()) { - throw mLinkError; - } - } - - /** - * Override this method to make some concrete (quick and harmless) native call. - * This avoids lazy-loading some phones (LG) use when we call loadLibrary. If there's a problem - * we'll face an UnsupportedLinkError when first using the feature instead of here. - * This check force a check right when intended. - * This way clients of this library can know if it's loaded for sure or not. - * @throws UnsatisfiedLinkError if there was an error loading native library - */ - protected void initialNativeCheck() throws UnsatisfiedLinkError { - } - - public UnsatisfiedLinkError getError() { - return mLinkError; - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NoopSoSource.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NoopSoSource.java deleted file mode 100644 index cd5d15e48eeeed..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/NoopSoSource.java +++ /dev/null @@ -1,28 +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. - */ - -package com.facebook.soloader; - -import java.io.File; - -/** - * {@link SoSource} that does nothing and pretends to successfully load all libraries. - */ -public class NoopSoSource extends SoSource { - @Override - public int loadLibrary(String soName, int loadFlags) { - return LOAD_RESULT_LOADED; - } - - @Override - public File unpackLibrary(String soName) { - throw new UnsupportedOperationException( - "unpacking not supported in test mode"); - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoLoader.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoLoader.java deleted file mode 100644 index a070ed9a96898a..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoLoader.java +++ /dev/null @@ -1,237 +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. - */ - -package com.facebook.soloader; - -import java.io.BufferedOutputStream; -import java.io.Closeable; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.HashSet; -import java.util.ArrayList; -import java.io.FileNotFoundException; - -import java.util.Set; - -import javax.annotation.Nullable; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Build; -import android.os.StatFs; -import android.util.Log; - -import android.content.pm.ApplicationInfo; - -/** - * Note that {@link com.facebook.base.app.DelegatingApplication} will automatically register itself - * with SoLoader before running application-specific code; most applications do not need to call - * {@link #init} explicitly. - */ -@SuppressLint({ - "BadMethodUse-android.util.Log.v", - "BadMethodUse-android.util.Log.d", - "BadMethodUse-android.util.Log.i", - "BadMethodUse-android.util.Log.w", - "BadMethodUse-android.util.Log.e", -}) -public class SoLoader { - - /* package */ static final String TAG = "SoLoader"; - /* package */ static final boolean DEBUG = false; - - /** - * Ordered list of sources to consult when trying to load a shared library or one of its - * dependencies. {@code null} indicates that SoLoader is uninitialized. - */ - @Nullable private static SoSource[] sSoSources = null; - - /** - * Records the sonames (e.g., "libdistract.so") of shared libraries we've loaded. - */ - private static final Set sLoadedLibraries = new HashSet<>(); - - /** - * Initializes native code loading for this app; this class's other static facilities cannot be - * used until this {@link #init} is called. This method is idempotent: calls after the first are - * ignored. - * - * @param context - application context. - * @param isNativeExopackageEnabled - whether native exopackage feature is enabled in the build. - */ - public static synchronized void init(@Nullable Context context, boolean isNativeExopackageEnabled) { - if (sSoSources == null) { - ArrayList soSources = new ArrayList<>(); - - // - // Add SoSource objects for each of the system library directories. - // - - String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH"); - if (LD_LIBRARY_PATH == null) { - LD_LIBRARY_PATH = "/vendor/lib:/system/lib"; - } - - String[] systemLibraryDirectories = LD_LIBRARY_PATH.split(":"); - for (int i = 0; i < systemLibraryDirectories.length; ++i) { - // Don't pass DirectorySoSource.RESOLVE_DEPENDENCIES for directories we find on - // LD_LIBRARY_PATH: Bionic's dynamic linker is capable of correctly resolving dependencies - // these libraries have on each other, so doing that ourselves would be a waste. - File systemSoDirectory = new File(systemLibraryDirectories[i]); - soSources.add( - new DirectorySoSource( - systemSoDirectory, - DirectorySoSource.ON_LD_LIBRARY_PATH)); - } - - // - // We can only proceed forward if we have a Context. The prominent case - // where we don't have a Context is barebones dalvikvm instantiations. In - // that case, the caller is responsible for providing a correct LD_LIBRARY_PATH. - // - - if (context != null) { - // - // Prepend our own SoSource for our own DSOs. - // - - ApplicationInfo applicationInfo = context.getApplicationInfo(); - boolean isSystemApplication = - (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && - (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0; - - try { - if (isNativeExopackageEnabled) { - soSources.add(0, new ExoSoSource(context)); - } else if (isSystemApplication) { - soSources.add(0, new ApkSoSource(context)); - } else { - // Delete the old libs directory if we don't need it. - SysUtil.dumbDeleteRecrusive(SysUtil.getLibsDirectory(context)); - - int ourSoSourceFlags = 0; - - // On old versions of Android, Bionic doesn't add our library directory to its internal - // search path, and the system doesn't resolve dependencies between modules we ship. On - // these systems, we resolve dependencies ourselves. On other systems, Bionic's built-in - // resolver suffices. - - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) { - ourSoSourceFlags |= DirectorySoSource.RESOLVE_DEPENDENCIES; - } - - SoSource ourSoSource = new DirectorySoSource( - new File(applicationInfo.nativeLibraryDir), - ourSoSourceFlags); - - soSources.add(0, ourSoSource); - } - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - sSoSources = soSources.toArray(new SoSource[soSources.size()]); - } - } - - /** - * Turn shared-library loading into a no-op. Useful in special circumstances. - */ - public static void setInTestMode() { - sSoSources = new SoSource[]{new NoopSoSource()}; - } - - /** - * Load a shared library, initializing any JNI binding it contains. - * - * @param shortName Name of library to find, without "lib" prefix or ".so" suffix - */ - public static synchronized void loadLibrary(String shortName) - throws UnsatisfiedLinkError - { - if (sSoSources == null) { - // This should never happen during normal operation, - // but if we're running in a non-Android environment, - // fall back to System.loadLibrary. - if ("http://www.android.com/".equals(System.getProperty("java.vendor.url"))) { - // This will throw. - assertInitialized(); - } else { - // Not on an Android system. Ask the JVM to load for us. - System.loadLibrary(shortName); - return; - } - } - - try { - loadLibraryBySoName(System.mapLibraryName(shortName), 0); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - /** - * Unpack library and its dependencies, returning the location of the unpacked library file. All - * non-system dependencies of the given library will either be on LD_LIBRARY_PATH or will be in - * the same directory as the returned File. - * - * @param shortName Name of library to find, without "lib" prefix or ".so" suffix - * @return Unpacked DSO location - */ - public static File unpackLibraryAndDependencies(String shortName) - throws UnsatisfiedLinkError - { - assertInitialized(); - try { - return unpackLibraryBySoName(System.mapLibraryName(shortName)); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - /* package */ static void loadLibraryBySoName(String soName, int loadFlags) throws IOException { - int result = sLoadedLibraries.contains(soName) - ? SoSource.LOAD_RESULT_LOADED - : SoSource.LOAD_RESULT_NOT_FOUND; - - for (int i = 0; result == SoSource.LOAD_RESULT_NOT_FOUND && i < sSoSources.length; ++i) { - result = sSoSources[i].loadLibrary(soName, loadFlags); - } - - if (result == SoSource.LOAD_RESULT_NOT_FOUND) { - throw new UnsatisfiedLinkError("could find DSO to load: " + soName); - } - - if (result == SoSource.LOAD_RESULT_LOADED) { - sLoadedLibraries.add(soName); - } - } - - /* package */ static File unpackLibraryBySoName(String soName) throws IOException { - for (int i = 0; i < sSoSources.length; ++i) { - File unpacked = sSoSources[i].unpackLibrary(soName); - if (unpacked != null) { - return unpacked; - } - } - - throw new FileNotFoundException(soName); - } - - private static void assertInitialized() { - if (sSoSources == null) { - throw new RuntimeException("SoLoader.init() not yet called"); - } - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoSource.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoSource.java deleted file mode 100644 index 016013e15af73a..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SoSource.java +++ /dev/null @@ -1,57 +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. - */ - -package com.facebook.soloader; - -import java.io.File; -import java.io.IOException; - -abstract public class SoSource { - - /** - * This SoSource doesn't know how to provide the given library. - */ - public static final int LOAD_RESULT_NOT_FOUND = 0; - - /** - * This SoSource loaded the given library. - */ - public static final int LOAD_RESULT_LOADED = 1; - - /** - * This SoSource did not load the library, but verified that the system loader will load it if - * some other library depends on it. Returned only if LOAD_FLAG_ALLOW_IMPLICIT_PROVISION is - * provided to loadLibrary. - */ - public static final int LOAD_RESULT_IMPLICITLY_PROVIDED = 2; - - /** - * Allow loadLibrary to implicitly provide the library instead of actually loading it. - */ - public static final int LOAD_FLAG_ALLOW_IMPLICIT_PROVISION = 1; - - /** - * Load a shared library library into this process. This routine is independent of - * {@link #loadLibrary}. - * - * @param soName Name of library to load - * @param loadFlags Zero or more of the LOAD_FLAG_XXX constants. - * @return One of the LOAD_RESULT_XXX constants. - */ - abstract public int loadLibrary(String soName, int LoadFlags) throws IOException; - - /** - * Ensure that a shared library exists on disk somewhere. This routine is independent of - * {@link #loadLibrary}. - * - * @param soName Name of library to load - * @return File if library found; {@code null} if not. - */ - abstract public File unpackLibrary(String soName) throws IOException; -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SysUtil.java b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SysUtil.java deleted file mode 100644 index 91f28583e1469c..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/SysUtil.java +++ /dev/null @@ -1,205 +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. - */ - -package com.facebook.soloader; - -import java.io.File; -import java.io.IOException; -import android.content.Context; - -import java.util.jar.JarFile; -import java.util.jar.JarEntry; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import android.os.Build; -import android.system.Os; -import android.system.ErrnoException; - -import java.util.HashMap; -import java.util.Map; -import java.util.Enumeration; - -import java.io.InputStream; -import java.io.FileOutputStream; -import java.io.FileDescriptor; - -/*package*/ final class SysUtil { - - private static byte[] cachedBuffer = null; - - /** - * Copy from an inputstream to a named filesystem file. Take care to ensure that we can detect - * incomplete copies and that the copied bytes make it to stable storage before returning. - * The destination file will be marked executable. - * - * This routine caches an internal buffer between invocations; after making a sequence of calls - * {@link #reliablyCopyExecutable} calls, call {@link #freeCopyBuffer} to release this buffer. - * - * @param is Stream from which to copy - * @param destination File to which to write - * @param expectedSize Number of bytes we expect to write; -1 if unknown - * @param time Modification time to which to set file on success; must be in the past - */ - public static void reliablyCopyExecutable( - InputStream is, - File destination, - long expectedSize, - long time) throws IOException { - destination.delete(); - try (FileOutputStream os = new FileOutputStream(destination)) { - byte buffer[]; - if (cachedBuffer == null) { - cachedBuffer = buffer = new byte[16384]; - } else { - buffer = cachedBuffer; - } - - int nrBytes; - if (expectedSize > 0) { - fallocateIfSupported(os.getFD(), expectedSize); - } - - while ((nrBytes = is.read(buffer, 0, buffer.length)) >= 0) { - os.write(buffer, 0, nrBytes); - } - - os.getFD().sync(); - destination.setExecutable(true); - destination.setLastModified(time); - os.getFD().sync(); - } - } - - /** - * Free the internal buffer cache for {@link #reliablyCopyExecutable}. - */ - public static void freeCopyBuffer() { - cachedBuffer = null; - } - - /** - * Determine how preferred a given ABI is on this system. - * - * @param supportedAbis ABIs on this system - * @param abi ABI of a shared library we might want to unpack - * @return -1 if not supported or an integer, smaller being more preferred - */ - public static int findAbiScore(String[] supportedAbis, String abi) { - for (int i = 0; i < supportedAbis.length; ++i) { - if (supportedAbis[i] != null && abi.equals(supportedAbis[i])) { - return i; - } - } - - return -1; - } - - public static void deleteOrThrow(File file) throws IOException { - if (!file.delete()) { - throw new IOException("could not delete file " + file); - } - } - - /** - * Return an list of ABIs we supported on this device ordered according to preference. Use a - * separate inner class to isolate the version-dependent call where it won't cause the whole - * class to fail preverification. - * - * @return Ordered array of supported ABIs - */ - public static String[] getSupportedAbis() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - return new String[]{Build.CPU_ABI, Build.CPU_ABI2}; - } else { - return LollipopSysdeps.getSupportedAbis(); - } - } - - /** - * Pre-allocate disk space for a file if we can do that - * on this version of the OS. - * - * @param fd File descriptor for file - * @param length Number of bytes to allocate. - */ - public static void fallocateIfSupported(FileDescriptor fd, long length) throws IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - LollipopSysdeps.fallocate(fd, length); - } - } - - public static FileLocker lockLibsDirectory(Context context) throws IOException { - File lockFile = new File(context.getApplicationInfo().dataDir, "libs-dir-lock"); - return FileLocker.lock(lockFile); - } - - /** - * Return the directory into which we put our self-extracted native libraries. - * - * @param context Application context - * @return File pointing to an existing directory - */ - /* package */ static File getLibsDirectory(Context context) { - return new File(context.getApplicationInfo().dataDir, "app_libs"); - } - - /** - * Return the directory into which we put our self-extracted native libraries and make sure it - * exists. - */ - /* package */ static File createLibsDirectory(Context context) { - File libsDirectory = getLibsDirectory(context); - if (!libsDirectory.isDirectory() && !libsDirectory.mkdirs()) { - throw new RuntimeException("could not create libs directory"); - } - - return libsDirectory; - } - - /** - * Delete a directory and its contents. - * - * WARNING: Java APIs do not let us distinguish directories from symbolic links to directories. - * Consequently, if the directory contains symbolic links to directories, we will attempt to - * delete the contents of pointed-to directories. - * - * @param file File or directory to delete - */ - /* package */ static void dumbDeleteRecrusive(File file) throws IOException { - if (file.isDirectory()) { - for (File entry : file.listFiles()) { - dumbDeleteRecrusive(entry); - } - } - - if (!file.delete() && file.exists()) { - throw new IOException("could not delete: " + file); - } - } - - /** - * Encapsulate Lollipop-specific calls into an independent class so we don't fail preverification - * downlevel. - */ - private static final class LollipopSysdeps { - public static String[] getSupportedAbis() { - return Build.SUPPORTED_32_BIT_ABIS; // We ain't doing no newfangled 64-bit - } - - public static void fallocate(FileDescriptor fd, long length) throws IOException { - try { - Os.posix_fallocate(fd, 0, length); - } catch (ErrnoException ex) { - throw new IOException(ex.toString(), ex); - } - } - } -} diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/genstructs.sh b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/genstructs.sh deleted file mode 100644 index a7bcd49a581a82..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/genstructs.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# -# This script generates Java structures that contain the offsets of -# fields in various ELF ABI structures. com.facebook.soloader.MinElf -# uses these structures while parsing ELF files. -# - -set -euo pipefail - -struct2java() { - ../../../../scripts/struct2java.py "$@" -} - -declare -a structs=(Elf32_Ehdr Elf64_Ehdr) -structs+=(Elf32_Ehdr Elf64_Ehdr) -structs+=(Elf32_Phdr Elf64_Phdr) -structs+=(Elf32_Shdr Elf64_Shdr) -structs+=(Elf32_Dyn Elf64_Dyn) - -for struct in "${structs[@]}"; do - cat > elfhdr.c < -static const $struct a; -EOF - gcc -g -c -o elfhdr.o elfhdr.c - cat > $struct.java <> $struct.java -done - -rm -f elfhdr.o elfhdr.c diff --git a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/soloader.pro b/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/soloader.pro deleted file mode 100644 index 4a832314c5492e..00000000000000 --- a/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/soloader.pro +++ /dev/null @@ -1,6 +0,0 @@ -# Ensure that methods from LollipopSysdeps don't get inlined. LollipopSysdeps.fallocate references -# an exception that isn't present prior to Lollipop, which trips up the verifier if the class is -# loaded on a pre-Lollipop OS. --keep class com.facebook.soloader.SysUtil$LollipopSysdeps { - public ; -} From 70b989eb67ce7d1e1a8ba3022388093b0a956aeb Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Fri, 8 Jul 2016 14:47:31 -0700 Subject: [PATCH 052/241] Revert land of css-layout PR #199 Reviewed By: bestander Differential Revision: D3536627 fbshipit-source-id: e89b2a5fd38d1228bd8526c46bb26c594947837a --- React/Layout/Layout.c | 89 +++++++++---------- React/Layout/README | 2 +- .../com/facebook/csslayout/LayoutEngine.java | 89 +++++++++---------- .../main/java/com/facebook/csslayout/README | 2 +- 4 files changed, 86 insertions(+), 96 deletions(-) diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index d9bc697fc9b4d2..22412a8d5c2701 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -7,7 +7,7 @@ */ // NOTE: this file is auto-copied from https://github.com/facebook/css-layout -// @generated SignedSource<<8af322a110307b3277db5b00573da822>> +// @generated SignedSource<<484d0d17453521463896ce24d946412b>> #include @@ -829,61 +829,56 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab child->layout.flex_basis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). childWidth = CSS_UNDEFINED; childHeight = CSS_UNDEFINED; childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; - // Main axis - if (isMainAxisRow) { - if (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED || isUndefined(availableInnerMainDim)) { - childWidth = CSS_UNDEFINED; - childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; - } else { - childWidth = availableInnerMainDim; - childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; - } - } else if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { - if (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED || isUndefined(availableInnerMainDim)) { - childHeight = CSS_UNDEFINED; - childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; - } else { - childHeight = availableInnerMainDim; + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + childWidth = child->style.dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = child->style.dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; } } - // Cross axis - if (isMainAxisRow) { - if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { - if (!isUndefined(availableInnerCrossDim) && - !isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN) && - heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && - getAlignItem(node, child) == CSS_ALIGN_STRETCH) { - childHeight = availableInnerCrossDim; - childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } else if (!isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { - childHeight = availableInnerCrossDim; - childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; - } else { - childHeight = child->style.dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); - childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } - } - } else { - if (!isUndefined(availableInnerCrossDim) && - !isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW) && - widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && - getAlignItem(node, child) == CSS_ALIGN_STRETCH) { - childWidth = availableInnerCrossDim; - childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } else if (!isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { - childWidth = availableInnerCrossDim; - childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; - } else { - childWidth = child->style.dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); - childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; - } + // If child has no defined size in the cross axis and is set to stretch, set the cross + // axis to be measured exactly with the available inner width + if (!isMainAxisRow && + !isUndefined(availableInnerWidth) && + !isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW) && + widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && + getAlignItem(node, child) == CSS_ALIGN_STRETCH) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + if (isMainAxisRow && + !isUndefined(availableInnerHeight) && + !isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN) && + heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && + getAlignItem(node, child) == CSS_ALIGN_STRETCH) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; } // Measure the child diff --git a/React/Layout/README b/React/Layout/README index 5a2ca3b1e73de9..9d85b26ff7a12f 100644 --- a/React/Layout/README +++ b/React/Layout/README @@ -1,7 +1,7 @@ The source of truth for css-layout is: https://github.com/facebook/css-layout The code here should be kept in sync with GitHub. -HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/ca34ff44460bce122d02b27c0409a825f8de90b1 +HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/a1f36b53f5464c8ee7abc311765dc3ecb1b879c6 There is generated code in: - README (this file) diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java b/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java index 735eec68703ee6..582fc44aa43316 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/LayoutEngine.java @@ -7,7 +7,7 @@ */ // NOTE: this file is auto-copied from https://github.com/facebook/css-layout -// @generated SignedSource<<9805b148b88d298edb7fcd7c13738ede>> +// @generated SignedSource<> package com.facebook.csslayout; @@ -726,61 +726,56 @@ private static void layoutNodeImpl( child.layout.flexBasis = Math.max(0, ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))); } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). childWidth = CSSConstants.UNDEFINED; childHeight = CSSConstants.UNDEFINED; childWidthMeasureMode = CSSMeasureMode.UNDEFINED; childHeightMeasureMode = CSSMeasureMode.UNDEFINED; - // Main axis - if (isMainAxisRow) { - if (widthMeasureMode == CSSMeasureMode.UNDEFINED || Float.isNaN(availableInnerMainDim)) { - childWidth = CSSConstants.UNDEFINED; - childWidthMeasureMode = CSSMeasureMode.UNDEFINED; - } else { - childWidth = availableInnerMainDim; - childWidthMeasureMode = CSSMeasureMode.AT_MOST; - } - } else if (node.style.overflow == CSSOverflow.HIDDEN) { - if (heightMeasureMode == CSSMeasureMode.UNDEFINED || Float.isNaN(availableInnerMainDim)) { - childHeight = CSSConstants.UNDEFINED; - childHeightMeasureMode = CSSMeasureMode.UNDEFINED; - } else { - childHeight = availableInnerMainDim; + if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + } + if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && Float.isNaN(childWidth) && !Float.isNaN(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSSMeasureMode.AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node.style.overflow == CSSOverflow.HIDDEN) { + if (isMainAxisRow && Float.isNaN(childHeight) && !Float.isNaN(availableInnerHeight)) { + childHeight = availableInnerHeight; childHeightMeasureMode = CSSMeasureMode.AT_MOST; } } - // Cross axis - if (isMainAxisRow) { - if (node.style.overflow == CSSOverflow.HIDDEN) { - if (!Float.isNaN(availableInnerCrossDim) && - !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) && - heightMeasureMode == CSSMeasureMode.EXACTLY && - getAlignItem(node, child) == CSSAlign.STRETCH) { - childHeight = availableInnerCrossDim; - childHeightMeasureMode = CSSMeasureMode.EXACTLY; - } else if (!(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - childHeight = availableInnerCrossDim; - childHeightMeasureMode = Float.isNaN(childHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.AT_MOST; - } else { - childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); - childHeightMeasureMode = CSSMeasureMode.EXACTLY; - } - } - } else { - if (!Float.isNaN(availableInnerCrossDim) && - !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0) && - widthMeasureMode == CSSMeasureMode.EXACTLY && - getAlignItem(node, child) == CSSAlign.STRETCH) { - childWidth = availableInnerCrossDim; - childWidthMeasureMode = CSSMeasureMode.EXACTLY; - } else if (!(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { - childWidth = availableInnerCrossDim; - childWidthMeasureMode = Float.isNaN(childWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.AT_MOST; - } else { - childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); - childWidthMeasureMode = CSSMeasureMode.EXACTLY; - } + // If child has no defined size in the cross axis and is set to stretch, set the cross + // axis to be measured exactly with the available inner width + if (!isMainAxisRow && + !Float.isNaN(availableInnerWidth) && + !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0) && + widthMeasureMode == CSSMeasureMode.EXACTLY && + getAlignItem(node, child) == CSSAlign.STRETCH) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + } + if (isMainAxisRow && + !Float.isNaN(availableInnerHeight) && + !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) && + heightMeasureMode == CSSMeasureMode.EXACTLY && + getAlignItem(node, child) == CSSAlign.STRETCH) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSSMeasureMode.EXACTLY; } // Measure the child diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/README b/ReactAndroid/src/main/java/com/facebook/csslayout/README index 5a2ca3b1e73de9..9d85b26ff7a12f 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/README +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/README @@ -1,7 +1,7 @@ The source of truth for css-layout is: https://github.com/facebook/css-layout The code here should be kept in sync with GitHub. -HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/ca34ff44460bce122d02b27c0409a825f8de90b1 +HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/a1f36b53f5464c8ee7abc311765dc3ecb1b879c6 There is generated code in: - README (this file) From d73446c549a4f4bbace8b7f4dedf6116553f345a Mon Sep 17 00:00:00 2001 From: Simon Dohmen Date: Fri, 8 Jul 2016 18:57:50 -0700 Subject: [PATCH 053/241] Fixed position of ripple-effect Summary: I recognised that the ripple effect was always located at the bottom of my buttons. So i started to search for other ppl having the same problem. I also found this bug on the f8 app. The gifs show the behaviour before and after my changes: [The ripple effect is always on the bottom of the element, because pageY is used instead of the relative pos] ![before](https://cloud.githubusercontent.com/assets/956410/16042805/df49d97e-323c-11e6-8ce2-b0658ec85b27.gif) ![after](https://cloud.githubusercontent.com/assets/956410/16042804/df483a60-323c-11e6-862f-e8c97b835339.gif) Before, the absolute position of the touch was used to update the position during movement but it should be the relative position. Just some small calculation changes were needed to achieve the correct behaviour :) Closes https://github.com/facebook/react-native/pull/8111 Differential Revision: D3539652 fbshipit-source-id: dd8c8f3c7402d0d435f1c2ca67c1a59b474efda3 --- .../Components/Touchable/TouchableNativeFeedback.android.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 8d8a12af7594c7..180bccd6f79290 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -185,7 +185,7 @@ var TouchableNativeFeedback = React.createClass({ _handleResponderMove: function(e) { this.touchableHandleResponderMove(e); - this._dispatchHotspotUpdate(e.nativeEvent.pageX, e.nativeEvent.pageY); + this._dispatchHotspotUpdate(e.nativeEvent.locationX, e.nativeEvent.locationY); }, _dispatchHotspotUpdate: function(destX, destY) { From 65ff8192dd6a542485d1ea0647eadee44d76a55b Mon Sep 17 00:00:00 2001 From: Nicolas Charpentier Date: Fri, 8 Jul 2016 19:11:00 -0700 Subject: [PATCH 054/241] Remove instructions about IP address on iOS Summary: The documentation was not updated after this change : Implemented automatic IP detection for iOS (8c29a52) Fixes #8651. Closes https://github.com/facebook/react-native/pull/8660 Differential Revision: D3539749 fbshipit-source-id: fe3b37c446a8c37941adbb08c4301284950a176a --- docs/RunningOnDeviceIOS.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/RunningOnDeviceIOS.md b/docs/RunningOnDeviceIOS.md index 5a4af00eda5b5d..81d9e73e035822 100644 --- a/docs/RunningOnDeviceIOS.md +++ b/docs/RunningOnDeviceIOS.md @@ -13,9 +13,7 @@ Running an iOS app on a device requires an [Apple Developer account](https://dev You can iterate quickly on device using the development server. First, ensure that you are on the same Wi-Fi network as your computer. -1. Open `ios/YourApp/AppDelegate.m` -2. Change the host in the URL from `localhost` to your laptop's IP address. On Mac, you can find the IP address in System Preferences / Network. -3. In Xcode, select your phone as build target and press "Build and run" +In Xcode, select your phone as build target and press "Build and run" > Hint > From c0316c695d679ba2d8157904014e1d3849a811fa Mon Sep 17 00:00:00 2001 From: Ritesh Kadmawala Date: Sun, 10 Jul 2016 05:31:26 -0700 Subject: [PATCH 055/241] Fixed the issue due to which js assets are not bundled in the apk when separate build for different CPU architectures is enabled Summary: This PR tries to fix a minor bug in `react.gradle` due to which task that bundles JS into the assets folder of the APK is not run when separate build per CPU architecture is enabled and we are using different product flavors. Closes https://github.com/facebook/react-native/pull/8675 Differential Revision: D3541348 fbshipit-source-id: 4c84f21a06a45046f84bdd8ae5c5d834ec080476 --- react.gradle | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/react.gradle b/react.gradle index ffa9e48c6d37ed..d34469ddce3f4b 100644 --- a/react.gradle +++ b/react.gradle @@ -31,7 +31,9 @@ gradle.projectsEvaluated { productFlavors.each { productFlavorName -> buildTypes.each { buildTypeName -> // Create variant and target names - def targetName = "${productFlavorName.capitalize()}${buildTypeName.capitalize()}" + def flavorNameCapitalized = "${productFlavorName.capitalize()}" + def buildNameCapitalized = "${buildTypeName.capitalize()}" + def targetName = "${flavorNameCapitalized}${buildNameCapitalized}" def targetPath = productFlavorName ? "${productFlavorName}/${buildTypeName}" : "${buildTypeName}" @@ -92,8 +94,8 @@ gradle.projectsEvaluated { currentBundleTask.dependsOn("merge${targetName}Resources") currentBundleTask.dependsOn("merge${targetName}Assets") - runBefore("processArmeabi-v7a${targetName}Resources", currentBundleTask) - runBefore("processX86${targetName}Resources", currentBundleTask) + runBefore("process${flavorNameCapitalized}Armeabi-v7a${buildNameCapitalized}Resources", currentBundleTask) + runBefore("process${flavorNameCapitalized}X86${buildNameCapitalized}Resources", currentBundleTask) runBefore("processUniversal${targetName}Resources", currentBundleTask) runBefore("process${targetName}Resources", currentBundleTask) } From 4aedcc767095bd5fd33a643e68311920ea18a5f5 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Sun, 10 Jul 2016 12:23:42 -0700 Subject: [PATCH 056/241] Fix double completion callback in RCTJavascriptLoader Reviewed By: foghina, mmmulani Differential Revision: D3541511 fbshipit-source-id: 9a0a4be635ca910cb1a5c875a0f4a2b82c51cf71 --- React/Base/RCTJavaScriptLoader.m | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index fbc5cc1c9a008a..a44dc5baf2cd3f 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -60,7 +60,6 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp } magicNumber = NSSwapLittleIntToHost(magicNumber); - if (magicNumber == RCTRAMBundleMagicNumber) { NSData *source = [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; NSError *error = nil; @@ -73,16 +72,16 @@ + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComp sourceLength = statInfo.st_size; } onComplete(error, source, sourceLength); + } else { + // Reading in a large bundle can be slow. Dispatch to the background queue to do it. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSError *error = nil; + NSData *source = [NSData dataWithContentsOfFile:scriptURL.path + options:NSDataReadingMappedIfSafe + error:&error]; + onComplete(error, source, source.length); + }); } - - // Reading in a large bundle can be slow. Dispatch to the background queue to do it. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error = nil; - NSData *source = [NSData dataWithContentsOfFile:scriptURL.path - options:NSDataReadingMappedIfSafe - error:&error]; - onComplete(error, source, source.length); - }); return; } From 0b773c41ac263f84149d54da183c577b0dc4677a Mon Sep 17 00:00:00 2001 From: Nivetha Singara Vadivelu Date: Sun, 10 Jul 2016 20:28:20 -0700 Subject: [PATCH 057/241] Navigator - making changes so that a simple flick can be detected Summary: Decreased the distance threshold so that the user doesn't need to swipe a large distance to set the pan responder. Also, set a threshold for the velocity so that a simple flick can be detected. Reviewed By: hedgerwang Differential Revision: D3540354 fbshipit-source-id: 251a4f14dc014bc32b3a83fa8de419f86ca40b84 --- .../NavigationPagerPanResponder.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js index d4da7a52275004..e7c08509828ce2 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js @@ -37,12 +37,22 @@ type Props = NavigationSceneRendererProps & { */ const { ANIMATION_DURATION, - DISTANCE_THRESHOLD, POSITION_THRESHOLD, RESPOND_THRESHOLD, Directions, } = NavigationCardStackPanResponder; +/** + * The threshold (in pixels) to finish the gesture action. + */ +const DISTANCE_THRESHOLD = 50; + +/** + * The threshold to trigger the gesture action. This determines the rate of the + * flick when the action will be triggered + */ +const VELOCITY_THRESHOLD = 1.5; + /** * Pan responder that handles gesture for a card in the cards list. * @@ -159,6 +169,7 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { const isVertical = this._isVertical; const axis = isVertical ? 'dy' : 'dx'; + const velocityAxis = isVertical ? 'vy' : 'vx'; const index = navigationState.index; const distance = gesture[axis]; @@ -166,7 +177,8 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { this._reset(); if ( distance > DISTANCE_THRESHOLD || - value <= index - POSITION_THRESHOLD + value <= index - POSITION_THRESHOLD || + gesture[velocityAxis] > VELOCITY_THRESHOLD ) { onNavigateBack && onNavigateBack(); return; @@ -174,7 +186,8 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { if ( distance < -DISTANCE_THRESHOLD || - value >= index + POSITION_THRESHOLD + value >= index + POSITION_THRESHOLD || + gesture[velocityAxis] < -VELOCITY_THRESHOLD ) { onNavigateForward && onNavigateForward(); } From defc34a28b1346938dc4540c0f1c194c29db8cc5 Mon Sep 17 00:00:00 2001 From: Nathan Azaria Date: Mon, 11 Jul 2016 02:47:47 -0700 Subject: [PATCH 058/241] Removed calls to RCTBundleURLProvider setDefaults Reviewed By: javache Differential Revision: D3534799 fbshipit-source-id: 0c784cd9a993c6379e49087906ebf670ddd4f8cd --- .../UIExplorer/UIExplorerUnitTests/RCTBundleURLProviderTests.m | 1 - React/Base/RCTBundleURLProvider.m | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTBundleURLProviderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTBundleURLProviderTests.m index bb044887c5e3f1..3f0956b5367f7f 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTBundleURLProviderTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTBundleURLProviderTests.m @@ -60,7 +60,6 @@ - (void)setUp RCTSwapInstanceMethods([NSBundle class], @selector(URLForResource:withExtension:), @selector(RCT_URLForResource:withExtension:)); - [[RCTBundleURLProvider sharedSettings] setDefaults]; } - (void)tearDown diff --git a/React/Base/RCTBundleURLProvider.m b/React/Base/RCTBundleURLProvider.m index 105f6a1497d1d1..51d0e941aab420 100644 --- a/React/Base/RCTBundleURLProvider.m +++ b/React/Base/RCTBundleURLProvider.m @@ -58,7 +58,6 @@ - (void)settingsUpdated - (void)setDefaults { [[NSUserDefaults standardUserDefaults] registerDefaults:[self defaults]]; - [self settingsUpdated]; } - (void)resetToDefaults @@ -67,6 +66,7 @@ - (void)resetToDefaults [[NSUserDefaults standardUserDefaults] removeObjectForKey:key]; } [self setDefaults]; + [self settingsUpdated]; } - (BOOL)isPackagerRunning:(NSString *)host From c8b58801020d85ed85d02525d6e9012c6c8ca8e9 Mon Sep 17 00:00:00 2001 From: Nathan Azaria Date: Mon, 11 Jul 2016 03:34:33 -0700 Subject: [PATCH 059/241] Renamed updateObject:forKey to updateValue:forKey Reviewed By: javache Differential Revision: D3534820 fbshipit-source-id: 32afa39aedd43319fb5933ee0b169a41f4c8cd19 --- React/Base/RCTBundleURLProvider.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/React/Base/RCTBundleURLProvider.m b/React/Base/RCTBundleURLProvider.m index 51d0e941aab420..ba8a0a0081c9bd 100644 --- a/React/Base/RCTBundleURLProvider.m +++ b/React/Base/RCTBundleURLProvider.m @@ -134,7 +134,7 @@ - (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackResource:(NSS } } -- (void)updateDefaults:(id)object forKey:(NSString *)key +- (void)updateValue:(id)object forKey:(NSString *)key { [[NSUserDefaults standardUserDefaults] setObject:object forKey:key]; [[NSUserDefaults standardUserDefaults] synchronize]; @@ -163,22 +163,22 @@ - (NSString *)jsLocation - (void)setEnableDev:(BOOL)enableDev { - [self updateDefaults:@(enableDev) forKey:kRCTEnableDevKey]; + [self updateValue:@(enableDev) forKey:kRCTEnableDevKey]; } - (void)setEnableLiveReload:(BOOL)enableLiveReload { - [self updateDefaults:@(enableLiveReload) forKey:kRCTEnableLiveReloadKey]; + [self updateValue:@(enableLiveReload) forKey:kRCTEnableLiveReloadKey]; } - (void)setJsLocation:(NSString *)jsLocation { - [self updateDefaults:jsLocation forKey:kRCTJsLocationKey]; + [self updateValue:jsLocation forKey:kRCTJsLocationKey]; } - (void)setEnableMinification:(BOOL)enableMinification { - [self updateDefaults:@(enableMinification) forKey:kRCTEnableMinificationKey]; + [self updateValue:@(enableMinification) forKey:kRCTEnableMinificationKey]; } + (instancetype)sharedSettings From a2c6a7bc64c991b934ad8c662bb251f03ca2fee6 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 11 Jul 2016 04:55:59 -0700 Subject: [PATCH 060/241] export SwipeableListView component. Summary: Explain the **motivation** for making this change. What existing problem does the pull request solve? 1. [`SwipeableListView`](https://github.com/facebook/react-native/blob/master/Libraries/Experimental/SwipeableRow/SwipeableListView.js) is already usable, I think it would be great to export it so users can test it. Closes https://github.com/facebook/react-native/pull/8482 Differential Revision: D3542403 Pulled By: fred2028 fbshipit-source-id: 7b4e0aa8c50110e434c9d64b72b15d1206fa0470 --- Libraries/react-native/react-native.js | 1 + Libraries/react-native/react-native.js.flow | 1 + 2 files changed, 2 insertions(+) diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index 361234ace4b371..4ce701b3c89014 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -54,6 +54,7 @@ const ReactNative = { get RecyclerViewBackedScrollView() { return require('RecyclerViewBackedScrollView'); }, get RefreshControl() { return require('RefreshControl'); }, get StatusBar() { return require('StatusBar'); }, + get SwipeableListView() { return require('SwipeableListView'); }, get SwitchAndroid() { return require('SwitchAndroid'); }, get SwitchIOS() { return require('SwitchIOS'); }, get TabBarIOS() { return require('TabBarIOS'); }, diff --git a/Libraries/react-native/react-native.js.flow b/Libraries/react-native/react-native.js.flow index 05eff164c749de..930ec4e04c6d96 100644 --- a/Libraries/react-native/react-native.js.flow +++ b/Libraries/react-native/react-native.js.flow @@ -63,6 +63,7 @@ var ReactNative = { Slider: require('Slider'), SnapshotViewIOS: require('SnapshotViewIOS'), StatusBar: require('StatusBar'), + SwipeableListView: require('SwipeableListView'), Switch: require('Switch'), RecyclerViewBackedScrollView: require('RecyclerViewBackedScrollView'), RefreshControl: require('RefreshControl'), From 13a19a8897b4d96b9dfbee5640451d6913a4f44c Mon Sep 17 00:00:00 2001 From: Chirag Date: Mon, 11 Jul 2016 04:56:20 -0700 Subject: [PATCH 061/241] Don't block scroll when using non-touchable visible row for SwipeableRow Summary: **motivation** Currently when using a non-touchable visible row for a SwipeableRow, the scroll on list view is blocked. **Test Plan** ``` } renderQuickActions={() => } /> ``` * Tested that when using non-touchable for renderRow allows scrolling on list view. * Tested that when using touchable for renderRow allows scrolling on list view. cc fred2028 Closes https://github.com/facebook/react-native/pull/8537 Differential Revision: D3542407 Pulled By: fred2028 fbshipit-source-id: b7ad7ff7c7dd1b717544746a85bb265414aa18f4 --- Libraries/Experimental/SwipeableRow/SwipeableRow.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRow.js b/Libraries/Experimental/SwipeableRow/SwipeableRow.js index 23a608902371a1..80a2ffb5fd39bf 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableRow.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableRow.js @@ -128,10 +128,6 @@ const SwipeableRow = React.createClass({ componentWillMount(): void { this._panResponder = PanResponder.create({ - onStartShouldSetPanResponder: (event, gestureState) => true, - // Don't capture child's start events - onStartShouldSetPanResponderCapture: (event, gestureState) => false, - onMoveShouldSetPanResponder: (event, gestureState) => false, onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture, onPanResponderGrant: this._handlePanResponderGrant, onPanResponderMove: this._handlePanResponderMove, From e5650560c08d4ac1ed97d233aa170da3e0751188 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 11 Jul 2016 06:05:51 -0700 Subject: [PATCH 062/241] Class for JS stack frames instead of dictionaries Summary: Currently React Native codebase treats JS stack traces as array of dictionaries. This diff switches the Red Box to use new `RCTJSStackFrame` for internal data representation, while keeping the exposed API unchanged. The next step would be to replace the rest of manual parsing and usage of dictionaries. The new class has ability to parse the stack from raw strings or dictionaries. Depends on D3429031 Reviewed By: javache Differential Revision: D3473199 fbshipit-source-id: 90d2a4f5e8e054b75c99905f35c2ee54927bb311 --- React/Base/RCTJSStackFrame.h | 27 +++++++ React/Base/RCTJSStackFrame.m | 101 ++++++++++++++++++++++++++ React/Modules/RCTRedBox.m | 48 ++++++------ React/React.xcodeproj/project.pbxproj | 6 ++ 4 files changed, 160 insertions(+), 22 deletions(-) create mode 100644 React/Base/RCTJSStackFrame.h create mode 100644 React/Base/RCTJSStackFrame.m diff --git a/React/Base/RCTJSStackFrame.h b/React/Base/RCTJSStackFrame.h new file mode 100644 index 00000000000000..34c0fbe93b248e --- /dev/null +++ b/React/Base/RCTJSStackFrame.h @@ -0,0 +1,27 @@ +/** + * 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 RCTJSStackFrame : NSObject + +@property (nonatomic, copy, readonly) NSString *methodName; +@property (nonatomic, copy, readonly) NSString *file; +@property (nonatomic, readonly) NSInteger lineNumber; +@property (nonatomic, readonly) NSInteger column; + +- (instancetype)initWithMethodName:(NSString *)methodName file:(NSString *)file lineNumber:(NSInteger)lineNumber column:(NSInteger)column; +- (NSDictionary *)toDictionary; + ++ (instancetype)stackFrameWithLine:(NSString *)line; ++ (instancetype)stackFrameWithDictionary:(NSDictionary *)dict; ++ (NSArray *)stackFramesWithLines:(NSString *)lines; ++ (NSArray *)stackFramesWithDictionaries:(NSArray *)dicts; + +@end diff --git a/React/Base/RCTJSStackFrame.m b/React/Base/RCTJSStackFrame.m new file mode 100644 index 00000000000000..fd2a615fe5cdb0 --- /dev/null +++ b/React/Base/RCTJSStackFrame.m @@ -0,0 +1,101 @@ +/** + * 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 "RCTJSStackFrame.h" +#import "RCTLog.h" + + +static NSRegularExpression *RCTJSStackFrameRegex() +{ + static dispatch_once_t onceToken; + static NSRegularExpression *_regex; + dispatch_once(&onceToken, ^{ + NSError *regexError; + _regex = [NSRegularExpression regularExpressionWithPattern:@"^([^@]+)@(.*):(\\d+):(\\d+)$" options:0 error:®exError]; + if (regexError) { + RCTLogError(@"Failed to build regex: %@", [regexError localizedDescription]); + } + }); + return _regex; +} + +@implementation RCTJSStackFrame + +- (instancetype)initWithMethodName:(NSString *)methodName file:(NSString *)file lineNumber:(NSInteger)lineNumber column:(NSInteger)column +{ + if (self = [super init]) { + _methodName = methodName; + _file = file; + _lineNumber = lineNumber; + _column = column; + } + return self; +} + +- (NSDictionary *)toDictionary +{ + return @{ + @"methodName": self.methodName, + @"file": self.file, + @"lineNumber": @(self.lineNumber), + @"column": @(self.column) + }; +} + ++ (instancetype)stackFrameWithLine:(NSString *)line +{ + NSTextCheckingResult *match = [RCTJSStackFrameRegex() firstMatchInString:line options:0 range:NSMakeRange(0, line.length)]; + if (!match) { + return nil; + } + + NSString *methodName = [line substringWithRange:[match rangeAtIndex:1]]; + NSString *file = [line substringWithRange:[match rangeAtIndex:2]]; + NSString *lineNumber = [line substringWithRange:[match rangeAtIndex:3]]; + NSString *column = [line substringWithRange:[match rangeAtIndex:4]]; + + return [[self alloc] initWithMethodName:methodName + file:file + lineNumber:[lineNumber integerValue] + column:[column integerValue]]; +} + ++ (instancetype)stackFrameWithDictionary:(NSDictionary *)dict +{ + return [[self alloc] initWithMethodName:dict[@"methodName"] + file:dict[@"file"] + lineNumber:[dict[@"lineNumber"] integerValue] + column:[dict[@"column"] integerValue]]; +} + ++ (NSArray *)stackFramesWithLines:(NSString *)lines +{ + NSMutableArray *stack = [NSMutableArray new]; + for (NSString *line in [lines componentsSeparatedByString:@"\n"]) { + RCTJSStackFrame *frame = [self stackFrameWithLine:line]; + if (frame) { + [stack addObject:frame]; + } + } + return stack; +} + ++ (NSArray *)stackFramesWithDictionaries:(NSArray *)dicts +{ + NSMutableArray *stack = [NSMutableArray new]; + for (NSDictionary *dict in dicts) { + RCTJSStackFrame *frame = [self stackFrameWithDictionary:dict]; + if (frame) { + [stack addObject:frame]; + } + } + return stack; +} + +@end diff --git a/React/Modules/RCTRedBox.m b/React/Modules/RCTRedBox.m index 92c3c541fc6aa1..125b19f7381f94 100644 --- a/React/Modules/RCTRedBox.m +++ b/React/Modules/RCTRedBox.m @@ -13,6 +13,7 @@ #import "RCTConvert.h" #import "RCTDefines.h" #import "RCTUtils.h" +#import "RCTJSStackFrame.h" #if RCT_DEBUG @@ -20,7 +21,7 @@ @protocol RCTRedBoxWindowActionDelegate -- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(NSDictionary *)stackFrame; +- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame; - (void)reloadFromRedBoxWindow:(RCTRedBoxWindow *)redBoxWindow; @end @@ -33,7 +34,7 @@ @implementation RCTRedBoxWindow { UITableView *_stackTraceTableView; NSString *_lastErrorMessage; - NSArray *_lastStackTrace; + NSArray *_lastStackTrace; } - (instancetype)initWithFrame:(CGRect)frame @@ -110,7 +111,7 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate +- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate { // Show if this is a new message, or if we're updating the previous message if ((self.hidden && !isUpdate) || (!self.hidden && isUpdate && [_lastErrorMessage isEqualToString:message])) { @@ -156,9 +157,9 @@ - (void)copyStack fullStackTrace = [NSMutableString string]; } - for (NSDictionary *stackFrame in _lastStackTrace) { - [fullStackTrace appendString:[NSString stringWithFormat:@"%@\n", stackFrame[@"methodName"]]]; - if (stackFrame[@"file"]) { + for (RCTJSStackFrame *stackFrame in _lastStackTrace) { + [fullStackTrace appendString:[NSString stringWithFormat:@"%@\n", stackFrame.methodName]]; + if (stackFrame.file) { [fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]]; } } @@ -167,15 +168,14 @@ - (void)copyStack [pb setString:fullStackTrace]; } -- (NSString *)formatFrameSource:(NSDictionary *)stackFrame +- (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame { NSString *lineInfo = [NSString stringWithFormat:@"%@:%zd", - [stackFrame[@"file"] lastPathComponent], - [stackFrame[@"lineNumber"] integerValue]]; + [stackFrame.file lastPathComponent], + stackFrame.lineNumber]; - NSInteger column = [stackFrame[@"column"] integerValue]; - if (column != 0) { - lineInfo = [lineInfo stringByAppendingFormat:@":%zd", column]; + if (stackFrame.column != 0) { + lineInfo = [lineInfo stringByAppendingFormat:@":%zd", stackFrame.column]; } return lineInfo; } @@ -200,7 +200,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; NSUInteger index = indexPath.row; - NSDictionary *stackFrame = _lastStackTrace[index]; + RCTJSStackFrame *stackFrame = _lastStackTrace[index]; return [self reuseCell:cell forStackFrame:stackFrame]; } @@ -223,7 +223,7 @@ - (UITableViewCell *)reuseCell:(UITableViewCell *)cell forErrorMessage:(NSString return cell; } -- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forStackFrame:(NSDictionary *)stackFrame +- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forStackFrame:(RCTJSStackFrame *)stackFrame { if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"]; @@ -238,8 +238,8 @@ - (UITableViewCell *)reuseCell:(UITableViewCell *)cell forStackFrame:(NSDictiona cell.selectedBackgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2]; } - cell.textLabel.text = stackFrame[@"methodName"]; - if (stackFrame[@"file"]) { + cell.textLabel.text = stackFrame.methodName; + if (stackFrame.file) { cell.detailTextLabel.text = [self formatFrameSource:stackFrame]; } else { cell.detailTextLabel.text = @""; @@ -266,7 +266,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath { if (indexPath.section == 1) { NSUInteger row = indexPath.row; - NSDictionary *stackFrame = _lastStackTrace[row]; + RCTJSStackFrame *stackFrame = _lastStackTrace[row]; [_actionDelegate redBoxWindow:self openStackFrameInEditor:stackFrame]; } [tableView deselectRowAtIndexPath:indexPath animated:YES]; @@ -341,9 +341,8 @@ - (void)showErrorMessage:(NSString *)message withDetails:(NSString *)details - (void)showErrorMessage:(NSString *)message withRawStack:(NSString *)rawStack { - // TODO #11638796: convert rawStack into something useful - message = [NSString stringWithFormat:@"%@\n\n%@", message, rawStack]; - [self showErrorMessage:message withStack:nil isUpdate:NO]; + NSArray *stack = [RCTJSStackFrame stackFramesWithLines:rawStack]; + [self _showErrorMessage:message withStack:stack isUpdate:NO]; } - (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack @@ -357,6 +356,11 @@ - (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate +{ + [self _showErrorMessage:message withStack:[RCTJSStackFrame stackFramesWithDictionaries:stack] isUpdate:isUpdate]; +} + +- (void)_showErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate { dispatch_async(dispatch_get_main_queue(), ^{ if (!self->_window) { @@ -379,14 +383,14 @@ - (void)invalidate [self dismiss]; } -- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(NSDictionary *)stackFrame +- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame { if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) { RCTLogWarn(@"Cannot open stack frame in editor because you're not connected to the packager."); return; } - NSData *stackFrameJSON = [RCTJSONStringify(stackFrame, NULL) dataUsingEncoding:NSUTF8StringEncoding]; + NSData *stackFrameJSON = [RCTJSONStringify([stackFrame toDictionary], NULL) dataUsingEncoding:NSUTF8StringEncoding]; NSString *postLength = [NSString stringWithFormat:@"%tu", stackFrameJSON.length]; NSMutableURLRequest *request = [NSMutableURLRequest new]; request.URL = [NSURL URLWithString:@"/open-stack-frame" relativeToURL:_bridge.bundleURL]; diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 3873f67f1c8d9d..6656696787f94b 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; + 008341F61D1DB34400876D9A /* RCTJSStackFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */; }; 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */; }; 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */; }; 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */; }; @@ -117,6 +118,8 @@ /* Begin PBXFileReference section */ 000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSourceCode.h; sourceTree = ""; }; 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; }; + 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSStackFrame.m; sourceTree = ""; }; + 008341F51D1DB34400876D9A /* RCTJSStackFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSStackFrame.h; sourceTree = ""; }; 131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControl.h; sourceTree = ""; }; 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControl.m; sourceTree = ""; }; 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControlManager.h; sourceTree = ""; }; @@ -575,6 +578,8 @@ 83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */, 14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */, 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */, + 008341F51D1DB34400876D9A /* RCTJSStackFrame.h */, + 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */, 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */, 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */, 83CBBA4D1A601E3B00E9B192 /* RCTLog.h */, @@ -703,6 +708,7 @@ 14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */, 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */, 352DCFF01D19F4C20056D623 /* RCTI18nUtil.m in Sources */, + 008341F61D1DB34400876D9A /* RCTJSStackFrame.m in Sources */, 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */, 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */, 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */, From 5d06918d0748df522ec9444d5742377095df609b Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 11 Jul 2016 06:52:06 -0700 Subject: [PATCH 063/241] Add new FileSourceProvider Summary: Add a new interface to JSC that allows loading a file lazily from disk, i.e. using mmap, instead of loading the whole file upfront and copying into the VM. Reviewed By: michalgr Differential Revision: D3534042 fbshipit-source-id: 98b193cc7b7e33248073e2556ea94ce3391507c7 --- .../react/XReactInstanceManagerImpl.java | 5 +- .../react/cxxbridge/CatalystInstanceImpl.java | 2 +- .../react/cxxbridge/JSBundleLoader.java | 9 +- .../jni/xreact/jni/CatalystInstanceImpl.cpp | 125 +++++++++++++++++- .../jni/xreact/jni/CatalystInstanceImpl.h | 2 +- .../src/main/jni/xreact/jni/OnLoad.cpp | 8 +- ReactAndroid/src/main/jni/xreact/jni/OnLoad.h | 1 + ReactCommon/cxxreact/Executor.h | 64 +++++++++ ReactCommon/cxxreact/JSCExecutor.cpp | 23 ++++ ReactCommon/cxxreact/JSCHelpers.cpp | 82 +++++++----- ReactCommon/cxxreact/JSCHelpers.h | 12 ++ 11 files changed, 287 insertions(+), 46 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java index e510d129cbb9c6..42911f885d11b5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java @@ -412,9 +412,12 @@ public void run() { } private void recreateReactContextInBackgroundFromBundleFile() { + boolean useLazyBundle = mJSCConfig.getConfigMap().hasKey("useLazyBundle") ? + mJSCConfig.getConfigMap().getBoolean("useLazyBundle") : false; + recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()), - JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile)); + JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile, useLazyBundle)); } /** 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 007495e763cbbf..569ae980ed3476 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -161,7 +161,7 @@ private native void initializeBridge(ReactCallback callback, MessageQueueThread moduleQueue, ModuleRegistryHolder registryHolder); - /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL); + /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean useLazyBundle); /* package */ native void loadScriptFromFile(String fileName, String sourceURL); @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java index c55b30ce692049..7b4d6e49976710 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java @@ -28,11 +28,18 @@ public abstract class JSBundleLoader { public static JSBundleLoader createFileLoader( final Context context, final String fileName) { + return createFileLoader(context, fileName, false); + } + + public static JSBundleLoader createFileLoader( + final Context context, + final String fileName, + final boolean useLazyBundle) { return new JSBundleLoader() { @Override public void loadScript(CatalystInstanceImpl instance) { if (fileName.startsWith("assets://")) { - instance.loadScriptFromAssets(context.getAssets(), fileName); + instance.loadScriptFromAssets(context.getAssets(), fileName, useLazyBundle); } else { instance.loadScriptFromFile(fileName, fileName); } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index e9bf94140ebf00..612107b7cc684c 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include #include #include @@ -23,6 +25,7 @@ #include "ModuleRegistryHolder.h" #include "NativeArray.h" #include "JNativeRunnable.h" +#include "OnLoad.h" using namespace facebook::jni; @@ -98,7 +101,7 @@ void CatalystInstanceImpl::registerNatives() { makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge), makeNativeMethod("loadScriptFromAssets", - "(Landroid/content/res/AssetManager;Ljava/lang/String;)V", + "(Landroid/content/res/AssetManager;Ljava/lang/String;Z)V", CatalystInstanceImpl::loadScriptFromAssets), makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile), makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction), @@ -149,20 +152,132 @@ void CatalystInstanceImpl::initializeBridge( mrh->getModuleRegistry()); } +#ifdef WITH_FBJSCEXTENSIONS +static std::unique_ptr loadScriptFromCache( + AAssetManager* manager, + std::string& sourceURL) { + + // 20-byte sha1 as hex + static const size_t HASH_STR_SIZE = 40; + + // load bundle hash from the metadata file in the APK + auto hash = react::loadScriptFromAssets(manager, sourceURL + ".meta"); + auto cacheDir = getApplicationCacheDir() + "/rn-bundle"; + auto encoding = static_cast(hash->c_str()[20]); + + if (mkdir(cacheDir.c_str(), 0755) == -1 && errno != EEXIST) { + throw std::runtime_error("Can't create cache directory"); + } + + if (encoding != JSBigMmapString::Encoding::Ascii) { + throw std::runtime_error("Can't use mmap fastpath for non-ascii bundles"); + } + + // convert hash to string + char hashStr[HASH_STR_SIZE + 1]; + for (size_t i = 0; i < HASH_STR_SIZE; i += 2) { + snprintf(hashStr + i, 3, "%02hhx", hash->c_str()[i / 2] & 0xFF); + } + + // the name of the cached bundle file should be the hash + std::string cachePath = cacheDir + "/" + hashStr; + FILE *cache = fopen(cachePath.c_str(), "r"); + SCOPE_EXIT { if (cache) fclose(cache); }; + + size_t size = 0; + if (cache == NULL) { + // delete old bundle, if there was one. + std::string metaPath = cacheDir + "/meta"; + if (auto meta = fopen(metaPath.c_str(), "r")) { + char oldBundleHash[HASH_STR_SIZE + 1]; + if (fread(oldBundleHash, HASH_STR_SIZE, 1, meta) == HASH_STR_SIZE) { + remove((cacheDir + "/" + oldBundleHash).c_str()); + remove(metaPath.c_str()); + } + fclose(meta); + } + + // load script from the APK and write to temporary file + auto script = react::loadScriptFromAssets(manager, sourceURL); + auto tmpPath = cachePath + "_"; + cache = fopen(tmpPath.c_str(), "w"); + if (!cache) { + throw std::runtime_error("Can't open cache, errno: " + errno); + } + if (fwrite(script->c_str(), 1, script->size(), cache) != size) { + remove(tmpPath.c_str()); + throw std::runtime_error("Failed to unpack bundle"); + } + + // force data to be written to disk + fsync(fileno(cache)); + fclose(cache); + + // move script to final path - atomic operation + if (rename(tmpPath.c_str(), cachePath.c_str())) { + throw std::runtime_error("Failed to update cache, errno: " + errno); + } + + // store the bundle hash in a metadata file + auto meta = fopen(metaPath.c_str(), "w"); + if (!meta) { + throw std::runtime_error("Failed to open metadata file to store bundle hash"); + } + if (fwrite(hashStr, HASH_STR_SIZE, 1, meta) != HASH_STR_SIZE) { + throw std::runtime_error("Failed to write bundle hash to metadata file"); + } + fsync(fileno(meta)); + fclose(meta); + + // return the final written cache + cache = fopen(cachePath.c_str(), "r"); + if (!cache) { + throw std::runtime_error("Cache has been cleared"); + } + } else { + struct stat fileInfo = {0}; + if (fstat(fileno(cache), &fileInfo)) { + throw std::runtime_error("Failed to get cache stats, errno: " + errno); + } + size = fileInfo.st_size; + } + + return folly::make_unique( + dup(fileno(cache)), + size, + reinterpret_cast(hash->c_str()), + encoding); +} +#endif + void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager, - const std::string& assetURL) { + const std::string& assetURL, + bool useLazyBundle) { const int kAssetsLength = 9; // strlen("assets://"); auto sourceURL = assetURL.substr(kAssetsLength); - auto manager = react::extractAssetManager(assetManager); - auto script = react::loadScriptFromAssets(manager, sourceURL); + if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) { + auto script = react::loadScriptFromAssets(manager, sourceURL); instance_->loadUnbundle( folly::make_unique(manager, sourceURL), std::move(script), sourceURL); + return; } else { - instance_->loadScriptFromString(std::move(script), std::move(sourceURL)); +#ifdef WITH_FBJSCEXTENSIONS + if (useLazyBundle) { + try { + auto script = loadScriptFromCache(manager, sourceURL); + instance_->loadScriptFromString(std::move(script), sourceURL); + return; + } catch (...) { + LOG(WARNING) << "Failed to load bundle as Source Code"; + } + } +#endif + auto script = react::loadScriptFromAssets(manager, sourceURL); + instance_->loadScriptFromString(std::move(script), sourceURL); } } diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index 1e5f2a894336bc..64835e6b461d1c 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -47,7 +47,7 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref jsQueue, jni::alias_ref moduleQueue, ModuleRegistryHolder* mrh); - void loadScriptFromAssets(jobject assetManager, const std::string& assetURL); + void loadScriptFromAssets(jobject assetManager, const std::string& assetURL, bool useLazyBundle); void loadScriptFromFile(jni::alias_ref fileName, const std::string& sourceURL); void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments); void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments); diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp index 952a6b46c4c4ff..67e167b4eaa9c8 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp @@ -51,10 +51,6 @@ static std::string getApplicationDir(const char* methodName) { return getAbsolutePathMethod(dirObj)->toStdString(); } -static std::string getApplicationCacheDir() { - return getApplicationDir("getCacheDir"); -} - static std::string getApplicationPersistentDir() { return getApplicationDir("getFilesDir"); } @@ -162,6 +158,10 @@ class JReactMarker : public JavaClass { } +std::string getApplicationCacheDir() { + return getApplicationDir("getCacheDir"); +} + extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return initialize(vm, [] { // Inject some behavior into react/ diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h index 5cd3c1749e62bf..713d8b582daf36 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h @@ -10,5 +10,6 @@ namespace facebook { namespace react { jmethodID getLogMarkerMethod(); +std::string getApplicationCacheDir(); } // namespace react } // namespace facebook diff --git a/ReactCommon/cxxreact/Executor.h b/ReactCommon/cxxreact/Executor.h index e2c5a331b6bf18..6b99df0b2cc1d0 100644 --- a/ReactCommon/cxxreact/Executor.h +++ b/ReactCommon/cxxreact/Executor.h @@ -7,6 +7,8 @@ #include #include +#include + #include #include "JSModulesUnbundle.h" @@ -134,6 +136,68 @@ class JSBigBufferString : public facebook::react::JSBigString { size_t m_size; }; +class JSBigMmapString : public JSBigString { +public: + enum class Encoding { + Unknown, + Ascii, + Utf8, + Utf16, + }; + + + JSBigMmapString(int fd, size_t size, const uint8_t sha1[20], Encoding encoding) : + m_fd(fd), + m_size(size), + m_encoding(encoding), + m_str(nullptr) + { + memcpy(m_hash, sha1, 20); + } + + ~JSBigMmapString() { + if (m_str) { + CHECK(munmap((void *)m_str, m_size) != -1); + } + close(m_fd); + } + + bool isAscii() const override { + return m_encoding == Encoding::Ascii; + } + + const char* c_str() const override { + if (!m_str) { + m_str = (const char *)mmap(0, m_size, PROT_READ, MAP_SHARED, m_fd, 0); + CHECK(m_str != MAP_FAILED); + } + return m_str; + } + + size_t size() const override { + return m_size; + } + + int fd() const { + return m_fd; + } + + const uint8_t* hash() const { + return m_hash; + } + + Encoding encoding() const { + return m_encoding; + } + +private: + int m_fd; + size_t m_size; + uint8_t m_hash[20]; + Encoding m_encoding; + mutable const char *m_str; +}; + class JSExecutor { public: /** diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index 0b624da12ee5cb..d26409c2e0bcc7 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -253,10 +253,33 @@ void JSCExecutor::terminateOnJSVMThread() { m_context = nullptr; } +#ifdef WITH_FBJSCEXTENSIONS +static void loadApplicationSource( + const JSGlobalContextRef context, + const JSBigMmapString* script, + const std::string& sourceURL) { + String jsSourceURL(sourceURL.c_str()); + bool is8bit = script->encoding() == JSBigMmapString::Encoding::Ascii || script->encoding() == JSBigMmapString::Encoding::Utf8; + JSSourceCodeRef sourceCode = JSCreateSourceCode(script->fd(), script->size(), jsSourceURL, script->hash(), is8bit); + evaluateSourceCode(context, sourceCode, jsSourceURL); + JSReleaseSourceCode(sourceCode); +} +#endif + void JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL) throw(JSException) { SystraceSection s("JSCExecutor::loadApplicationScript", "sourceURL", sourceURL); + #ifdef WITH_FBJSCEXTENSIONS + if (auto source = dynamic_cast(script.get())) { + loadApplicationSource(m_context, source, sourceURL); + bindBridge(); + flush(); + ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); + return; + } + #endif + #ifdef WITH_FBSYSTRACE fbsystrace_begin_section( TRACE_TAG_REACT_CXX_BRIDGE, diff --git a/ReactCommon/cxxreact/JSCHelpers.cpp b/ReactCommon/cxxreact/JSCHelpers.cpp index 40554219ddf4ae..194cc473791eac 100644 --- a/ReactCommon/cxxreact/JSCHelpers.cpp +++ b/ReactCommon/cxxreact/JSCHelpers.cpp @@ -44,47 +44,63 @@ JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef JSValueRef exn, result; result = JSEvaluateScript(context, script, NULL, source, 0, &exn); if (result == nullptr) { - Value exception = Value(context, exn); - - std::string exceptionText = exception.toString().str(); - - // The null/empty-ness of source tells us if the JS came from a - // file/resource, or was a constructed statement. The location - // info will include that source, if any. - std::string locationInfo = source != nullptr ? String::ref(source).str() : ""; - Object exObject = exception.asObject(); - auto line = exObject.getProperty("line"); - if (line != nullptr && line.isNumber()) { - if (locationInfo.empty() && line.asInteger() != 1) { - // If there is a non-trivial line number, but there was no - // location info, we include a placeholder, and the line - // number. - locationInfo = folly::to(":", line.asInteger()); - } else if (!locationInfo.empty()) { - // If there is location info, we always include the line - // number, regardless of its value. - locationInfo += folly::to(":", line.asInteger()); - } - } + formatAndThrowJSException(context, exn, source); + } + return result; +} - if (!locationInfo.empty()) { - exceptionText += " (" + locationInfo + ")"; +#if WITH_FBJSCEXTENSIONS +JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) { + JSValueRef exn, result; + result = JSEvaluateSourceCode(context, source, NULL, &exn); + if (result == nullptr) { + formatAndThrowJSException(context, exn, sourceURL); + } + return result; +} +#endif + +void formatAndThrowJSException(JSContextRef context, JSValueRef exn, JSStringRef source) { + Value exception = Value(context, exn); + + std::string exceptionText = exception.toString().str(); + + // The null/empty-ness of source tells us if the JS came from a + // file/resource, or was a constructed statement. The location + // info will include that source, if any. + std::string locationInfo = source != nullptr ? String::ref(source).str() : ""; + Object exObject = exception.asObject(); + auto line = exObject.getProperty("line"); + if (line != nullptr && line.isNumber()) { + if (locationInfo.empty() && line.asInteger() != 1) { + // If there is a non-trivial line number, but there was no + // location info, we include a placeholder, and the line + // number. + locationInfo = folly::to(":", line.asInteger()); + } else if (!locationInfo.empty()) { + // If there is location info, we always include the line + // number, regardless of its value. + locationInfo += folly::to(":", line.asInteger()); } + } + + if (!locationInfo.empty()) { + exceptionText += " (" + locationInfo + ")"; + } - LOG(ERROR) << "Got JS Exception: " << exceptionText; + LOG(ERROR) << "Got JS Exception: " << exceptionText; - Value jsStack = exObject.getProperty("stack"); - if (jsStack.isNull() || !jsStack.isString()) { - throwJSExecutionException("%s", exceptionText.c_str()); - } else { - LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str(); - throwJSExecutionExceptionWithStack( + Value jsStack = exObject.getProperty("stack"); + if (jsStack.isNull() || !jsStack.isString()) { + throwJSExecutionException("%s", exceptionText.c_str()); + } else { + LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str(); + throwJSExecutionExceptionWithStack( exceptionText.c_str(), jsStack.toString().str().c_str()); - } } - return result; } + JSValueRef makeJSError(JSContextRef ctx, const char *error) { JSValueRef nestedException = nullptr; JSValueRef args[] = { Value(ctx, String(error)) }; diff --git a/ReactCommon/cxxreact/JSCHelpers.h b/ReactCommon/cxxreact/JSCHelpers.h index 3be708164ae028..a55906d560c2d8 100644 --- a/ReactCommon/cxxreact/JSCHelpers.h +++ b/ReactCommon/cxxreact/JSCHelpers.h @@ -49,6 +49,18 @@ JSValueRef evaluateScript( JSStringRef script, JSStringRef sourceURL); +#if WITH_FBJSCEXTENSIONS +JSValueRef evaluateSourceCode( + JSContextRef ctx, + JSSourceCodeRef source, + JSStringRef sourceURL); +#endif + +void formatAndThrowJSException( + JSContextRef ctx, + JSValueRef exn, + JSStringRef sourceURL); + JSValueRef makeJSError(JSContextRef ctx, const char *error); JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation); From 4840233bcdd29de24ee709a6fe04b6ea3de4b1ec Mon Sep 17 00:00:00 2001 From: Mehdi Mulani Date: Mon, 11 Jul 2016 07:14:54 -0700 Subject: [PATCH 064/241] Bring back FBPortForwarding into fbobjc (Revert D3056293) Reviewed By: frantic Differential Revision: D3542397 fbshipit-source-id: 7f25f9753ec8d7a88f736014bba231fc5fb8b245 --- Tools/FBPortForwarding/Apps/MacApp/main.m | 23 -- Tools/FBPortForwarding/Apps/iOSApp/Info.plist | 36 --- .../Apps/iOSApp/PFAppDelegate.h | 16 - .../Apps/iOSApp/PFAppDelegate.m | 65 ---- Tools/FBPortForwarding/Apps/iOSApp/main.m | 19 -- .../FBPortForwarding/FBPortForwardingClient.h | 20 -- .../FBPortForwarding/FBPortForwardingClient.m | 284 ------------------ .../FBPortForwarding/FBPortForwardingCommon.h | 26 -- .../FBPortForwarding/FBPortForwardingServer.h | 20 -- .../FBPortForwarding/FBPortForwardingServer.m | 193 ------------ .../FBPortForwardingTests/PFPingClient.h | 22 -- .../FBPortForwardingTests/PFPingClient.m | 53 ---- .../FBPortForwardingTests/PFPingServer.h | 20 -- .../FBPortForwardingTests/PFPingServer.m | 55 ---- .../PFSimpleHTTPServer.h | 18 -- .../PFSimpleHTTPServer.m | 51 ---- .../FBPortForwardingTests/PFTests.m | 217 ------------- Tools/FBPortForwarding/README.md | 66 ---- 18 files changed, 1204 deletions(-) delete mode 100644 Tools/FBPortForwarding/Apps/MacApp/main.m delete mode 100644 Tools/FBPortForwarding/Apps/iOSApp/Info.plist delete mode 100644 Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.h delete mode 100644 Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.m delete mode 100644 Tools/FBPortForwarding/Apps/iOSApp/main.m delete mode 100644 Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.h delete mode 100644 Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.m delete mode 100644 Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingCommon.h delete mode 100644 Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.h delete mode 100644 Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.m delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.h delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.m delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.h delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.m delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.h delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.m delete mode 100644 Tools/FBPortForwarding/FBPortForwardingTests/PFTests.m delete mode 100644 Tools/FBPortForwarding/README.md diff --git a/Tools/FBPortForwarding/Apps/MacApp/main.m b/Tools/FBPortForwarding/Apps/MacApp/main.m deleted file mode 100644 index bfe623d2790a6a..00000000000000 --- a/Tools/FBPortForwarding/Apps/MacApp/main.m +++ /dev/null @@ -1,23 +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 - -#import - -int main(int argc, char *argv[]) -{ - FBPortForwardingClient *client = [FBPortForwardingClient new]; - [client forwardConnectionsToPort:8081]; - [client connectToMultiplexingChannelOnPort:8025]; - - [[NSRunLoop currentRunLoop] run]; - client = nil; - return 0; -} diff --git a/Tools/FBPortForwarding/Apps/iOSApp/Info.plist b/Tools/FBPortForwarding/Apps/iOSApp/Info.plist deleted file mode 100644 index 54a71b0241ad1b..00000000000000 --- a/Tools/FBPortForwarding/Apps/iOSApp/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - Port Forwarding - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - com.facebook.example.PortForwarding - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - - diff --git a/Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.h b/Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.h deleted file mode 100644 index e504b34bbb043b..00000000000000 --- a/Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.h +++ /dev/null @@ -1,16 +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 PFAppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - -@end diff --git a/Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.m b/Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.m deleted file mode 100644 index edeb6e90dab47f..00000000000000 --- a/Tools/FBPortForwarding/Apps/iOSApp/PFAppDelegate.m +++ /dev/null @@ -1,65 +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 "PFAppDelegate.h" - -#import - -@implementation PFAppDelegate -{ - FBPortForwardingServer *_portForwardingServer; -} - -@synthesize window = window_; - - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - CGRect rect = [[UIScreen mainScreen] bounds]; - - UIView *view = [[UIView alloc] initWithFrame:rect]; - view.backgroundColor = [UIColor whiteColor]; - UIViewController *controller = [UIViewController new]; - controller.view = view; - - UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [button setTitle:@"Send request" forState:UIControlStateNormal]; - [button addTarget:self action:@selector(sendRequest) forControlEvents:UIControlEventTouchUpInside]; - button.frame = CGRectMake(0, 0, 200, 50); - button.center = view.center; - [view addSubview:button]; - - self.window = [[UIWindow alloc] initWithFrame:rect]; - self.window.rootViewController = controller; - [self.window makeKeyAndVisible]; - - _portForwardingServer = [FBPortForwardingServer new]; - [_portForwardingServer forwardConnectionsFromPort:8082]; - [_portForwardingServer listenForMultiplexingChannelOnPort:8025]; - - return YES; -} - -- (void)sendRequest -{ - NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://localhost:8082/404"]]; - [[[NSURLConnection alloc] initWithRequest:req delegate:self] start]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data -{ - NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSLog(@"Success: %@", content); -} - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - NSLog(@"Error: %@", error); -} - -@end diff --git a/Tools/FBPortForwarding/Apps/iOSApp/main.m b/Tools/FBPortForwarding/Apps/iOSApp/main.m deleted file mode 100644 index 3b145a74555ad1..00000000000000 --- a/Tools/FBPortForwarding/Apps/iOSApp/main.m +++ /dev/null @@ -1,19 +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 - -#import "PFAppDelegate.h" - -int main(int argc, char *argv[]) -{ - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([PFAppDelegate class])); - } -} diff --git a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.h b/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.h deleted file mode 100644 index 9a4887829fca01..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.h +++ /dev/null @@ -1,20 +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 FBPortForwardingClient : NSObject - -- (instancetype)init; - -- (void)forwardConnectionsToPort:(NSUInteger)port; -- (void)connectToMultiplexingChannelOnPort:(NSUInteger)port; -- (void)close; - -@end diff --git a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.m b/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.m deleted file mode 100644 index d9d1e97820f426..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingClient.m +++ /dev/null @@ -1,284 +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 "FBPortForwardingClient.h" - -#import - -#import -#import - -#import "FBPortForwardingCommon.h" - -static const NSTimeInterval ReconnectDelay = 1.0; - -@interface FBPortForwardingClient () -{ - NSUInteger _destPort; - NSUInteger _channelPort; - NSNumber *_connectingToDeviceID; - NSNumber *_connectedDeviceID; - NSDictionary *_connectedDeviceProperties; - BOOL _notConnectedQueueSuspended; - PTChannel *_connectedChannel; - dispatch_queue_t _notConnectedQueue; - dispatch_queue_t _clientSocketsQueue; - NSMutableDictionary *_clientSockets; -} - -@property (atomic, readonly) NSNumber *connectedDeviceID; -@property (atomic, assign) PTChannel *connectedChannel; - -@end - -@implementation FBPortForwardingClient - -@synthesize connectedDeviceID = _connectedDeviceID; - -- (instancetype)init -{ - if (self = [super init]) { - _notConnectedQueue = dispatch_queue_create("FBPortForwarding.notConnectedQueue", DISPATCH_QUEUE_SERIAL); - _clientSocketsQueue = dispatch_queue_create("FBPortForwarding.clients", DISPATCH_QUEUE_SERIAL); - _clientSockets = [NSMutableDictionary dictionary]; - } - return self; -} - -- (void)forwardConnectionsToPort:(NSUInteger)port -{ - _destPort = port; -} - -- (void)connectToMultiplexingChannelOnPort:(NSUInteger)port -{ - _channelPort = port; - [self startListeningForDevices]; - [self enqueueConnectToLocalIPv4Port]; -} - -- (void)close -{ - [self.connectedChannel close]; -} - -- (PTChannel *)connectedChannel { - return _connectedChannel; -} - -- (void)setConnectedChannel:(PTChannel *)connectedChannel { - _connectedChannel = connectedChannel; - - if (!_connectedChannel) { - for (GCDAsyncSocket *sock in [_clientSockets objectEnumerator]) { - [sock setDelegate:nil]; - [sock disconnect]; - } - [_clientSockets removeAllObjects]; - } - - // Toggle the notConnectedQueue_ depending on if we are connected or not - if (!_connectedChannel && _notConnectedQueueSuspended) { - dispatch_resume(_notConnectedQueue); - _notConnectedQueueSuspended = NO; - } else if (_connectedChannel && !_notConnectedQueueSuspended) { - dispatch_suspend(_notConnectedQueue); - _notConnectedQueueSuspended = YES; - } - - if (!_connectedChannel && _connectingToDeviceID) { - [self enqueueConnectToUSBDevice]; - } -} - - -#pragma mark - PTChannelDelegate - -- (void)ioFrameChannel:(PTChannel *)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData *)payload { - //NSLog(@"received %@, %u, %u, %@", channel, type, tag, payload); - - if (type == FBPortForwardingFrameTypeOpenPipe) { - GCDAsyncSocket *sock = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_clientSocketsQueue]; - sock.userData = @(tag); - _clientSockets[@(tag)] = sock; - - NSError *connectError; - if (![sock connectToHost:@"localhost" onPort:_destPort error:&connectError]) { - FBPFLog(@"Failed to connect to local %lu - %@", (unsigned long)_destPort, connectError); - } - - FBPFTrace(@"open socket (%d)", tag); - } - - if (type == FBPortForwardingFrameTypeWriteToPipe) { - GCDAsyncSocket *sock = _clientSockets[@(tag)]; - [sock writeData:[NSData dataWithBytes:payload.data length:payload.length] withTimeout:-1 tag:0]; - FBPFTrace(@"channel -> socket (%d) %zu bytes", tag, payload.length); - } - - if (type == FBPortForwardingFrameTypeClosePipe) { - GCDAsyncSocket *sock = _clientSockets[@(tag)]; - [sock disconnectAfterWriting]; - FBPFTrace(@"close socket (%d)", tag); - } -} - -- (void)ioFrameChannel:(PTChannel *)channel didEndWithError:(NSError *)error { - if (_connectedDeviceID && [_connectedDeviceID isEqualToNumber:channel.userInfo]) { - [self didDisconnectFromDevice:_connectedDeviceID]; - } - - if (_connectedChannel == channel) { - FBPFTrace(@"Disconnected from %@", channel.userInfo); - self.connectedChannel = nil; - } -} - - -#pragma mark - GCDAsyncSocketDelegate - - -- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port -{ - FBPFTrace(@"socket (%ld) connected to %@", (long)[sock.userData integerValue], host); - [sock readDataWithTimeout:-1 tag:0]; -} - -- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err -{ - UInt32 tag = [sock.userData unsignedIntValue]; - [_clientSockets removeObjectForKey:@(tag)]; - FBPFTrace(@"socket (%d) disconnected", (unsigned int)tag); - - [_connectedChannel sendFrameOfType:FBPortForwardingFrameTypeClosePipe tag:tag withPayload:nil callback:nil]; -} - -- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)_ -{ - UInt32 tag = [sock.userData unsignedIntValue]; - [_connectedChannel sendFrameOfType:FBPortForwardingFrameTypeWriteToPipe tag:tag withPayload:NSDataToGCDData(data) callback:^(NSError *error) { - FBPFTrace(@"channel -> socket (%d), %lu bytes", (unsigned int)tag, (unsigned long)data.length); - [sock readDataWithTimeout:-1 tag:0]; - }]; -} - -#pragma mark - Wired device connections - - -- (void)startListeningForDevices { - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - - [nc addObserverForName:PTUSBDeviceDidAttachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) { - NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"]; - //NSLog(@"PTUSBDeviceDidAttachNotification: %@", note.userInfo); - FBPFTrace(@"PTUSBDeviceDidAttachNotification: %@", deviceID); - - dispatch_async(_notConnectedQueue, ^{ - if (!_connectingToDeviceID || ![deviceID isEqualToNumber:_connectingToDeviceID]) { - [self disconnectFromCurrentChannel]; - _connectingToDeviceID = deviceID; - _connectedDeviceProperties = [note.userInfo objectForKey:@"Properties"]; - [self enqueueConnectToUSBDevice]; - } - }); - }]; - - [nc addObserverForName:PTUSBDeviceDidDetachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) { - NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"]; - //NSLog(@"PTUSBDeviceDidDetachNotification: %@", note.userInfo); - FBPFTrace(@"PTUSBDeviceDidDetachNotification: %@", deviceID); - - if ([_connectingToDeviceID isEqualToNumber:deviceID]) { - _connectedDeviceProperties = nil; - _connectingToDeviceID = nil; - if (_connectedChannel) { - [_connectedChannel close]; - } - } - }]; -} - - -- (void)didDisconnectFromDevice:(NSNumber *)deviceID { - FBPFLog(@"Disconnected from device #%@", deviceID); - if ([_connectedDeviceID isEqualToNumber:deviceID]) { - [self willChangeValueForKey:@"connectedDeviceID"]; - _connectedDeviceID = nil; - [self didChangeValueForKey:@"connectedDeviceID"]; - } -} - - -- (void)disconnectFromCurrentChannel { - if (_connectedDeviceID && _connectedChannel) { - [_connectedChannel close]; - self.connectedChannel = nil; - } -} - -- (void)enqueueConnectToLocalIPv4Port { - dispatch_async(_notConnectedQueue, ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self connectToLocalIPv4Port]; - }); - }); -} - - -- (void)connectToLocalIPv4Port { - PTChannel *channel = [PTChannel channelWithDelegate:self]; - channel.userInfo = [NSString stringWithFormat:@"127.0.0.1:%lu", (unsigned long)_channelPort]; - [channel connectToPort:_channelPort IPv4Address:INADDR_LOOPBACK callback:^(NSError *error, PTAddress *address) { - if (error) { - if (error.domain == NSPOSIXErrorDomain && (error.code == ECONNREFUSED || error.code == ETIMEDOUT)) { - // this is an expected state - } else { - FBPFTrace(@"Failed to connect to 127.0.0.1:%lu: %@", (unsigned long)_channelPort, error); - } - } else { - [self disconnectFromCurrentChannel]; - self.connectedChannel = channel; - channel.userInfo = address; - FBPFLog(@"Connected to %@", address); - } - [self performSelector:@selector(enqueueConnectToLocalIPv4Port) withObject:nil afterDelay:ReconnectDelay]; - }]; -} - -- (void)enqueueConnectToUSBDevice { - dispatch_async(_notConnectedQueue, ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self connectToUSBDevice]; - }); - }); -} - - -- (void)connectToUSBDevice { - PTChannel *channel = [PTChannel channelWithDelegate:self]; - channel.userInfo = _connectingToDeviceID; - channel.delegate = self; - - [channel connectToPort:(int)_channelPort overUSBHub:PTUSBHub.sharedHub deviceID:_connectingToDeviceID callback:^(NSError *error) { - if (error) { - FBPFTrace(@"Failed to connect to device #%@: %@", channel.userInfo, error); - if (channel.userInfo == _connectingToDeviceID) { - [self performSelector:@selector(enqueueConnectToUSBDevice) withObject:nil afterDelay:ReconnectDelay]; - } - } else { - _connectedDeviceID = _connectingToDeviceID; - self.connectedChannel = channel; - FBPFLog(@"Connected to device #%@\n%@", _connectingToDeviceID, _connectedDeviceProperties); - } - }]; -} - - - -@end diff --git a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingCommon.h b/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingCommon.h deleted file mode 100644 index b240b660d1a899..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingCommon.h +++ /dev/null @@ -1,26 +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 - -#define FBPFTrace(...) /*NSLog(__VA_ARGS__)*/ -#define FBPFLog(...) NSLog(__VA_ARGS__) - -enum { - FBPortForwardingFrameTypeOpenPipe = 201, - FBPortForwardingFrameTypeWriteToPipe = 202, - FBPortForwardingFrameTypeClosePipe = 203, -}; - -static dispatch_data_t NSDataToGCDData(NSData *data) { - __block NSData *retainedData = data; - return dispatch_data_create(data.bytes, data.length, nil, ^{ - retainedData = nil; - }); -} diff --git a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.h b/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.h deleted file mode 100644 index 5bc082fd0213e0..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.h +++ /dev/null @@ -1,20 +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 FBPortForwardingServer : NSObject - -- (instancetype)init; - -- (void)listenForMultiplexingChannelOnPort:(NSUInteger)port; -- (void)forwardConnectionsFromPort:(NSUInteger)port; -- (void)close; - -@end diff --git a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.m b/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.m deleted file mode 100644 index 3bc0daec886938..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwarding/FBPortForwardingServer.m +++ /dev/null @@ -1,193 +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 "FBPortForwardingServer.h" - -#import - -#import - -#import - -#import "FBPortForwardingCommon.h" - -@interface FBPortForwardingServer () -{ - __weak PTChannel *_serverChannel; - __weak PTChannel *_peerChannel; - - GCDAsyncSocket *_serverSocket; - NSMutableDictionary *_clientSockets; - UInt32 _lastClientSocketTag; - dispatch_queue_t _socketQueue; - PTProtocol *_protocol; -} - -@end - -@implementation FBPortForwardingServer - -- (instancetype)init -{ - if (self = [super init]) { - _socketQueue = dispatch_queue_create("FBPortForwardingServer", DISPATCH_QUEUE_SERIAL); - _lastClientSocketTag = 0; - _clientSockets = [NSMutableDictionary dictionary]; - _protocol = [[PTProtocol alloc] initWithDispatchQueue:_socketQueue]; - } - return self; -} - -- (void)dealloc -{ - [self close]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)forwardConnectionsFromPort:(NSUInteger)port -{ - [self _forwardConnectionsFromPort:port reportError:YES]; - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) { - [self _forwardConnectionsFromPort:port reportError:NO]; - }]; -} - -- (void)_forwardConnectionsFromPort:(NSUInteger)port reportError:(BOOL)shouldReportError -{ - GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_socketQueue]; - NSError *listenError; - if ([serverSocket acceptOnPort:port error:&listenError]) { - _serverSocket = serverSocket; - } else { - if (shouldReportError) { - FBPFLog(@"Failed to listen: %@", listenError); - } - } -} - -- (void)listenForMultiplexingChannelOnPort:(NSUInteger)port -{ - [self _listenForMultiplexingChannelOnPort:port reportError:YES]; - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) { - [self _listenForMultiplexingChannelOnPort:port reportError:NO]; - }]; -} - -- (void)_listenForMultiplexingChannelOnPort:(NSUInteger)port reportError:(BOOL)shouldReportError -{ - PTChannel *channel = [[PTChannel alloc] initWithProtocol:_protocol delegate:self]; - [channel listenOnPort:port IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) { - if (error) { - if (shouldReportError) { - FBPFLog(@"Failed to listen on 127.0.0.1:%lu: %@", (unsigned long)port, error); - } - } else { - FBPFTrace(@"Listening on 127.0.0.1:%lu", (unsigned long)port); - _serverChannel = channel; - } - }]; -} - -- (void)close -{ - if (_serverChannel) { - [_serverChannel close]; - _serverChannel = nil; - } - [_serverSocket disconnect]; -} - -#pragma mark - PTChannelDelegate - -- (void)ioFrameChannel:(PTChannel *)channel didAcceptConnection:(PTChannel *)otherChannel fromAddress:(PTAddress *)address { - // Cancel any other connection. We are FIFO, so the last connection - // established will cancel any previous connection and "take its place". - if (_peerChannel) { - [_peerChannel cancel]; - } - - // Weak pointer to current connection. Connection objects live by themselves - // (owned by its parent dispatch queue) until they are closed. - _peerChannel = otherChannel; - _peerChannel.userInfo = address; - FBPFTrace(@"Connected to %@", address); -} - -- (void)ioFrameChannel:(PTChannel *)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData *)payload { - //NSLog(@"didReceiveFrameOfType: %u, %u, %@", type, tag, payload); - if (type == FBPortForwardingFrameTypeWriteToPipe) { - GCDAsyncSocket *sock = _clientSockets[@(tag)]; - [sock writeData:[NSData dataWithBytes:payload.data length:payload.length] withTimeout:-1 tag:0]; - FBPFTrace(@"channel -> socket (%d), %zu bytes", tag, payload.length); - } - - if (type == FBPortForwardingFrameTypeClosePipe) { - GCDAsyncSocket *sock = _clientSockets[@(tag)]; - [sock disconnectAfterWriting]; - } -} - -- (void)ioFrameChannel:(PTChannel *)channel didEndWithError:(NSError *)error { - for (GCDAsyncSocket *sock in [_clientSockets objectEnumerator]) { - [sock setDelegate:nil]; - [sock disconnect]; - } - [_clientSockets removeAllObjects]; - FBPFTrace(@"Disconnected from %@, error = %@", channel.userInfo, error); -} - - -#pragma mark - GCDAsyncSocketDelegate - -- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket -{ - dispatch_block_t block = ^() { - if (!_peerChannel) { - [newSocket setDelegate:nil]; - [newSocket disconnect]; - } - - UInt32 tag = ++_lastClientSocketTag; - newSocket.userData = @(tag); - newSocket.delegate = self; - _clientSockets[@(tag)] = newSocket; - [_peerChannel sendFrameOfType:FBPortForwardingFrameTypeOpenPipe tag:_lastClientSocketTag withPayload:nil callback:^(NSError *error) { - FBPFTrace(@"open socket (%d), error = %@", (unsigned int)tag, error); - [newSocket readDataWithTimeout:-1 tag:0]; - }]; - }; - - if (_peerChannel) { - block(); - } else { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), _socketQueue, block); - } -} - -- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)_ -{ - UInt32 tag = [[sock userData] unsignedIntValue]; - FBPFTrace(@"Incoming data on socket (%d) - %lu bytes", (unsigned int)tag, (unsigned long)data.length); - [_peerChannel sendFrameOfType:FBPortForwardingFrameTypeWriteToPipe tag:tag withPayload:NSDataToGCDData(data) callback:^(NSError *error) { - FBPFTrace(@"socket (%d) -> channel %lu bytes, error = %@", (unsigned int)tag, (unsigned long)data.length, error); - [sock readDataWithTimeout:-1 tag:_]; - }]; -} - -- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err -{ - UInt32 tag = [sock.userData unsignedIntValue]; - [_clientSockets removeObjectForKey:@(tag)]; - [_peerChannel sendFrameOfType:FBPortForwardingFrameTypeClosePipe tag:tag withPayload:nil callback:^(NSError *error) { - FBPFTrace(@"socket (%d) disconnected, err = %@, peer error = %@", (unsigned int)tag, err, error); - }]; -} - - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.h b/Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.h deleted file mode 100644 index 1bc3f988711262..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.h +++ /dev/null @@ -1,22 +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 - -#import - -@interface PFPingClient : NSObject - -- (BOOL)connectToLocalServerOnPort:(NSUInteger)port; -- (void)sendPing:(NSData *)ping; - -@property (nonatomic, copy, readonly) NSArray *pongs; -@property (nonatomic, readonly) BOOL connected; - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.m b/Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.m deleted file mode 100644 index 36297585083ce9..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingClient.m +++ /dev/null @@ -1,53 +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 "PFPingClient.h" - -@implementation PFPingClient -{ - GCDAsyncSocket *_client; -} - -- (instancetype)init -{ - if (self = [super init]) { - _pongs = [NSArray array]; - _client = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; - } - return self; -} - -- (BOOL)connectToLocalServerOnPort:(NSUInteger)port -{ - return [_client connectToHost:@"localhost" onPort:port error:nil]; -} - -- (void)sendPing:(NSData *)ping -{ - [_client writeData:ping withTimeout:-1 tag:0]; -} - -- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port -{ - _connected = YES; - [_client readDataWithTimeout:-1 tag:0]; -} - -- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag -{ - _pongs = [_pongs arrayByAddingObject:data]; - [_client readDataWithTimeout:-1 tag:0]; -} - -- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err -{ - _connected = NO; -} - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.h b/Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.h deleted file mode 100644 index c8859b2aa81083..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.h +++ /dev/null @@ -1,20 +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 - -#import - -@interface PFPingServer : NSObject - -- (instancetype)initWithPort:(NSUInteger)port; - -@property (readonly, nonatomic) NSInteger clientsCount; - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.m b/Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.m deleted file mode 100644 index 7e4e03495a27ae..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFPingServer.m +++ /dev/null @@ -1,55 +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 "PFPingServer.h" - -@implementation PFPingServer -{ - GCDAsyncSocket *_server; - NSMutableArray *_clients; -} - -- (instancetype)initWithPort:(NSUInteger)port -{ - if (self = [super init]) { - _clients = [NSMutableArray array]; - _server = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; - - NSError *error; - if (![_server acceptOnPort:port error:&error]) { - NSLog(@"Failed to listen on port %lu: %@", (unsigned long)port, error); - return nil; - } - } - return self; -} - -- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket -{ - [_clients addObject:newSocket]; - [newSocket readDataWithTimeout:-1 tag:0]; -} - -- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag -{ - [sock writeData:data withTimeout:-1 tag:0]; - [sock readDataWithTimeout:-1 tag:0]; -} - -- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err -{ - [_clients removeObject:sock]; -} - -- (NSInteger)clientsCount -{ - return [_clients count]; -} - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.h b/Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.h deleted file mode 100644 index d5e1c6994e3ac3..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.h +++ /dev/null @@ -1,18 +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 - -#import - -@interface PFSimpleHTTPServer : NSObject - -- (instancetype)initWithPort:(NSUInteger)port response:(NSData *)data; - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.m b/Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.m deleted file mode 100644 index 4d1989bbaf8ab5..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFSimpleHTTPServer.m +++ /dev/null @@ -1,51 +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 "PFSimpleHTTPServer.h" - -@implementation PFSimpleHTTPServer -{ - NSData *_response; - GCDAsyncSocket *_server; - NSMutableArray *_clients; -} - -- (instancetype)initWithPort:(NSUInteger)port response:(NSData *)data -{ - if (self = [super init]) { - _response = [data copy]; - _clients = [NSMutableArray array]; - _server = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; - if (![_server acceptOnPort:port error:nil]) { - return nil; - }; - } - return self; -} - -- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket -{ - [_clients addObject:newSocket]; - [newSocket readDataToData:[NSData dataWithBytes:"\r\n\r\n" length:4] withTimeout:-1 tag:0]; -} - -- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag -{ - NSString *headers = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n", (unsigned long)[_response length]]; - [sock writeData:[headers dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0]; - [sock writeData:_response withTimeout:-1 tag:0]; - [sock disconnectAfterWriting]; -} - -- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err -{ - [_clients removeObject:sock]; -} - -@end diff --git a/Tools/FBPortForwarding/FBPortForwardingTests/PFTests.m b/Tools/FBPortForwarding/FBPortForwardingTests/PFTests.m deleted file mode 100644 index b7bfd0f7dcea95..00000000000000 --- a/Tools/FBPortForwarding/FBPortForwardingTests/PFTests.m +++ /dev/null @@ -1,217 +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 -#import - -#import -#import - -#import - -#import "PFPingClient.h" -#import "PFPingServer.h" -#import "PFSimpleHTTPServer.h" - -#define PFWaitForPongNumber(n) \ - XCTAssertTrue(FBRunRunLoopUntilBlockIsTrue(^BOOL{ \ - return [client1.pongs count] == n; \ - }), @"Failed to receive pong"); - -@interface PFTests : XCTestCase - -@end - -@implementation PFTests - -- (void)simpleHTTPTestWithData:(NSData *)data -{ - FBPortForwardingServer *portForwardingServer = [[FBPortForwardingServer alloc] init]; - [portForwardingServer forwardConnectionsFromPort:9701]; - [portForwardingServer listenForMultiplexingChannelOnPort:8055]; - - FBPortForwardingClient *portForwardingClient = [[FBPortForwardingClient alloc] init]; - [portForwardingClient forwardConnectionsToPort:9702]; - [portForwardingClient connectToMultiplexingChannelOnPort:8055]; - - PFSimpleHTTPServer *httpServer = [[PFSimpleHTTPServer alloc] initWithPort:9702 response:data]; - XCTAssertNotNil(httpServer); - - __block BOOL finished = NO; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:9701/"]]; - [NSURLConnection sendAsynchronousRequest:request - queue:[NSOperationQueue mainQueue] - completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *connectionError) { - XCTAssertNil(connectionError); - XCTAssertTrue([data isEqualToData:responseData]); - finished = YES; - }]; - - XCTAssert(FBRunRunLoopWithConditionReturningPassed(&finished)); - [portForwardingServer close]; - FBRunRunLoopBarrier(); -} - -- (void)testProxiesHTTPRequests -{ - [self simpleHTTPTestWithData:[@"OK" dataUsingEncoding:NSUTF8StringEncoding]]; -} - -- (void)testLargeHTTPResponse -{ - NSMutableData *largePayload = [NSMutableData data]; - [largePayload setLength:10000000]; - char *bytes = [largePayload mutableBytes]; - for (NSUInteger i = 0; i < largePayload.length; i++) { - bytes[i] = (char)(i % 255); - } - - [self simpleHTTPTestWithData:largePayload]; -} - -- (void)testPingPong -{ - FBPortForwardingServer *portForwardingServer = [[FBPortForwardingServer alloc] init]; - [portForwardingServer forwardConnectionsFromPort:9706]; - [portForwardingServer listenForMultiplexingChannelOnPort:8055]; - - FBPortForwardingClient *portForwardingClient = [[FBPortForwardingClient alloc] init]; - [portForwardingClient forwardConnectionsToPort:9705]; - [portForwardingClient connectToMultiplexingChannelOnPort:8055]; - - PFPingServer *server = [[PFPingServer alloc] initWithPort:9705]; - XCTAssertNotNil(server); - - PFPingClient *client1 = [[PFPingClient alloc] init]; - [client1 connectToLocalServerOnPort:9706]; - - PFPingClient *client2 = [[PFPingClient alloc] init]; - [client2 connectToLocalServerOnPort:9706]; - - XCTAssertTrue(FBRunRunLoopUntilBlockIsTrue(^BOOL{ - return client1.connected && client2.connected; - }), @"Failed to connect"); - - NSData *ping = [NSData dataWithBytes:"PING" length:4]; - [client1 sendPing:ping]; - - PFWaitForPongNumber(1); - - NSData *pong = client1.pongs[0]; - XCTAssert([ping isEqualToData:pong]); - - [client1 sendPing:ping]; - PFWaitForPongNumber(2); - - [client1 sendPing:ping]; - PFWaitForPongNumber(3); - - [client1 sendPing:ping]; - PFWaitForPongNumber(4); - - [client1 sendPing:ping]; - [client1 sendPing:ping]; - PFWaitForPongNumber(6); - - XCTAssertEqual(0, client2.pongs.count); - - [portForwardingServer close]; -} - -- (void)testDisconnectsWhenNoChannel -{ - FBPortForwardingServer *portForwardingServer = [[FBPortForwardingServer alloc] init]; - [portForwardingServer forwardConnectionsFromPort:9707]; - [portForwardingServer listenForMultiplexingChannelOnPort:8056]; - - __block BOOL finished = NO; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:9707/"]]; - [NSURLConnection sendAsynchronousRequest:request - queue:[NSOperationQueue mainQueue] - completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *connectionError) { - XCTAssertNotNil(connectionError); - finished = YES; - }]; - - NSDate *start = [NSDate date]; - XCTAssert(FBRunRunLoopWithConditionReturningPassed(&finished)); - NSTimeInterval elapsed = -[start timeIntervalSinceNow]; - XCTAssertLessThan(elapsed, 5, @"Must disconnect - no port forwarding client"); - - [portForwardingServer close]; -} - -- (void)testWaitsForChannel -{ - FBPortForwardingServer *portForwardingServer = [[FBPortForwardingServer alloc] init]; - [portForwardingServer forwardConnectionsFromPort:9707]; - [portForwardingServer listenForMultiplexingChannelOnPort:8056]; - - NSData *data = [@"OK" dataUsingEncoding:NSUTF8StringEncoding]; - PFSimpleHTTPServer *httpServer = [[PFSimpleHTTPServer alloc] initWithPort:9702 response:data]; - XCTAssertNotNil(httpServer); - - __block BOOL finished = NO; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:9707/"]]; - [NSURLConnection sendAsynchronousRequest:request - queue:[NSOperationQueue mainQueue] - completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *connectionError) { - XCTAssertNil(connectionError); - XCTAssertNotNil(responseData); - NSString *res = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(res, @"OK"); - finished = YES; - }]; - - NSDate *start = [NSDate date]; - FBRunRunLoopUntilBlockIsTrue(^BOOL{ - return [start timeIntervalSinceNow] < -0.5; - }); - - // NOTE: Establishing port forwarding connection *after* sending HTTP request - FBPortForwardingClient *portForwardingClient = [[FBPortForwardingClient alloc] init]; - [portForwardingClient forwardConnectionsToPort:9702]; - [portForwardingClient connectToMultiplexingChannelOnPort:8056]; - - XCTAssert(FBRunRunLoopWithConditionReturningPassed(&finished)); - [portForwardingServer close]; -} - -- (void)testDisconnectsWhenChannelConnectionLost -{ - FBPortForwardingServer *portForwardingServer = [[FBPortForwardingServer alloc] init]; - [portForwardingServer forwardConnectionsFromPort:9706]; - [portForwardingServer listenForMultiplexingChannelOnPort:8055]; - - FBPortForwardingClient *portForwardingClient = [[FBPortForwardingClient alloc] init]; - [portForwardingClient forwardConnectionsToPort:9705]; - [portForwardingClient connectToMultiplexingChannelOnPort:8055]; - - PFPingServer *server = [[PFPingServer alloc] initWithPort:9705]; - XCTAssertNotNil(server); - - PFPingClient *client1 = [[PFPingClient alloc] init]; - [client1 connectToLocalServerOnPort:9706]; - - XCTAssertTrue(FBRunRunLoopUntilBlockIsTrue(^BOOL{ - return client1.connected; - }), @"Failed to connect"); - - [portForwardingClient close]; - - XCTAssertTrue(FBRunRunLoopUntilBlockIsTrue(^BOOL{ - return !client1.connected; - }), @"Failed to disconnect"); - - XCTAssertEqual(server.clientsCount, 0); - - [portForwardingServer close]; -} - -@end diff --git a/Tools/FBPortForwarding/README.md b/Tools/FBPortForwarding/README.md deleted file mode 100644 index 790387c49b3207..00000000000000 --- a/Tools/FBPortForwarding/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# FBPortForwarding - -FBPortForwarding lets you expose your Mac's port to iOS device via lightning -cable. The typical usecase is connecting to a TCP server that runs on OS X -from an iPhone app without common WiFi network. - -## Benefits: - - 1. No need to be on the same WiFi, worry about firewalls (fbguest) or VPN - 2. iOS app doesn't have to know your Mac's IP address - 3. Secure - communication is possible only when connected via USB - -## How it works - -iOS provides a way to connect to device's TCP server from Mac via USBHub, but -there is no API to connect from iOS to TCP server running on Mac. FBPortForwarding -uses [Peertalk](https://github.com/rsms/peertalk) to establish communication -channel from Mac to iOS, creates a TCP server on iOS and multiplexes all -connections to that server via the peertalk channel. Helper app running on Mac -listens for commands on the peertalk channel and initializes TCP connections -to local port and forwards all communication back via the same peertalk channel. - - - | - iOS Device | Mac - | - +----------------+ +----------------+ - |Peertalk Server | connect |Peertalk Client | - | <------------+ | - | | | | - | Port 8025| | | - +----+-----------+ +---------^------+ - | | - | | - incoming +----------------+ | | +--------------+ - connections |Proxy Server | | | |Real Server | - ------------>> | | +-------------+ commands | | | - | Port 8081| | create | | stream | | Port 8081| - +-+--------------+ +---------> Peertalk <----------+ +-^------------+ - | | Channel | ^ - | +--------+ | | +--------+ | outgoing - | | | onConnect | | connect | | | connections - +---> Client +---------------> OpenPipe +---------------> Client +-----+ - | #[tag] | onRead | | write | #[tag] | - | +---------------> WriteToPipe +---------------> | - | | onDisconnect | | disconnect | | - | +---------------> ClosePipe +---------------> | - | | | | | | - | | write | | onRead | | - | <---------------+ WriteToPipe <---------------+ | - | | close | | onDisconnect | | - | <---------------+ ClosePipe <---------------+ | - | | | | | | - +--------+ | | +--------+ - +-------------+ - -First, the library on iOS device creates a TCP server on the port we want to -forward (let's say 8081) and a special Peertalk server on port 8025. Mac helper -app looks for connected iOS devices, and once it finds one it connects to its -peertalk server. Only *one* channel is created that's going to be used for -multiplexing data. - -When a socket connects to local proxy server, FBPortForwarding is going to assign -a tag to the connection and use peertalk channel to tell Mac helper app to connect -to TCP port 8081 on Mac. Now events and data on both sides of the wire are going -to be multiplexed and transferred via the peertalk channel. From 7b718b03ebd571026acd0b17850ef786dfc10cc3 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Jul 2016 08:25:43 -0700 Subject: [PATCH 065/241] Allow RCTDisplayLink to pause more often Summary: By default we run the the JS display link, even if there are no modules listening. Given that most listeners will be lazily constructed, let's make it paused by default. Since RCTTiming almost never unpauses due to some long-lived timers, implement a sleep timer that pauses the displaylink but uses an NSTimer to wake up in time. Reviewed By: mhorowitz Differential Revision: D3235044 fbshipit-source-id: 4a340fea552ada1bd8bc0d83b596a7df6f992387 --- React/Base/RCTDisplayLink.m | 44 ++++++++----- React/Base/RCTModuleMethod.m | 2 +- React/Modules/RCTTiming.m | 116 +++++++++++++++++++++++++++++------ 3 files changed, 126 insertions(+), 36 deletions(-) diff --git a/React/Base/RCTDisplayLink.m b/React/Base/RCTDisplayLink.m index 12450cb96cd489..7042dc0b6929df 100644 --- a/React/Base/RCTDisplayLink.m +++ b/React/Base/RCTDisplayLink.m @@ -18,6 +18,10 @@ #import "RCTModuleData.h" #import "RCTProfile.h" +#define RCTAssertRunLoop() \ + RCTAssert(_runLoop == [NSRunLoop currentRunLoop], \ + @"This method must be called on the CADisplayLink run loop") + @implementation RCTDisplayLink { CADisplayLink *_jsDisplayLink; @@ -38,12 +42,13 @@ - (instancetype)init - (void)registerModuleForFrameUpdates:(id)module withModuleData:(RCTModuleData *)moduleData { - if ([_frameUpdateObservers containsObject:moduleData] || - ![moduleData.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) { + if (![moduleData.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)] || + [_frameUpdateObservers containsObject:moduleData]) { return; } [_frameUpdateObservers addObject:moduleData]; + // Don't access the module instance via moduleData, as this will cause deadlock id observer = (id)module; __weak typeof(self) weakSelf = self; @@ -54,16 +59,28 @@ - (void)registerModuleForFrameUpdates:(id)module } CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop]; - - if (!self->_runLoop) { + if (!cfRunLoop) { return; } - CFRunLoopPerformBlock(cfRunLoop, kCFRunLoopDefaultMode, ^{ - [weakSelf updateJSDisplayLinkState]; - }); - CFRunLoopWakeUp(cfRunLoop); + if ([NSRunLoop currentRunLoop] == strongSelf->_runLoop) { + [weakSelf updateJSDisplayLinkState]; + } else { + CFRunLoopPerformBlock(cfRunLoop, kCFRunLoopDefaultMode, ^{ + [weakSelf updateJSDisplayLinkState]; + }); + CFRunLoopWakeUp(cfRunLoop); + } }; + + // Assuming we're paused right now, we only need to update the display link's state + // when the new observer is not paused. If it not paused, the observer will immediately + // start receiving updates anyway. + if (![observer isPaused] && _runLoop) { + CFRunLoopPerformBlock([_runLoop getCFRunLoop], kCFRunLoopDefaultMode, ^{ + [self updateJSDisplayLinkState]; + }); + } } - (void)addToRunLoop:(NSRunLoop *)runLoop @@ -77,12 +94,6 @@ - (void)invalidate [_jsDisplayLink invalidate]; } -- (void)assertOnRunLoop -{ - RCTAssert(_runLoop == [NSRunLoop currentRunLoop], - @"This method must be called on the CADisplayLink run loop"); -} - - (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue { @@ -95,7 +106,7 @@ - (void)dispatchBlock:(dispatch_block_t)block - (void)_jsThreadUpdate:(CADisplayLink *)displayLink { - [self assertOnRunLoop]; + RCTAssertRunLoop(); RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTDisplayLink _jsThreadUpdate:]", nil); @@ -121,7 +132,7 @@ - (void)_jsThreadUpdate:(CADisplayLink *)displayLink - (void)updateJSDisplayLinkState { - [self assertOnRunLoop]; + RCTAssertRunLoop(); BOOL pauseDisplayLink = YES; for (RCTModuleData *moduleData in _frameUpdateObservers) { @@ -131,6 +142,7 @@ - (void)updateJSDisplayLinkState break; } } + _jsDisplayLink.paused = pauseDisplayLink; } diff --git a/React/Base/RCTModuleMethod.m b/React/Base/RCTModuleMethod.m index 2d4360d89488d4..38c18c8081b7d5 100644 --- a/React/Base/RCTModuleMethod.m +++ b/React/Base/RCTModuleMethod.m @@ -426,7 +426,7 @@ - (SEL)selector - (NSDictionary *)profileArgs { - if (_profileArgs) { + if (!_profileArgs) { // This sets _selector [self processMethodSignature]; _profileArgs = @{ diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index dd10ca9ca80ee8..ac6c53c77b8a12 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -15,7 +15,9 @@ #import "RCTLog.h" #import "RCTUtils.h" -@interface RCTTimer : NSObject +static const NSTimeInterval kMinimumSleepInterval = 1; + +@interface _RCTTimer : NSObject @property (nonatomic, strong, readonly) NSDate *target; @property (nonatomic, assign, readonly) BOOL repeats; @@ -24,7 +26,7 @@ @interface RCTTimer : NSObject @end -@implementation RCTTimer +@implementation _RCTTimer - (instancetype)initWithCallbackID:(NSNumber *)callbackID interval:(NSTimeInterval)interval @@ -55,9 +57,36 @@ - (BOOL)updateFoundNeedsJSUpdate @end +@interface _RCTTimingProxy : NSObject + +@end + +// NSTimer retains its target, insert this class to break potential retain cycles +@implementation _RCTTimingProxy +{ + __weak id _target; +} + ++ (instancetype)proxyWithTarget:(id)target +{ + _RCTTimingProxy *proxy = [self new]; + if (proxy) { + proxy->_target = target; + } + return proxy; +} + +- (void)timerDidFire +{ + [_target timerDidFire]; +} + +@end + @implementation RCTTiming { - NSMutableDictionary *_timers; + NSMutableDictionary *_timers; + NSTimer *_sleepTimer; } @synthesize bridge = _bridge; @@ -95,6 +124,7 @@ - (void)setBridge:(RCTBridge *)bridge - (void)dealloc { + [_sleepTimer invalidate]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -111,7 +141,12 @@ - (void)invalidate - (void)stopTimers { - self.paused = YES; + if (!_paused) { + _paused = YES; + if (_pauseCallback) { + _pauseCallback(); + } + } } - (void)startTimers @@ -120,13 +155,8 @@ - (void)startTimers return; } - self.paused = NO; -} - -- (void)setPaused:(BOOL)paused -{ - if (_paused != paused) { - _paused = paused; + if (_paused) { + _paused = NO; if (_pauseCallback) { _pauseCallback(); } @@ -135,23 +165,65 @@ - (void)setPaused:(BOOL)paused - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update { + NSDate *nextScheduledTarget = [NSDate distantFuture]; NSMutableArray *timersToCall = [NSMutableArray new]; - for (RCTTimer *timer in _timers.allValues) { + for (_RCTTimer *timer in _timers.allValues) { + NSDate *target = timer.target; if ([timer updateFoundNeedsJSUpdate]) { [timersToCall addObject:timer.callbackID]; } if (!timer.target) { [_timers removeObjectForKey:timer.callbackID]; + } else { + nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target]; } } - // call timers that need to be called + // Call timers that need to be called if (timersToCall.count > 0) { [_bridge enqueueJSCall:@"JSTimersExecution.callTimers" args:@[timersToCall]]; + + // If we call at least one timer this frame, don't switch to a paused state yet, so if + // in response to this timer another timer is scheduled, we don't pause and unpause + // the displaylink frivolously. + return; } + // No need to call the pauseCallback as RCTDisplayLink will ask us about our paused + // status immediately after completing this call if (_timers.count == 0) { - [self stopTimers]; + _paused = YES; + } + // If the next timer is more than 1 second out, pause and schedule an NSTimer; + else if ([nextScheduledTarget timeIntervalSinceNow] > kMinimumSleepInterval) { + [self scheduleSleepTimer:nextScheduledTarget]; + _paused = YES; + } +} + +- (void)scheduleSleepTimer:(NSDate *)sleepTarget +{ + if (!_sleepTimer || !_sleepTimer.valid) { + _sleepTimer = [[NSTimer alloc] initWithFireDate:sleepTarget + interval:0 + target:[_RCTTimingProxy proxyWithTarget:self] + selector:@selector(timerDidFire) + userInfo:nil + repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:_sleepTimer forMode:NSDefaultRunLoopMode]; + } else { + _sleepTimer.fireDate = [_sleepTimer.fireDate earlierDate:sleepTarget]; + } +} + +- (void)timerDidFire +{ + _sleepTimer = nil; + if (_paused) { + [self startTimers]; + + // Immediately dispatch frame, so we don't have to wait on the displaylink. + [self didUpdateFrame:nil]; } } @@ -180,12 +252,18 @@ - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update jsDuration = 0; } - RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID - interval:jsDuration - targetTime:targetTime - repeats:repeats]; + _RCTTimer *timer = [[_RCTTimer alloc] initWithCallbackID:callbackID + interval:jsDuration + targetTime:targetTime + repeats:repeats]; _timers[callbackID] = timer; - [self startTimers]; + if (_paused) { + if ([timer.target timeIntervalSinceNow] > kMinimumSleepInterval) { + [self scheduleSleepTimer:timer.target]; + } else { + [self startTimers]; + } + } } RCT_EXPORT_METHOD(deleteTimer:(nonnull NSNumber *)timerID) From 6d3c7b8a4c1d1a0552a21cfabe03a8d1f344d1ed Mon Sep 17 00:00:00 2001 From: Andy Street Date: Mon, 11 Jul 2016 09:56:00 -0700 Subject: [PATCH 066/241] Fix 'Unexpected EOF' in old bridge Summary: This is caused by receiving \u2028/2029 in callbacks/function calls. The correct solution is to not evaluate these strings as scripts but instead parse them as json and pass them through the JSC API. Reviewed By: lexs Differential Revision: D3543098 fbshipit-source-id: 4d8acce1d510bb17361d32103d4738fc0208b0a8 --- .../src/main/jni/react/JSCExecutor.cpp | 57 +++++++++---------- ReactAndroid/src/main/jni/react/JSCExecutor.h | 2 + 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp index a26a79742500e2..c76c24ed18d4b6 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp @@ -68,25 +68,6 @@ static JSValueRef nativeInjectHMRUpdate( const JSValueRef arguments[], JSValueRef *exception); -static std::string executeJSCallWithJSC( - JSGlobalContextRef ctx, - const std::string& methodName, - const std::vector& arguments) { - #ifdef WITH_FBSYSTRACE - FbSystraceSection s( - TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.executeJSCall", - "method", methodName); - #endif - - // Evaluate script with JSC - folly::dynamic jsonArgs(arguments.begin(), arguments.end()); - auto js = folly::to( - "__fbBatchedBridge.", methodName, ".apply(null, ", - folly::toJson(jsonArgs), ")"); - auto result = evaluateScript(ctx, String(js.c_str()), nullptr); - return Value(ctx, result).toJSONString(); -} - std::unique_ptr JSCExecutorFactory::createJSExecutor(Bridge *bridge) { return std::unique_ptr(new JSCExecutor(bridge, cacheDir_, m_jscConfig)); } @@ -202,6 +183,8 @@ void JSCExecutor::terminateOnJSVMThread() { m_batchedBridge.reset(); m_flushedQueueObj.reset(); + m_callFunctionObj.reset(); + m_invokeCallbackObj.reset(); s_globalContextRefToJSCExecutor.erase(m_context); JSGlobalContextRelease(m_context); @@ -266,6 +249,8 @@ bool JSCExecutor::ensureBatchedBridgeObject() { } m_batchedBridge = folly::make_unique(batchedBridgeValue.asObject()); m_flushedQueueObj = folly::make_unique(m_batchedBridge->getProperty("flushedQueue").asObject()); + m_callFunctionObj = folly::make_unique(m_batchedBridge->getProperty("callFunctionReturnFlushedQueue").asObject()); + m_invokeCallbackObj = folly::make_unique(m_batchedBridge->getProperty("invokeCallbackAndReturnFlushedQueue").asObject()); return true; } @@ -290,6 +275,10 @@ void JSCExecutor::flush() { } void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) { +#ifdef WITH_FBSYSTRACE + FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.callFunction"); +#endif + if (!ensureBatchedBridgeObject()) { throwJSExecutionException( "Couldn't call JS module %s, method %s: bridge configuration isn't available. This " @@ -298,27 +287,35 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m methodId.c_str()); } - std::vector call { - moduleId, - methodId, - std::move(arguments), + String argsString = String(folly::toJson(std::move(arguments)).c_str()); + String moduleIdStr(moduleId.c_str()); + String methodIdStr(methodId.c_str()); + JSValueRef args[] = { + JSValueMakeString(m_context, moduleIdStr), + JSValueMakeString(m_context, methodIdStr), + Value::fromJSON(m_context, argsString) }; - std::string calls = executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call)); - m_bridge->callNativeModules(*this, calls, true); + auto result = m_callFunctionObj->callAsFunction(3, args); + m_bridge->callNativeModules(*this, result.toJSONString(), true); } void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) { +#ifdef WITH_FBSYSTRACE + FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.invokeCallback"); +#endif + if (!ensureBatchedBridgeObject()) { throwJSExecutionException( "Couldn't invoke JS callback %d: bridge configuration isn't available. This shouldn't be possible. Congratulations.", (int) callbackId); } - std::vector call { - (double) callbackId, - std::move(arguments) + String argsString = String(folly::toJson(std::move(arguments)).c_str()); + JSValueRef args[] = { + JSValueMakeNumber(m_context, callbackId), + Value::fromJSON(m_context, argsString) }; - std::string calls = executeJSCallWithJSC(m_context, "invokeCallbackAndReturnFlushedQueue", std::move(call)); - m_bridge->callNativeModules(*this, calls, true); + auto result = m_invokeCallbackObj->callAsFunction(2, args); + m_bridge->callNativeModules(*this, result.toJSONString(), true); } void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) { diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.h b/ReactAndroid/src/main/jni/react/JSCExecutor.h index acd6d14cc386f8..5faca08ba817d8 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.h +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.h @@ -91,6 +91,8 @@ class JSCExecutor : public JSExecutor { folly::dynamic m_jscConfig; std::unique_ptr m_batchedBridge; std::unique_ptr m_flushedQueueObj; + std::unique_ptr m_callFunctionObj; + std::unique_ptr m_invokeCallbackObj; /** * WebWorker constructor. Must be invoked from thread this Executor will run on. From ed4db631faf0091a92179e9fd629ce06e4704d1e Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Mon, 11 Jul 2016 10:28:03 -0700 Subject: [PATCH 067/241] RN: Change Time Drift Error into Warning Summary: Changes the time drift error into a warning that will only get logged once per debugging session. Reviewed By: jingc Differential Revision: D3539067 fbshipit-source-id: 357db15750d867a91c39b5fc5fd6ed4ae2852bc7 --- .../System/JSTimers/JSTimersExecution.js | 13 +++++++++++++ .../react/modules/core/JSTimersExecution.java | 2 ++ .../com/facebook/react/modules/core/Timing.java | 9 ++++----- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js b/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js index df1b609723e6dd..b118a28a6c48b3 100644 --- a/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js +++ b/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js @@ -16,6 +16,8 @@ var performanceNow = require('fbjs/lib/performanceNow'); var warning = require('fbjs/lib/warning'); var Systrace = require('Systrace'); +let hasEmittedTimeDriftWarning = false; + /** * JS implementation of timer functions. Must be completely driven by an * external clock signal, all that's stored here is timerID, timer type, and @@ -147,6 +149,17 @@ var JSTimersExecution = { } }, + /** + * Called from native (in development) when environment times are out-of-sync. + */ + emitTimeDriftWarning: function(warningMessage) { + if (hasEmittedTimeDriftWarning) { + return; + } + hasEmittedTimeDriftWarning = true; + console.warn(warningMessage); + }, + _clearIndex: function(i) { JSTimersExecution.timerIDs[i] = null; JSTimersExecution.callbacks[i] = null; diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java index 9e0ba2a3d72893..41b241fad07622 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java @@ -17,4 +17,6 @@ public interface JSTimersExecution extends JavaScriptModule { public void callTimers(WritableArray timerIDs); + + public void emitTimeDriftWarning(String warningMessage); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java index d0ee51cfcd191f..8b88c05b464199 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java @@ -226,11 +226,10 @@ public void createTimer( if (mDevSupportManager.getDevSupportEnabled()) { long driftTime = Math.abs(remoteTime - deviceTime); if (driftTime > 60000) { - throw new RuntimeException( - "Debugger and device times have drifted by more than 60s." + - "Please correct this by running adb shell " + - "\"date `date +%m%d%H%M%Y.%S`\" on your debugger machine." - ); + getReactApplicationContext().getJSModule(executorToken, JSTimersExecution.class) + .emitTimeDriftWarning( + "Debugger and device times have drifted by more than 60s. Please correct this by " + + "running adb shell \"date `date +%m%d%H%M%Y.%S`\" on your debugger machine."); } } From 1db781316bac866d6cd579e2bbfd52d846ff0f60 Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Mon, 11 Jul 2016 11:23:46 -0700 Subject: [PATCH 068/241] RN: Clean JSTimersExecution JS Reviewed By: sahrens Differential Revision: D3539098 fbshipit-source-id: f17a35c5d6a45627bd6961b6b06266fe254d3976 --- .../System/JSTimers/JSTimersExecution.js | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js b/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js index b118a28a6c48b3..a4938ed400a2dd 100644 --- a/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js +++ b/Libraries/JavaScriptAppEngine/System/JSTimers/JSTimersExecution.js @@ -10,11 +10,12 @@ */ 'use strict'; -var invariant = require('fbjs/lib/invariant'); -var keyMirror = require('fbjs/lib/keyMirror'); -var performanceNow = require('fbjs/lib/performanceNow'); -var warning = require('fbjs/lib/warning'); -var Systrace = require('Systrace'); +const Systrace = require('Systrace'); + +const invariant = require('fbjs/lib/invariant'); +const keyMirror = require('fbjs/lib/keyMirror'); +const performanceNow = require('fbjs/lib/performanceNow'); +const warning = require('fbjs/lib/warning'); let hasEmittedTimeDriftWarning = false; @@ -23,7 +24,7 @@ let hasEmittedTimeDriftWarning = false; * external clock signal, all that's stored here is timerID, timer type, and * callback. */ -var JSTimersExecution = { +const JSTimersExecution = { GUID: 1, Type: keyMirror({ setTimeout: null, @@ -43,9 +44,13 @@ var JSTimersExecution = { * if it was a one time timer (setTimeout), and not unregister it if it was * recurring (setInterval). */ - callTimer: function(timerID) { - warning(timerID <= JSTimersExecution.GUID, 'Tried to call timer with ID ' + timerID + ' but no such timer exists'); - var timerIndex = JSTimersExecution.timerIDs.indexOf(timerID); + callTimer(timerID) { + warning( + timerID <= JSTimersExecution.GUID, + 'Tried to call timer with ID %s but no such timer exists.', + timerID + ); + const timerIndex = JSTimersExecution.timerIDs.indexOf(timerID); // timerIndex of -1 means that no timer with that ID exists. There are // two situations when this happens, when a garbage timer ID was given // and when a previously existing timer was deleted before this callback @@ -54,8 +59,8 @@ var JSTimersExecution = { if (timerIndex === -1) { return; } - var type = JSTimersExecution.types[timerIndex]; - var callback = JSTimersExecution.callbacks[timerIndex]; + const type = JSTimersExecution.types[timerIndex]; + const callback = JSTimersExecution.callbacks[timerIndex]; // Clear the metadata if (type === JSTimersExecution.Type.setTimeout || @@ -70,7 +75,7 @@ var JSTimersExecution = { type === JSTimersExecution.Type.setImmediate) { callback(); } else if (type === JSTimersExecution.Type.requestAnimationFrame) { - var currentTime = performanceNow(); + const currentTime = performanceNow(); callback(currentTime); } else { console.error('Tried to call a callback with invalid type: ' + type); @@ -87,19 +92,22 @@ var JSTimersExecution = { * This is called from the native side. We are passed an array of timerIDs, * and */ - callTimers: function(timerIDs) { - invariant(timerIDs.length !== 0, 'Probably shouldn\'t call "callTimers" with no timerIDs'); + callTimers(timerIDs) { + invariant( + timerIDs.length !== 0, + 'Cannot call `callTimers` with an empty list of IDs.' + ); JSTimersExecution.errors = null; timerIDs.forEach(JSTimersExecution.callTimer); - var errors = JSTimersExecution.errors; + const errors = JSTimersExecution.errors; if (errors) { - var errorCount = errors.length; + const errorCount = errors.length; if (errorCount > 1) { // Throw all the other errors in a setTimeout, which will throw each // error one at a time - for (var ii = 1; ii < errorCount; ii++) { + for (let ii = 1; ii < errorCount; ii++) { require('JSTimers').setTimeout( ((error) => { throw error; }).bind(null, errors[ii]), 0 @@ -114,18 +122,18 @@ var JSTimersExecution = { * Performs a single pass over the enqueued immediates. Returns whether * more immediates are queued up (can be used as a condition a while loop). */ - callImmediatesPass: function() { + callImmediatesPass() { Systrace.beginEvent('JSTimersExecution.callImmediatesPass()'); // The main reason to extract a single pass is so that we can track // in the system trace if (JSTimersExecution.immediates.length > 0) { - var passImmediates = JSTimersExecution.immediates.slice(); + const passImmediates = JSTimersExecution.immediates.slice(); JSTimersExecution.immediates = []; // Use for loop rather than forEach as per @vjeux's advice // https://github.com/facebook/react-native/commit/c8fd9f7588ad02d2293cac7224715f4af7b0f352#commitcomment-14570051 - for (var i = 0; i < passImmediates.length; ++i) { + for (let i = 0; i < passImmediates.length; ++i) { JSTimersExecution.callTimer(passImmediates[i]); } } @@ -139,7 +147,7 @@ var JSTimersExecution = { * This is called after we execute any command we receive from native but * before we hand control back to native. */ - callImmediates: function() { + callImmediates() { JSTimersExecution.errors = null; while (JSTimersExecution.callImmediatesPass()) {} if (JSTimersExecution.errors) { @@ -152,7 +160,7 @@ var JSTimersExecution = { /** * Called from native (in development) when environment times are out-of-sync. */ - emitTimeDriftWarning: function(warningMessage) { + emitTimeDriftWarning(warningMessage) { if (hasEmittedTimeDriftWarning) { return; } @@ -160,7 +168,7 @@ var JSTimersExecution = { console.warn(warningMessage); }, - _clearIndex: function(i) { + _clearIndex(i) { JSTimersExecution.timerIDs[i] = null; JSTimersExecution.callbacks[i] = null; JSTimersExecution.types[i] = null; From b33424e5ed09f15cbfa0198a01ad372c1989f6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Mon, 11 Jul 2016 12:09:15 -0700 Subject: [PATCH 069/241] Fix links from networking to navigation in The Basics. Summary: Fixes #8611. Once this lands, we may want to cherry-pick it into 0.29 to fix the broken links. Closes https://github.com/facebook/react-native/pull/8698 Differential Revision: D3544388 Pulled By: JoelMarcey fbshipit-source-id: d5132b112e3079d1fd9ab6d84ff1a4328bee871f --- docs/Networking.md | 2 +- docs/{Navigators.md => UsingNavigators.md} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename docs/{Navigators.md => UsingNavigators.md} (99%) diff --git a/docs/Networking.md b/docs/Networking.md index 23a82897788f4e..baffa2c448cad6 100644 --- a/docs/Networking.md +++ b/docs/Networking.md @@ -4,7 +4,7 @@ title: Networking layout: docs category: The Basics permalink: docs/network.html -next: navigators +next: using-navigators --- Many mobile apps need to load resources from a remote URL. You may want to make a POST request to a REST API, or you may simply need to fetch a chunk of static content from another server. diff --git a/docs/Navigators.md b/docs/UsingNavigators.md similarity index 99% rename from docs/Navigators.md rename to docs/UsingNavigators.md index eb389f87d80f0a..ad8da54cc3f5db 100644 --- a/docs/Navigators.md +++ b/docs/UsingNavigators.md @@ -1,5 +1,5 @@ --- -id: navigators +id: using-navigators title: Using Navigators layout: docs category: The Basics From 8fb6111fa54db407199f6b86615f1fae65c0c8e4 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Mon, 11 Jul 2016 13:14:17 -0700 Subject: [PATCH 070/241] Make loadRAMBundle a C function, not a method Summary: This makes the state it uses more explicit. It also makes a bug with performance measurement obvious: if we early return to error, we never mark stop for `RCTPLRAMStartupCodeSize`. Reviewed By: javache Differential Revision: D3542751 fbshipit-source-id: e6c1e3f3a76098ca37b8078f6e9abc805ad2d9da --- React/Executors/RCTJSCExecutor.mm | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 9e6579f2d370ae..b3fe9d897d5a82 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -666,8 +666,12 @@ - (void)executeApplicationScript:(NSData *)script uint32_t magicNumber = NSSwapLittleIntToHost(*((uint32_t *)script.bytes)); BOOL isRAMBundle = magicNumber == RCTRAMBundleMagicNumber; if (isRAMBundle) { + [_performanceLogger markStartForTag:RCTPLRAMBundleLoad]; NSError *error; - script = [self loadRAMBundle:sourceURL error:&error]; + script = loadRAMBundle(sourceURL, &error, _randomAccessBundle); + [self registerNativeRequire]; + [_performanceLogger markStopForTag:RCTPLRAMBundleLoad]; + [_performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize]; if (error) { if (onComplete) { @@ -892,9 +896,8 @@ static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccess return {std::move(code), startupCodeSize}; } -- (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error +static NSData *loadRAMBundle(NSURL *sourceURL, NSError **error, RandomAccessBundleData &randomAccessBundle) { - [_performanceLogger markStartForTag:RCTPLRAMBundleLoad]; file_ptr bundle(fopen(sourceURL.path.UTF8String, "r"), fclose); if (!bundle) { if (error) { @@ -903,10 +906,7 @@ - (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error return nil; } - [self registerNativeRequire]; - - - auto startupCode = readRAMBundle(std::move(bundle), _randomAccessBundle); + auto startupCode = readRAMBundle(std::move(bundle), randomAccessBundle); if (startupCode.isEmpty()) { if (error) { *error = RCTErrorWithMessage(@"Error loading RAM Bundle"); @@ -914,8 +914,6 @@ - (NSData *)loadRAMBundle:(NSURL *)sourceURL error:(NSError **)error return nil; } - [_performanceLogger markStopForTag:RCTPLRAMBundleLoad]; - [_performanceLogger setValue:startupCode.size forTag:RCTPLRAMStartupCodeSize]; return [NSData dataWithBytesNoCopy:startupCode.code.release() length:startupCode.size freeWhenDone:YES]; } From 5e89baa7a0e3148e021b9129b8116b8aa2dca814 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Mon, 11 Jul 2016 13:14:21 -0700 Subject: [PATCH 071/241] Remove needless weakSelf from executeApplicationScript Reviewed By: javache Differential Revision: D3542798 fbshipit-source-id: bf3fe15c78b55691176aecf209210a6f35f98862 --- React/Executors/RCTJSCExecutor.mm | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index b3fe9d897d5a82..3c2a958459c57a 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -690,25 +690,23 @@ - (void)executeApplicationScript:(NSData *)script script = nullTerminatedScript; } - __weak RCTJSCExecutor *weakSelf = self; [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf || !strongSelf.isValid) { + if (!self.isValid) { return; } - [strongSelf->_performanceLogger markStartForTag:RCTPLScriptExecution]; + [self->_performanceLogger markStartForTag:RCTPLScriptExecution]; JSValueRef jsError = NULL; - RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper; + RCTJSCWrapper *jscWrapper = self->_jscWrapper; JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes); JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); - JSGlobalContextRef ctx = strongSelf->_context.context.JSGlobalContextRef; + JSGlobalContextRef ctx = self->_context.context.JSGlobalContextRef; JSValueRef result = jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError); jscWrapper->JSStringRelease(bundleURL); jscWrapper->JSStringRelease(execJSString); - [strongSelf->_performanceLogger markStopForTag:RCTPLScriptExecution]; + [self->_performanceLogger markStopForTag:RCTPLScriptExecution]; if (onComplete) { NSError *error; From af7104b49ed8363598a016bd3e4ac52baf32025f Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Mon, 11 Jul 2016 13:14:23 -0700 Subject: [PATCH 072/241] Extract part of executeApplicationScript into a C function Reviewed By: javache Differential Revision: D3542897 fbshipit-source-id: aa74c0c7c4477158fc9c8aff69432aed592f472f --- React/Executors/RCTJSCExecutor.mm | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 3c2a958459c57a..57512557d2bfc2 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -694,30 +694,26 @@ - (void)executeApplicationScript:(NSData *)script if (!self.isValid) { return; } - [self->_performanceLogger markStartForTag:RCTPLScriptExecution]; - - JSValueRef jsError = NULL; - RCTJSCWrapper *jscWrapper = self->_jscWrapper; - JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes); - JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); - JSGlobalContextRef ctx = self->_context.context.JSGlobalContextRef; - JSValueRef result = jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError); - jscWrapper->JSStringRelease(bundleURL); - jscWrapper->JSStringRelease(execJSString); - + NSError *error = executeApplicationScript(self->_jscWrapper, script, sourceURL, self->_context.context.JSGlobalContextRef); [self->_performanceLogger markStopForTag:RCTPLScriptExecution]; - if (onComplete) { - NSError *error; - if (!result) { - error = RCTNSErrorFromJSError(jscWrapper, ctx, jsError); - } onComplete(error); } }), 0, @"js_call", (@{ @"url": sourceURL.absoluteString }))]; } +static NSError *executeApplicationScript(RCTJSCWrapper *jscWrapper, NSData *script, NSURL *sourceURL, JSGlobalContextRef ctx) +{ + JSValueRef jsError = NULL; + JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes); + JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); + JSValueRef result = jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError); + jscWrapper->JSStringRelease(bundleURL); + jscWrapper->JSStringRelease(execJSString); + return result ? nil : RCTNSErrorFromJSError(jscWrapper, ctx, jsError); +} + - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block { if ([NSThread currentThread] != _javaScriptThread) { From 2ae23d9f36ca7140448fc9cafda3fef281cf5b3c Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Jul 2016 13:15:59 -0700 Subject: [PATCH 073/241] Flush pending calls off the main-thread Reviewed By: majak Differential Revision: D3535193 fbshipit-source-id: 8c4736629eab3c723641f0c3fb449c168cd492a1 --- React/Base/RCTBatchedBridge.m | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index bad2a4de3caca9..1f80fa390390e1 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -187,11 +187,13 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad } else { // Allow testing without a script dispatch_async(dispatch_get_main_queue(), ^{ - [self didFinishLoading]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:self->_parentBridge userInfo:@{@"bridge": self}]; }); + + [self flushPendingCalls]; + onSourceLoad(nil, nil, 0); } } @@ -477,14 +479,15 @@ - (void)executeSourceCode:(NSData *)sourceCode NSRunLoop *targetRunLoop = [self->_javaScriptExecutor isKindOfClass:[RCTJSCExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop]; [self->_displayLink addToRunLoop:targetRunLoop]; - // Perform the state update and notification on the main thread, so we can't run into + // Perform the notification on the main thread, so we can't run into // timing issues with RCTRootView dispatch_async(dispatch_get_main_queue(), ^{ - [self didFinishLoading]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:self->_parentBridge userInfo:@{@"bridge": self}]; }); + + [self flushPendingCalls]; }]; #if RCT_DEV @@ -495,17 +498,18 @@ - (void)executeSourceCode:(NSData *)sourceCode NSNumber *port = self.bundleURL.port; [self enqueueJSCall:@"HMRClient.enable" args:@[@"ios", path, host, RCTNullIfNil(port)]]; } - #endif - } -- (void)didFinishLoading +- (void)flushPendingCalls { [_performanceLogger markStopForTag:RCTPLBridgeStartup]; - _loading = NO; + [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ for (dispatch_block_t call in self->_pendingCalls) { + _loading = NO; + + for (dispatch_block_t call in _pendingCalls) { call(); } }]; From 81f59dfdc249c2192df635530d752cb819760646 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Jul 2016 13:16:09 -0700 Subject: [PATCH 074/241] Remove code for testing without JS bundle Reviewed By: mmmulani Differential Revision: D3542347 fbshipit-source-id: d0dcfb65645faf515e6e48e6ff733646e1bcf907 --- Examples/UIExplorer/TestBundle.js | 9 +++++ .../UIExplorer.xcodeproj/project.pbxproj | 32 +++--------------- .../UIExplorerUnitTests/RCTBridgeTests.m | 8 ++++- .../RCTComponentPropsTests.m | 3 +- .../UIExplorerUnitTests/RCTImageLoaderTests.m | 18 +++++++--- .../RCTModuleInitNotificationRaceTests.m | 3 +- .../UIExplorerUnitTests/RCTModuleInitTests.m | 3 +- React/Base/RCTBatchedBridge.m | 33 ++++++------------- 8 files changed, 49 insertions(+), 60 deletions(-) create mode 100644 Examples/UIExplorer/TestBundle.js diff --git a/Examples/UIExplorer/TestBundle.js b/Examples/UIExplorer/TestBundle.js new file mode 100644 index 00000000000000..5919f0f4f0406e --- /dev/null +++ b/Examples/UIExplorer/TestBundle.js @@ -0,0 +1,9 @@ +/** + * 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. + */ + diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index f73a4214d02fc8..a23d3d532e4c12 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */; }; 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; 3DB99D0C1BA0340600302749 /* UIExplorerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */; }; + 3DEFCE371D33A67200256E76 /* TestBundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 3DEFCE351D33A61500256E76 /* TestBundle.js */; }; 68FF44381CF6111500720EFD /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; @@ -220,12 +221,6 @@ 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 = ""; }; 143BC57E1B21E18100462512 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 143BC5811B21E18100462512 /* testLayoutExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testLayoutExampleSnapshot_1@2x.png"; sourceTree = ""; }; - 143BC5821B21E18100462512 /* testSliderExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testSliderExampleSnapshot_1@2x.png"; sourceTree = ""; }; - 143BC5831B21E18100462512 /* testSwitchExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testSwitchExampleSnapshot_1@2x.png"; sourceTree = ""; }; - 143BC5841B21E18100462512 /* testTabBarExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testTabBarExampleSnapshot_1@2x.png"; sourceTree = ""; }; - 143BC5851B21E18100462512 /* testTextExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testTextExampleSnapshot_1@2x.png"; sourceTree = ""; }; - 143BC5861B21E18100462512 /* testViewExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testViewExampleSnapshot_1@2x.png"; sourceTree = ""; }; 143BC5951B21E3E100462512 /* UIExplorerIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 143BC5981B21E3E100462512 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerSnapshotTests.m; sourceTree = ""; }; @@ -257,6 +252,7 @@ 27F441EA1BEBE5030039B79C /* FlexibleSizeExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexibleSizeExampleView.h; path = UIExplorer/NativeExampleViews/FlexibleSizeExampleView.h; sourceTree = ""; }; 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerIntegrationTests.m; sourceTree = ""; }; + 3DEFCE351D33A61500256E76 /* TestBundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = TestBundle.js; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBundleURLProviderTests.m; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; @@ -344,6 +340,7 @@ 1323F18D1C04ABAC0091BED0 /* Supporting Files */ = { isa = PBXGroup; children = ( + 3DEFCE351D33A61500256E76 /* TestBundle.js */, 13B07FB61A68108700A75B9A /* Info.plist */, 1323F1851C04AB9F0091BED0 /* bunny.png */, 1323F1861C04AB9F0091BED0 /* flux@3x.png */, @@ -459,32 +456,10 @@ 143BC57E1B21E18100462512 /* Info.plist */, 14D6D7101B220EB3001FB087 /* libOCMock.a */, 14D6D7011B220AE3001FB087 /* OCMock */, - 143BC57F1B21E18100462512 /* ReferenceImages */, ); path = UIExplorerUnitTests; sourceTree = ""; }; - 143BC57F1B21E18100462512 /* ReferenceImages */ = { - isa = PBXGroup; - children = ( - 143BC5801B21E18100462512 /* Examples-UIExplorer-UIExplorerApp */, - ); - path = ReferenceImages; - sourceTree = ""; - }; - 143BC5801B21E18100462512 /* Examples-UIExplorer-UIExplorerApp */ = { - isa = PBXGroup; - children = ( - 143BC5811B21E18100462512 /* testLayoutExampleSnapshot_1@2x.png */, - 143BC5821B21E18100462512 /* testSliderExampleSnapshot_1@2x.png */, - 143BC5831B21E18100462512 /* testSwitchExampleSnapshot_1@2x.png */, - 143BC5841B21E18100462512 /* testTabBarExampleSnapshot_1@2x.png */, - 143BC5851B21E18100462512 /* testTextExampleSnapshot_1@2x.png */, - 143BC5861B21E18100462512 /* testViewExampleSnapshot_1@2x.png */, - ); - path = "Examples-UIExplorer-UIExplorerApp"; - sourceTree = ""; - }; 143BC5961B21E3E100462512 /* UIExplorerIntegrationTests */ = { isa = PBXGroup; children = ( @@ -887,6 +862,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3DEFCE371D33A67200256E76 /* TestBundle.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m index 15744669bc779a..8a6ae4212b54c3 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m @@ -92,6 +92,11 @@ - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block block(); } +- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block +{ + block(); +} + - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete @@ -151,7 +156,8 @@ - (void)setUp [super setUp]; _unregisteredTestModule = [UnregisteredTestModule new]; - _bridge = [[RCTBridge alloc] initWithBundleURL:nil + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + _bridge = [[RCTBridge alloc] initWithBundleURL:[bundle URLForResource:@"TestBundle" withExtension:@"js"] moduleProvider:^{ return @[self, self->_unregisteredTestModule]; } launchOptions:nil]; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTComponentPropsTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTComponentPropsTests.m index 75a32bdac70ba2..248a424556c6c2 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTComponentPropsTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTComponentPropsTests.m @@ -95,7 +95,8 @@ - (void)setUp { [super setUp]; - _bridge = [[RCTBridge alloc] initWithBundleURL:nil + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + _bridge = [[RCTBridge alloc] initWithBundleURL:[bundle URLForResource:@"TestBundle" withExtension:@"js"] moduleProvider:nil launchOptions:nil]; } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m index 8de60a1992cc0c..e403daf547eb2d 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m @@ -33,7 +33,15 @@ @interface RCTImageLoaderTests : XCTestCase @end -@implementation RCTImageLoaderTests +@implementation RCTImageLoaderTests { + NSURL *_bundleURL; +} + +- (void)setUp +{ + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + _bundleURL = [bundle URLForResource:@"TestBundle" withExtension:@"js"]; +} - (void)testImageLoading { @@ -47,7 +55,7 @@ - (void)testImageLoading return nil; }]; - NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader]; } launchOptions:nil]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[loader]; } launchOptions:nil]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://facebook.github.io/react/img/logo_og.png"]]; [bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { @@ -78,7 +86,7 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority return nil; }]; - NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2]; } launchOptions:nil]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[loader1, loader2]; } launchOptions:nil]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://facebook.github.io/react/img/logo_og.png"]]; [bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { @@ -103,7 +111,7 @@ - (void)testImageDecoding return nil; }]; - NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder]; } launchOptions:nil]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[decoder]; } launchOptions:nil]; RCTImageLoaderCancellationBlock cancelBlock = [bridge.imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 clipped:NO resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) { XCTAssertEqualObjects(decodedImage, image); @@ -132,7 +140,7 @@ - (void)testImageLoaderUsesImageDecoderWithHighestPriority return nil; }]; - NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2]; } launchOptions:nil]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[decoder1, decoder2]; } launchOptions:nil]; RCTImageLoaderCancellationBlock cancelBlock = [bridge.imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 clipped:NO resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) { XCTAssertEqualObjects(decodedImage, image); diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m index 2e9220d2d6b442..82542de0656da0 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m @@ -99,7 +99,8 @@ @implementation RCTModuleInitNotificationRaceTests - (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge { - return nil; + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + return [bundle URLForResource:@"TestBundle" withExtension:@"js"]; } - (NSArray *)extraModulesForBridge:(__unused RCTBridge *)bridge diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m index 0de479392420ff..215a5a202578fd 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m @@ -147,7 +147,8 @@ @implementation RCTModuleInitTests - (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge { - return nil; + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + return [bundle URLForResource:@"TestBundle" withExtension:@"js"]; } - (NSArray *)extraModulesForBridge:(__unused RCTBridge *)bridge diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 1f80fa390390e1..2e589b6e3e097f 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -171,7 +171,9 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) { [self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad]; - } else if (self.bundleURL) { + } else { + RCTAssert(self.bundleURL, @"bundleURL must be non-nil when not implementing loadSourceForBridge"); + [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:^(NSError *error, NSData *source, int64_t sourceLength) { if (error && [self.delegate respondsToSelector:@selector(fallbackSourceURLForBridge:)]) { NSURL *fallbackURL = [self.delegate fallbackSourceURLForBridge:self->_parentBridge]; @@ -184,17 +186,6 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad } onSourceLoad(error, source, sourceLength); }]; - } else { - // Allow testing without a script - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] - postNotificationName:RCTJavaScriptDidLoadNotification - object:self->_parentBridge userInfo:@{@"bridge": self}]; - }); - - [self flushPendingCalls]; - - onSourceLoad(nil, nil, 0); } } @@ -487,11 +478,10 @@ - (void)executeSourceCode:(NSData *)sourceCode object:self->_parentBridge userInfo:@{@"bridge": self}]; }); - [self flushPendingCalls]; + [self _flushPendingCalls]; }]; #if RCT_DEV - if ([RCTGetURLQueryParam(self.bundleURL, @"hot") boolValue]) { NSString *path = [self.bundleURL.path substringFromIndex:1]; // strip initial slash NSString *host = self.bundleURL.host; @@ -501,18 +491,15 @@ - (void)executeSourceCode:(NSData *)sourceCode #endif } -- (void)flushPendingCalls +- (void)_flushPendingCalls { + RCTAssertJSThread(); [_performanceLogger markStopForTag:RCTPLBridgeStartup]; - [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ - for (dispatch_block_t call in self->_pendingCalls) { - _loading = NO; - - for (dispatch_block_t call in _pendingCalls) { - call(); - } - }]; + _loading = NO; + for (dispatch_block_t call in self->_pendingCalls) { + call(); + } } - (void)stopLoadingWithError:(NSError *)error From 3816ced49bca2ab30971a85a01cd2adee465de37 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Jul 2016 13:16:10 -0700 Subject: [PATCH 075/241] nil out pendingCalls when we're done with them Reviewed By: mmmulani Differential Revision: D3543825 fbshipit-source-id: cf2fcdb4e1536c00675fafa85d0f880313a80655 --- React/Base/RCTBatchedBridge.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 2e589b6e3e097f..527c882ecb46b6 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -496,10 +496,14 @@ - (void)_flushPendingCalls RCTAssertJSThread(); [_performanceLogger markStopForTag:RCTPLBridgeStartup]; + RCT_PROFILE_BEGIN_EVENT(0, @"Processing pendingCalls", @{ @"count": @(_pendingCalls.count) }); _loading = NO; - for (dispatch_block_t call in self->_pendingCalls) { + NSArray *pendingCalls = _pendingCalls; + _pendingCalls = nil; + for (dispatch_block_t call in pendingCalls) { call(); } + RCT_PROFILE_END_EVENT(0, @"", nil); } - (void)stopLoadingWithError:(NSError *)error From ff3ab32a72c189e66a6e65a89cb2d9e8cf7ecaa3 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Jul 2016 13:23:40 -0700 Subject: [PATCH 076/241] Cleanup RCTImageLoader weakSelf/strongSelf Reviewed By: mmmulani Differential Revision: D3542393 fbshipit-source-id: b241586b0da254f688d0e8bdbf7d4ce72dc0d21f --- Libraries/Image/RCTImageLoader.h | 4 +- Libraries/Image/RCTImageLoader.m | 245 ++++++++++++++++--------------- 2 files changed, 130 insertions(+), 119 deletions(-) diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index 1daafdfb88877c..b54d1a0fcdb97a 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -13,11 +13,9 @@ #import "RCTURLRequestHandler.h" #import "RCTResizeMode.h" -@class ALAssetsLibrary; - typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total); typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, UIImage *image); -typedef void (^RCTImageLoaderCancellationBlock)(void); +typedef dispatch_block_t RCTImageLoaderCancellationBlock; @interface UIImage (React) diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index 028157903d8179..5ad25e0c1896f7 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -293,12 +293,11 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * completionBlock:(void (^)(NSError *error, id imageOrData))completionBlock { __block volatile uint32_t cancelled = 0; - __block void(^cancelLoad)(void) = nil; + __block dispatch_block_t cancelLoad = nil; __weak RCTImageLoader *weakSelf = self; void (^completionHandler)(NSError *error, id imageOrData) = ^(NSError *error, id imageOrData) { if (RCTIsMainQueue()) { - // Most loaders do not return on the main thread, so caller is probably not // expecting it, and may do expensive post-processing in the callback dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @@ -315,21 +314,24 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * if (!_URLCacheQueue) { [self setUp]; } - dispatch_async(_URLCacheQueue, ^{ - - if (!self->_URLCache) { - self->_URLCache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB - diskCapacity:200 * 1024 * 1024 // 200MB - diskPath:@"React/RCTImageDownloader"]; - } - RCTImageLoader *strongSelf = weakSelf; + dispatch_async(_URLCacheQueue, ^{ + __typeof(self) strongSelf = weakSelf; if (cancelled || !strongSelf) { return; } + // Use a local variable so we can reassign it in this block + NSURLRequest *request = imageURLRequest; + + // Add missing png extension + if (request.URL.fileURL && request.URL.pathExtension.length == 0) { + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]]; + request = mutableRequest; + } + // Find suitable image URL loader - NSURLRequest *request = imageURLRequest; // Use a local variable so we can reassign it in this block id loadHandler = [strongSelf imageURLLoaderForURL:request.URL]; if (loadHandler) { cancelLoad = [loadHandler loadImageForURL:request.URL @@ -338,133 +340,144 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest * resizeMode:resizeMode progressHandler:progressHandler completionHandler:completionHandler] ?: ^{}; - return; + } else { + // Use networking module to load image + cancelLoad = [strongSelf _loadURLRequest:request + progressBlock:progressHandler + completionBlock:completionHandler]; } + }); - // Check if networking module is available - if (RCT_DEBUG && ![self->_bridge respondsToSelector:@selector(networking)]) { - RCTLogError(@"No suitable image URL loader found for %@. You may need to " - " import the RCTNetwork library in order to load images.", - request.URL.absoluteString); - return; + return ^{ + if (cancelLoad) { + cancelLoad(); } + OSAtomicOr32Barrier(1, &cancelled); + }; +} + +- (RCTImageLoaderCancellationBlock)_loadURLRequest:(NSURLRequest *)request + progressBlock:(RCTImageLoaderProgressBlock)progressHandler + completionBlock:(void (^)(NSError *error, id imageOrData))completionHandler +{ + // Check if networking module is available + if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) { + RCTLogError(@"No suitable image URL loader found for %@. You may need to " + " import the RCTNetwork library in order to load images.", + request.URL.absoluteString); + return NULL; + } + + RCTNetworking *networking = [_bridge networking]; - // Check if networking module can load image - if (RCT_DEBUG && ![self->_bridge.networking canHandleRequest:request]) { - RCTLogError(@"No suitable image URL loader found for %@", request.URL.absoluteString); + // Check if networking module can load image + if (RCT_DEBUG && ![networking canHandleRequest:request]) { + RCTLogError(@"No suitable image URL loader found for %@", request.URL.absoluteString); + return NULL; + } + + // Use networking module to load image + RCTURLRequestCompletionBlock processResponse = ^(NSURLResponse *response, NSData *data, NSError *error) { + // Check for system errors + if (error) { + completionHandler(error, nil); + return; + } else if (!data) { + completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil); return; } - // Use networking module to load image - RCTURLRequestCompletionBlock processResponse = - ^(NSURLResponse *response, NSData *data, NSError *error) { - - // Check for system errors - if (error) { - completionHandler(error, nil); + // Check for http errors + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; + if (statusCode != 200) { + completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain + code:statusCode + userInfo:nil], nil); return; - } else if (!data) { - completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil); - return; - } - - // Check for http errors - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; - if (statusCode != 200) { - completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain - code:statusCode - userInfo:nil], nil); - return; - } } - - // Call handler - completionHandler(nil, data); - }; - - // Add missing png extension - if (request.URL.fileURL && request.URL.pathExtension.length == 0) { - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]]; - request = mutableRequest; } - // Check for cached response before reloading - // TODO: move URL cache out of RCTImageLoader into its own module - NSCachedURLResponse *cachedResponse = [self->_URLCache cachedResponseForRequest:request]; - - while (cachedResponse) { - if ([cachedResponse.response isKindOfClass:[NSHTTPURLResponse class]]) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)cachedResponse.response; - if (httpResponse.statusCode == 301 || httpResponse.statusCode == 302) { - NSString *location = httpResponse.allHeaderFields[@"Location"]; - if (location == nil) { - completionHandler(RCTErrorWithMessage(@"Image redirect without location"), nil); - return; - } + // Call handler + completionHandler(nil, data); + }; + + // Check for cached response before reloading + // TODO: move URL cache out of RCTImageLoader into its own module + if (!_URLCache) { + _URLCache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB + diskCapacity:200 * 1024 * 1024 // 200MB + diskPath:@"React/RCTImageDownloader"]; + } - NSURL *redirectURL = [NSURL URLWithString: location relativeToURL: request.URL]; - request = [NSURLRequest requestWithURL:redirectURL]; - cachedResponse = [self->_URLCache cachedResponseForRequest:request]; - continue; + NSCachedURLResponse *cachedResponse = [_URLCache cachedResponseForRequest:request]; + while (cachedResponse) { + if ([cachedResponse.response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)cachedResponse.response; + if (httpResponse.statusCode == 301 || httpResponse.statusCode == 302) { + NSString *location = httpResponse.allHeaderFields[@"Location"]; + if (location == nil) { + completionHandler(RCTErrorWithMessage(@"Image redirect without location"), nil); + return NULL; } + + NSURL *redirectURL = [NSURL URLWithString: location relativeToURL: request.URL]; + request = [NSURLRequest requestWithURL:redirectURL]; + cachedResponse = [_URLCache cachedResponseForRequest:request]; + continue; } + } - processResponse(cachedResponse.response, cachedResponse.data, nil); + processResponse(cachedResponse.response, cachedResponse.data, nil); + return NULL; + } + + // Download image + __weak __typeof(self) weakSelf = self; + RCTNetworkTask *task = [networking networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { + if (error) { + completionHandler(error, nil); + [weakSelf dequeueTasks]; return; } - // Download image - RCTNetworkTask *task = [self->_bridge.networking networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { - if (error) { - completionHandler(error, nil); - [weakSelf dequeueTasks]; + dispatch_async(self->_URLCacheQueue, ^{ + __typeof(self) strongSelf = self; + if (!strongSelf) { return; } - dispatch_async(self->_URLCacheQueue, ^{ + // Cache the response + // TODO: move URL cache out of RCTImageLoader into its own module + BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"]; + [strongSelf->_URLCache storeCachedResponse: + [[NSCachedURLResponse alloc] initWithResponse:response + data:data + userInfo:nil + storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly] + forRequest:request]; + // Process image data + processResponse(response, data, nil); + + // Prepare for next task + [strongSelf dequeueTasks]; + }); - // Cache the response - // TODO: move URL cache out of RCTImageLoader into its own module - BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"]; - [strongSelf->_URLCache storeCachedResponse: - [[NSCachedURLResponse alloc] initWithResponse:response - data:data - userInfo:nil - storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly] - forRequest:request]; - // Process image data - processResponse(response, data, nil); + }]; - // Prepare for next task - [weakSelf dequeueTasks]; + task.downloadProgressBlock = progressHandler; - }); - - }]; - task.downloadProgressBlock = progressHandler; - - if (!self->_pendingTasks) { - self->_pendingTasks = [NSMutableArray new]; - } - if (task) { - [self->_pendingTasks addObject:task]; - [weakSelf dequeueTasks]; + if (task) { + if (!_pendingTasks) { + _pendingTasks = [NSMutableArray new]; } - - cancelLoad = ^{ - [task cancel]; - [weakSelf dequeueTasks]; - }; - - }); + [_pendingTasks addObject:task]; + [self dequeueTasks]; + } return ^{ - if (cancelLoad) { - cancelLoad(); - } - OSAtomicOr32Barrier(1, &cancelled); + [task cancel]; + [weakSelf dequeueTasks]; }; } @@ -520,11 +533,11 @@ - (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)image }; cancelLoad = [self loadImageOrDataWithURLRequest:imageURLRequest - size:size - scale:scale - resizeMode:resizeMode - progressBlock:progressHandler - completionBlock:completionHandler]; + size:size + scale:scale + resizeMode:resizeMode + progressBlock:progressHandler + completionBlock:completionHandler]; return ^{ if (cancelLoad) { cancelLoad(); From ba4c34c0db004cda9fb561b3bdf5224d518ab32d Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Jul 2016 13:28:13 -0700 Subject: [PATCH 077/241] Remove deprecated nativeScrollDelegate property Summary: We deprecated it a while back and nobody is using it internally. Reviewed By: majak Differential Revision: D3542602 fbshipit-source-id: dfe11a47b21d2f8a7c946c902f0ea427615ffc31 --- React/Views/RCTScrollView.m | 21 +-------------------- React/Views/RCTScrollableProtocol.h | 4 ---- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index 60ac1bff15fb89..93803e34db4c81 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -383,8 +383,6 @@ @implementation RCTScrollView NSHashTable *_scrollListeners; } -@synthesize nativeScrollDelegate = _nativeScrollDelegate; - - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { RCTAssertParam(eventDispatcher); @@ -588,19 +586,6 @@ - (void)delegateMethod:(UIScrollView *)scrollView \ RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin) RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) -- (void)setNativeScrollDelegate:(NSObject *)nativeScrollDelegate -{ - if (nativeScrollDelegate != _nativeScrollDelegate) { - if (_nativeScrollDelegate) { - [_scrollListeners removeObject:_nativeScrollDelegate]; - } - if (nativeScrollDelegate) { - [_scrollListeners addObject:nativeScrollDelegate]; - } - _nativeScrollDelegate = nativeScrollDelegate; - } -} - - (void)addScrollListener:(NSObject *)scrollListener { [_scrollListeners addObject:scrollListener]; @@ -608,11 +593,7 @@ - (void)addScrollListener:(NSObject *)scrollListener - (void)removeScrollListener:(NSObject *)scrollListener { - if (scrollListener == _nativeScrollDelegate) { - [self setNativeScrollDelegate:nil]; - } else { - [_scrollListeners removeObject:scrollListener]; - } + [_scrollListeners removeObject:scrollListener]; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView diff --git a/React/Views/RCTScrollableProtocol.h b/React/Views/RCTScrollableProtocol.h index 61762217350ecd..90c14ebd3cc39f 100644 --- a/React/Views/RCTScrollableProtocol.h +++ b/React/Views/RCTScrollableProtocol.h @@ -15,10 +15,6 @@ */ @protocol RCTScrollableProtocol -/* - * The nativeScrollDelegate property is now deprecated please use the scrollListener API instead - */ -@property (nonatomic, weak) NSObject *nativeScrollDelegate DEPRECATED_ATTRIBUTE; @property (nonatomic, readonly) CGSize contentSize; - (void)scrollToOffset:(CGPoint)offset; From 0a98b612aa67f944b22428f7bda39121604e7e81 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Mon, 11 Jul 2016 15:43:02 -0700 Subject: [PATCH 078/241] Change how native require hook is registered Summary: Instead of two separate dispatches to the JavaScript thread, only do one. Avoid the strongSelf dance entirely. This refactor does mean that the cost of registering the nativeRequire hook on the context is not measured by the `RCTPLRAMBundleLoad` tag. However it should be almost zero-cost to construct and set a single block, so I'm OK with that change. Reviewed By: bnham Differential Revision: D3542940 fbshipit-source-id: d6bd26e478d0d33b56f8116d7efe6aac80c91711 --- React/Executors/RCTJSCExecutor.mm | 66 ++++++++++++++----------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 57512557d2bfc2..1e920a367f4ed4 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -669,10 +669,14 @@ - (void)executeApplicationScript:(NSData *)script [_performanceLogger markStartForTag:RCTPLRAMBundleLoad]; NSError *error; script = loadRAMBundle(sourceURL, &error, _randomAccessBundle); - [self registerNativeRequire]; [_performanceLogger markStopForTag:RCTPLRAMBundleLoad]; [_performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize]; + // Reset the counters that the native require implementation uses + [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires]; + [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount]; + [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; + if (error) { if (onComplete) { onComplete(error); @@ -694,6 +698,10 @@ - (void)executeApplicationScript:(NSData *)script if (!self.isValid) { return; } + if (isRAMBundle) { + __weak RCTJSCExecutor *weakSelf = self; + self.context.context[@"nativeRequire"] = ^(NSNumber *moduleID) { [weakSelf _nativeRequire:moduleID]; }; + } [self->_performanceLogger markStartForTag:RCTPLScriptExecution]; NSError *error = executeApplicationScript(self->_jscWrapper, script, sourceURL, self->_context.context.JSGlobalContextRef); [self->_performanceLogger markStopForTag:RCTPLScriptExecution]; @@ -809,48 +817,34 @@ static void executeRandomAccessModule(RCTJSCExecutor *executor, uint32_t moduleI } } -- (void)registerNativeRequire +- (void)_nativeRequire:(NSNumber *)moduleID { - [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires]; - [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount]; - [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; - - [self executeBlockOnJavaScriptQueue:^{ - if (!self.valid) { - return; - } - - __weak RCTJSCExecutor *weakSelf = self; - self.context.context[@"nativeRequire"] = ^(NSNumber *moduleID) { - RCTJSCExecutor *strongSelf = weakSelf; - if (!strongSelf || !moduleID) { - return; - } + if (!moduleID) { + return; + } - [strongSelf->_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; - [strongSelf->_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; - RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, - [@"nativeRequire_" stringByAppendingFormat:@"%@", moduleID], nil); + [_performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; + [_performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; + RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, + [@"nativeRequire_" stringByAppendingFormat:@"%@", moduleID], nil); - const uint32_t ID = [moduleID unsignedIntValue]; + const uint32_t ID = [moduleID unsignedIntValue]; - if (ID < strongSelf->_randomAccessBundle.numTableEntries) { - ModuleData *moduleData = &strongSelf->_randomAccessBundle.table[ID]; - const uint32_t size = NSSwapLittleIntToHost(moduleData->size); + if (ID < _randomAccessBundle.numTableEntries) { + ModuleData *moduleData = &_randomAccessBundle.table[ID]; + const uint32_t size = NSSwapLittleIntToHost(moduleData->size); - // sparse entry in the table -- module does not exist or is contained in the startup section - if (size == 0) { - return; - } + // sparse entry in the table -- module does not exist or is contained in the startup section + if (size == 0) { + return; + } - [strongSelf->_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; - executeRandomAccessModule(strongSelf, ID, NSSwapLittleIntToHost(moduleData->offset), size); - } + [_performanceLogger addValue:size forTag:RCTPLRAMNativeRequiresSize]; + executeRandomAccessModule(self, ID, NSSwapLittleIntToHost(moduleData->offset), size); + } - RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); - [strongSelf->_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; - }; -}]; + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil); + [_performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; } static RandomAccessBundleStartupCode readRAMBundle(file_ptr bundle, RandomAccessBundleData &randomAccessBundle) From 9b184cc0f43194f8db73d2fea0b23b1284c487f9 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 11 Jul 2016 16:06:42 -0700 Subject: [PATCH 079/241] Fix bug in cancelling last task in TaskQueue Summary: We don't want to remove the last queue from the stack, it should just have no tasks in it. Fixes issue reported here: https://www.facebook.com/groups/reactnativeoss/permalink/1569170356712926/ Reviewed By: yungsters Differential Revision: D3539287 fbshipit-source-id: ea95673491fee0ea82f0f1b79b8f60e00cd3d035 --- Libraries/Interaction/TaskQueue.js | 15 +++++++++------ Libraries/Interaction/__tests__/TaskQueue-test.js | 9 +++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Libraries/Interaction/TaskQueue.js b/Libraries/Interaction/TaskQueue.js index 33838b15a15378..660241fb35bc5a 100644 --- a/Libraries/Interaction/TaskQueue.js +++ b/Libraries/Interaction/TaskQueue.js @@ -15,12 +15,12 @@ const infoLog = require('infoLog'); const invariant = require('fbjs/lib/invariant'); type SimpleTask = { - name: string; - run: () => void; + name: string, + run: () => void, }; type PromiseTask = { - name: string; - gen: () => Promise; + name: string, + gen: () => Promise, }; export type Task = Function | SimpleTask | PromiseTask; @@ -75,7 +75,7 @@ class TaskQueue { ...queue, tasks: queue.tasks.filter((task) => tasksToCancel.indexOf(task) === -1), })) - .filter((queue) => queue.tasks.length > 0); + .filter((queue, idx) => (queue.tasks.length > 0 || idx === 0)); } /** @@ -151,7 +151,10 @@ class TaskQueue { DEBUG && infoLog('exec gen task ' + task.name); task.gen() .then(() => { - DEBUG && infoLog('onThen for gen task ' + task.name, {stackIdx, queueStackSize: this._queueStack.length}); + DEBUG && infoLog( + 'onThen for gen task ' + task.name, + {stackIdx, queueStackSize: this._queueStack.length}, + ); this._queueStack[stackIdx].popable = true; this.hasTasksToProcess() && this._onMoreTasks(); }) diff --git a/Libraries/Interaction/__tests__/TaskQueue-test.js b/Libraries/Interaction/__tests__/TaskQueue-test.js index 1da2e4430f2320..e1a24009f01e57 100644 --- a/Libraries/Interaction/__tests__/TaskQueue-test.js +++ b/Libraries/Interaction/__tests__/TaskQueue-test.js @@ -142,4 +142,13 @@ describe('TaskQueue', () => { expectToBeCalledOnce(task4); expect(taskQueue.hasTasksToProcess()).toBe(false); }); + + it('should not crash when last task is cancelled', () => { + const task1 = jest.fn(); + taskQueue.enqueue(task1); + taskQueue.cancelTasks([task1]); + clearTaskQueue(taskQueue); + expect(task1).not.toBeCalled(); + expect(taskQueue.hasTasksToProcess()).toBe(false); + }); }); From 4d6c1e55d7853a5758f0373056428367799f5f93 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 11 Jul 2016 17:53:35 -0700 Subject: [PATCH 080/241] Bring back trackingName Reviewed By: yungsters Differential Revision: D3501805 fbshipit-source-id: be7e1a76c022d050542af797dda49b3cf14340bb --- Libraries/Network/RCTNetworking.android.js | 18 ++++++++++++++---- Libraries/Network/RCTNetworking.ios.js | 16 +++++++++++++--- Libraries/Network/XMLHttpRequest.js | 10 ++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Libraries/Network/RCTNetworking.android.js b/Libraries/Network/RCTNetworking.android.js index 23aa9e8bf54199..60f59bb370e134 100644 --- a/Libraries/Network/RCTNetworking.android.js +++ b/Libraries/Network/RCTNetworking.android.js @@ -27,7 +27,7 @@ function convertHeadersMapToArray(headers: Object): Array
{ } let _requestId = 1; -function generateRequestId() { +function generateRequestId(): number { return _requestId++; } @@ -41,7 +41,16 @@ class RCTNetworking extends NativeEventEmitter { super(RCTNetworkingNative); } - sendRequest(method, url, headers, data, incrementalUpdates, timeout, callback) { + sendRequest( + method: ?string, + trackingName: string, + url: ?string, + headers: Object, + data: string | FormData | Object, + incrementalUpdates: boolean, + timeout: number, + callback: (requestId: number) => void, + ) { if (typeof data === 'string') { data = {string: data}; } else if (data instanceof FormData) { @@ -52,6 +61,7 @@ class RCTNetworking extends NativeEventEmitter { }), }; } + data = {...data, trackingName}; const requestId = generateRequestId(); RCTNetworkingNative.sendRequest( method, @@ -65,11 +75,11 @@ class RCTNetworking extends NativeEventEmitter { callback(requestId); } - abortRequest(requestId) { + abortRequest(requestId: number) { RCTNetworkingNative.abortRequest(requestId); } - clearCookies(callback) { + clearCookies(callback: number) { RCTNetworkingNative.clearCookies(callback); } } diff --git a/Libraries/Network/RCTNetworking.ios.js b/Libraries/Network/RCTNetworking.ios.js index 3e0e4c35284119..b51de5afd1160a 100644 --- a/Libraries/Network/RCTNetworking.ios.js +++ b/Libraries/Network/RCTNetworking.ios.js @@ -20,12 +20,22 @@ class RCTNetworking extends NativeEventEmitter { super(RCTNetworkingNative); } - sendRequest(method, url, headers, data, incrementalUpdates, timeout, callback) { + sendRequest( + method: ?string, + trackingName: string, + url: ?string, + headers: Object, + data: string | FormData | Object, + incrementalUpdates: boolean, + timeout: number, + callback: (requestId: number) => void, + ) { if (typeof data === 'string') { data = {string: data}; } else if (data instanceof FormData) { data = {formData: data.getParts()}; } + data = {...data, trackingName}; RCTNetworkingNative.sendRequest({ method, url, @@ -36,11 +46,11 @@ class RCTNetworking extends NativeEventEmitter { }, callback); } - abortRequest(requestId) { + abortRequest(requestId: number) { RCTNetworkingNative.abortRequest(requestId); } - clearCookies(callback) { + clearCookies(callback: number) { console.warn('RCTNetworking.clearCookies is not supported on iOS'); } } diff --git a/Libraries/Network/XMLHttpRequest.js b/Libraries/Network/XMLHttpRequest.js index ed39d9fe450070..2e7d628139b62a 100644 --- a/Libraries/Network/XMLHttpRequest.js +++ b/Libraries/Network/XMLHttpRequest.js @@ -106,6 +106,7 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) { _sent: boolean; _url: ?string = null; _timedOut: boolean = false; + _trackingName: string = 'unknown'; _incrementalEvents: boolean = false; constructor() { @@ -322,6 +323,14 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) { this._headers[header.toLowerCase()] = value; } + /** + * Custom extension for tracking origins of request. + */ + setTrackingName(trackingName: string): XMLHttpRequest { + this._trackingName = trackingName; + return this; + } + open(method: string, url: string, async: ?boolean): void { /* Other optional arguments are not supported yet */ if (this.readyState !== this.UNSENT) { @@ -367,6 +376,7 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) { )); RCTNetworking.sendRequest( method, + this._trackingName, url, headers, data, From d905af47362e2a95677678ec5a4256e35f8782ea Mon Sep 17 00:00:00 2001 From: Elliot Lynde Date: Mon, 11 Jul 2016 19:30:45 -0700 Subject: [PATCH 081/241] Delete AnimatedNative-test Reviewed By: cpojer Differential Revision: D3546643 fbshipit-source-id: d0484e55e8e2943584d9084f2663dbdbfe3fce0e --- .../src/__tests__/AnimatedNative-test.js | 362 ------------------ 1 file changed, 362 deletions(-) delete mode 100644 Libraries/Animated/src/__tests__/AnimatedNative-test.js diff --git a/Libraries/Animated/src/__tests__/AnimatedNative-test.js b/Libraries/Animated/src/__tests__/AnimatedNative-test.js deleted file mode 100644 index 34175ef835ef6f..00000000000000 --- a/Libraries/Animated/src/__tests__/AnimatedNative-test.js +++ /dev/null @@ -1,362 +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. - */ -'use strict'; - -jest - .disableAutomock() - .setMock('Text', {}) - .setMock('View', {}) - .setMock('Image', {}) - .setMock('React', {Component: class {}}) - .setMock('NativeModules', { - NativeAnimatedModule: {}, - }); - -var Animated = require('Animated'); - -describe('Animated', () => { - - beforeEach(() => { - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - nativeAnimatedModule.createAnimatedNode = jest.fn(); - nativeAnimatedModule.connectAnimatedNodes = jest.fn(); - nativeAnimatedModule.disconnectAnimatedNodes = jest.fn(); - nativeAnimatedModule.startAnimatingNode = jest.fn(); - nativeAnimatedModule.stopAnimation = jest.fn(); - nativeAnimatedModule.setAnimatedNodeValue = jest.fn(); - nativeAnimatedModule.connectAnimatedNodeToView = jest.fn(); - nativeAnimatedModule.disconnectAnimatedNodeFromView = jest.fn(); - nativeAnimatedModule.dropAnimatedNode = jest.fn(); - - // jest environment doesn't have cancelAnimationFrame :( - if (!global.cancelAnimationFrame) { - global.cancelAnimationFrame = jest.fn(); - } - }); - - it('creates and detaches nodes', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim, - }, - }; - c.componentWillMount(); - - Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); - - c.componentWillUnmount(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.createAnimatedNode.mock.calls.length).toBe(3); - expect(nativeAnimatedModule.connectAnimatedNodes.mock.calls.length).toBe(2); - - expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( - jasmine.any(Number), - jasmine.any(Number), - {type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), delay: jasmine.any(Number)}, - jasmine.any(Function) - ); - - expect(nativeAnimatedModule.disconnectAnimatedNodes.mock.calls.length).toBe(2); - expect(nativeAnimatedModule.dropAnimatedNode.mock.calls.length).toBe(2); - }); - - it('sends a valid description for value, style and props nodes', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim, - }, - }; - c.componentWillMount(); - - Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'value', value: 0 }); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'style', style: { opacity: jasmine.any(Number) }}); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }}); - }); - - it('sends a valid graph description for Animated.add nodes', () => { - var first = new Animated.Value(1); - var second = new Animated.Value(2); - - var c = new Animated.View(); - c.props = { - style: { - opacity: Animated.add(first, second), - }, - }; - c.componentWillMount(); - - Animated.timing(first, {toValue: 2, duration: 1000, useNativeDriver: true}).start(); - Animated.timing(second, {toValue: 3, duration: 1000, useNativeDriver: true}).start(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'addition', input: jasmine.any(Array) }); - var additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( - (call) => call[1].type === 'addition' - ); - expect(additionCalls.length).toBe(1); - var additionCall = additionCalls[0]; - var additionNodeTag = additionCall[0]; - var additionConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( - (call) => call[1] === additionNodeTag - ); - expect(additionConnectionCalls.length).toBe(2); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(additionCall[1].input[0], { type: 'value', value: 1 }); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(additionCall[1].input[1], { type: 'value', value: 2 }); - }); - - it('sends a valid graph description for Animated.multiply nodes', () => { - var first = new Animated.Value(2); - var second = new Animated.Value(1); - - var c = new Animated.View(); - c.props = { - style: { - opacity: Animated.multiply(first, second), - }, - }; - c.componentWillMount(); - - Animated.timing(first, {toValue: 5, duration: 1000, useNativeDriver: true}).start(); - Animated.timing(second, {toValue: -1, duration: 1000, useNativeDriver: true}).start(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'multiplication', input: jasmine.any(Array) }); - var multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( - (call) => call[1].type === 'multiplication' - ); - expect(multiplicationCalls.length).toBe(1); - var multiplicationCall = multiplicationCalls[0]; - var multiplicationNodeTag = multiplicationCall[0]; - var multiplicationConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( - (call) => call[1] === multiplicationNodeTag - ); - expect(multiplicationConnectionCalls.length).toBe(2); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(multiplicationCall[1].input[0], { type: 'value', value: 2 }); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(multiplicationCall[1].input[1], { type: 'value', value: 1 }); - }); - - it('sends a valid graph description for interpolate() nodes', () => { - var node = new Animated.Value(10); - - var c = new Animated.View(); - c.props = { - style: { - opacity: node.interpolate({ - inputRange: [10, 20], - outputRange: [0, 1], - }), - }, - }; - c.componentWillMount(); - - Animated.timing(node, {toValue: 20, duration: 1000, useNativeDriver: true}).start(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(jasmine.any(Number), { type: 'value', value: 10 }); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { - type: 'interpolation', - inputRange: [10, 20], - outputRange: [0, 1], - }); - var interpolationNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find( - (call) => call[1].type === 'interpolation' - )[0]; - var valueNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find( - (call) => call[1].type === 'value' - )[0]; - expect(nativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(valueNodeTag, interpolationNodeTag); - }); - - it('sends a valid timing animation description', () => { - var anim = new Animated.Value(0); - Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( - jasmine.any(Number), - jasmine.any(Number), - {type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), delay: jasmine.any(Number)}, - jasmine.any(Function) - ); - }); - - it('proxies `setValue` correctly', () => { - var anim = new Animated.Value(0); - Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim, - }, - }; - c.componentWillMount(); - - // We expect `setValue` not to propagate down to `setNativeProps`, otherwise it may try to access `setNativeProps` - // via component refs table that we override here. - c.refs = { - node: { - setNativeProps: jest.genMockFunction(), - }, - }; - - anim.setValue(0.5); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(jasmine.any(Number), 0.5); - expect(c.refs.node.setNativeProps.mock.calls.length).toBe(0); - }); - - it('doesn\'t call into native API if useNativeDriver is set to false', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim, - }, - }; - c.componentWillMount(); - - Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: false}).start(); - - c.componentWillUnmount(); - - expect(require('NativeModules').NativeAnimatedModule.createAnimatedNode).not.toBeCalled(); - }); - - it('fails when trying to run non-native animation on native node', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim, - }, - }; - c.componentWillMount(); - - Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}).start(); - jest.runAllTimers(); - - Animated.timing(anim, {toValue: 4, duration: 500, useNativeDriver: false}).start(); - expect(jest.runAllTimers).toThrow(); - }); - - it('fails for unsupported prop types', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim, - }, - randomProperty: anim, - }; - c.componentWillMount(); - - var animation = Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}); - expect(animation.start).toThrowError(/randomProperty/); - }); - - it('fails for unsupported styles', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - left: anim, - }, - }; - c.componentWillMount(); - - var animation = Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}); - expect(animation.start).toThrowError(/left/); - }); - - it('fails for unsupported interpolation parameters', () => { - var anim = new Animated.Value(0); - - var c = new Animated.View(); - c.props = { - style: { - opacity: anim.interpolate({ - inputRange: [0, 100], - outputRange: [0, 1], - extrapolate: 'clamp', - }), - }, - }; - c.componentWillMount(); - - var animation = Animated.timing(anim, {toValue: 100, duration: 50, useNativeDriver: true}); - expect(animation.start).toThrowError(/extrapolate/); - }); - - it('works for any `static` props and styles', () => { - // Passing "unsupported" props should work just fine as long as they are not animated - var anim = new Animated.Value(0); - - var node = new Animated.__PropsOnlyForTests({ - style: { - left: 10, - top: 20, - opacity: anim, - }, - removeClippedSubviews: true, - }); - Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}).start(); - node.__detach(); - - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'style', style: { opacity: jasmine.any(Number) }}); - expect(nativeAnimatedModule.createAnimatedNode) - .toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }}); - }); - - it('sends stopAnimation command to native', () => { - var value = new Animated.Value(0); - var animation = Animated.timing(value, {toValue: 10, duration: 50, useNativeDriver: true}); - var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; - - animation.start(); - expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( - jasmine.any(Number), - jasmine.any(Number), - {type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), delay: jasmine.any(Number)}, - jasmine.any(Function) - ); - var animationId = nativeAnimatedModule.startAnimatingNode.mock.calls[0][0]; - - animation.stop(); - expect(nativeAnimatedModule.stopAnimation).toBeCalledWith(animationId); - }); - -}); From 471ee87445878bae729550b44421c5bbe6ccb999 Mon Sep 17 00:00:00 2001 From: Mengjue Wang Date: Mon, 11 Jul 2016 20:43:53 -0700 Subject: [PATCH 082/241] Wrap native module I18nManager with a new RCTI18nManager.js and fix current use of native module I18nManager Summary: create new RCTI18nManager.js to warp-up native module I18nManager and fix current use of it. Reviewed By: ericvicenti Differential Revision: D3547427 fbshipit-source-id: 53a695c94ca6bba2f566d0725c553e58d60bf451 --- .../Navigator/NavigatorSceneConfigs.js | 2 +- .../Experimental/SwipeableRow/SwipeableRow.js | 6 +++-- Libraries/Utilities/I18nManager.js | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 Libraries/Utilities/I18nManager.js diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index 62c7acf9a1dfd6..3578281ebe0c72 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -33,10 +33,10 @@ var Dimensions = require('Dimensions'); var PixelRatio = require('PixelRatio'); +var I18nManager = require('I18nManager'); var buildStyleInterpolator = require('buildStyleInterpolator'); -var I18nManager = require('NativeModules').I18nManager || {}; var IS_RTL = I18nManager.isRTL; var SCREEN_WIDTH = Dimensions.get('window').width; diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRow.js b/Libraries/Experimental/SwipeableRow/SwipeableRow.js index 80a2ffb5fd39bf..922d70a25eb43a 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableRow.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableRow.js @@ -25,6 +25,7 @@ const Animated = require('Animated'); const PanResponder = require('PanResponder'); +const I18nManager = require('I18nManager'); const React = require('React'); const StyleSheet = require('StyleSheet'); const TimerMixin = require('react-timer-mixin'); @@ -34,7 +35,6 @@ const {PropTypes} = React; const emptyFunction = require('fbjs/lib/emptyFunction'); -const I18nManager = require('NativeModules').I18nManager || {}; const IS_RTL = I18nManager.isRTL; // NOTE: Eventually convert these consts to an input object of configurations @@ -308,7 +308,9 @@ const SwipeableRow = React.createClass({ * When swiping right, we want to bounce back past closed position on release * so users know they should swipe right to get content. */ - const swipeBounceBackDistance = IS_RTL ? -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE : RIGHT_SWIPE_BOUNCE_BACK_DISTANCE; + const swipeBounceBackDistance = IS_RTL ? + -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE : + RIGHT_SWIPE_BOUNCE_BACK_DISTANCE; this._animateTo( -swipeBounceBackDistance, duration, diff --git a/Libraries/Utilities/I18nManager.js b/Libraries/Utilities/I18nManager.js new file mode 100644 index 00000000000000..eae4e5935d4c8c --- /dev/null +++ b/Libraries/Utilities/I18nManager.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule I18nManager + * @flow + */ +'use strict'; + +type I18nManagerStatus = { + isRTL: boolean, + allowRTL: (allowRTL: boolean) => {}, +}; + +const I18nManager : I18nManagerStatus = require('NativeModules').I18nManager || { + isRTL: false, + allowRTL: () => {}, +}; + +module.exports = I18nManager; From 38a6eec0db85a5204e85a9a92b4dee2db9641671 Mon Sep 17 00:00:00 2001 From: Mengjue Wang Date: Mon, 11 Jul 2016 20:43:57 -0700 Subject: [PATCH 083/241] Provide RTL support for new Navigator -- Make RTL works in NUX Summary: Provide RTL support in NavigationPager Reviewed By: fkgozali Differential Revision: D3536850 fbshipit-source-id: 29890a125dc5e001b4c10208cd53bfeca0d9b5c3 --- .../NavigationPagerPanResponder.js | 17 +++++++++++++---- .../NavigationPagerStyleInterpolator.js | 9 +++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js index e7c08509828ce2..6bd5410b120533 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js @@ -15,6 +15,7 @@ const Animated = require('Animated'); const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder'); const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder'); +const I18nManager = require('I18nManager'); const clamp = require('clamp'); @@ -133,6 +134,9 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { const distance = isVertical ? layout.height.__getValue() : layout.width.__getValue(); + const currentValue = I18nManager.isRTL && axis === 'dx' ? + this._startValue + (gesture[axis] / distance) : + this._startValue - (gesture[axis] / distance); const prevIndex = Math.max( 0, @@ -146,7 +150,7 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { const value = clamp( prevIndex, - this._startValue - (gesture[axis] / distance), + currentValue, nextIndex, ); @@ -171,14 +175,19 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { const axis = isVertical ? 'dy' : 'dx'; const velocityAxis = isVertical ? 'vy' : 'vx'; const index = navigationState.index; - const distance = gesture[axis]; + const distance = I18nManager.isRTL && axis === 'dx' ? + -gesture[axis] : + gesture[axis]; + const moveSpeed = I18nManager.isRTL && velocityAxis === 'vx' ? + -gesture[velocityAxis] : + gesture[velocityAxis]; position.stopAnimation((value: number) => { this._reset(); if ( distance > DISTANCE_THRESHOLD || value <= index - POSITION_THRESHOLD || - gesture[velocityAxis] > VELOCITY_THRESHOLD + moveSpeed > VELOCITY_THRESHOLD ) { onNavigateBack && onNavigateBack(); return; @@ -187,7 +196,7 @@ class NavigationPagerPanResponder extends NavigationAbstractPanResponder { if ( distance < -DISTANCE_THRESHOLD || value >= index + POSITION_THRESHOLD || - gesture[velocityAxis] < -VELOCITY_THRESHOLD + moveSpeed < -VELOCITY_THRESHOLD ) { onNavigateForward && onNavigateForward(); } diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js index 87a211b1a723d5..950aa5eb136a1c 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js @@ -32,6 +32,8 @@ */ 'use strict'; +const I18nManager = require('I18nManager'); + import type { NavigationSceneRendererProps, } from 'NavigationTypeDefinition'; @@ -87,11 +89,14 @@ function forHorizontal(props: NavigationSceneRendererProps): Object { const index = scene.index; const inputRange = [index - 1, index, index + 1]; - const width = layout.initWidth; + const outputRange = I18nManager.isRTL ? + ([-width, 0, width]: Array) : + ([width, 0, -width]: Array); + const translateX = position.interpolate({ inputRange, - outputRange: ([width, 0, -width]: Array), + outputRange, }); return { From 80c71e5caecd24cc2d0d02ba43caee7495a96688 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Tue, 12 Jul 2016 05:13:30 -0700 Subject: [PATCH 084/241] Refine RCTJSCExecutor's APIs for using a thread/context that already exists Summary: The `initWithJSContextProvider:` API created a `RCTJSCExecutor` with a thread/context that already exists, but it did not solve the problem of loading an actual application script; the `executeApplicationScript:` API is also asynchronous. Create a new merged API that allows you to pass in a pre-created thread/context pair and immediately receive an `RCTJSCExector` that has already executed a specified application script. This also removes the `underlyingJSContext` API entirely, in favor of passing it back in a byref variable in the new API. This minimizes the surface area for API abuse. Reviewed By: bnham Differential Revision: D3545349 fbshipit-source-id: 1c564f44d2a5379b5e6f75640079a28fd7169f67 --- React/Executors/RCTJSCExecutor.h | 17 ++-- React/Executors/RCTJSCExecutor.mm | 126 +++++++++++++++++++++--------- 2 files changed, 99 insertions(+), 44 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.h b/React/Executors/RCTJSCExecutor.h index 474db76e2c97f5..11932c1d8bea4a 100644 --- a/React/Executors/RCTJSCExecutor.h +++ b/React/Executors/RCTJSCExecutor.h @@ -54,11 +54,6 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification; */ - (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary; -/** - * Pass a RCTJSContextProvider object to use an NSThread/JSContext pair that have already been created. - */ -- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider; - /** * Create a NSError from a JSError object. * @@ -68,8 +63,16 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification; - (NSError *)convertJSErrorToNSError:(JSValueRef)jsError context:(JSContextRef)context; /** - * Returns the underlying JSContext. + * @experimental + * Pass a RCTJSContextProvider object to use an NSThread/JSContext pair that have already been created. + * The returned executor has already executed the supplied application script synchronously. + * The underlying JSContext will be returned in the JSContext pointer if it is non-NULL and there was no error. + * If an error occurs, this method will return nil and specify the error in the error pointer if it is non-NULL. */ -- (JSContext *)underlyingJSContext; ++ (instancetype)initializedExecutorWithContextProvider:(RCTJSContextProvider *)JSContextProvider + applicationScript:(NSData *)applicationScript + sourceURL:(NSURL *)sourceURL + JSContext:(JSContext **)JSContext + error:(NSError **)error; @end diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 1e920a367f4ed4..7359c03687af2f 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -320,10 +320,26 @@ - (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary return self; } -- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider ++ (instancetype)initializedExecutorWithContextProvider:(RCTJSContextProvider *)JSContextProvider + applicationScript:(NSData *)applicationScript + sourceURL:(NSURL *)sourceURL + JSContext:(JSContext **)JSContext + error:(NSError **)error +{ + const RCTJSContextData data = JSContextProvider.data; + if (JSContext) { + *JSContext = data.context; + } + RCTJSCExecutor *executor = [[RCTJSCExecutor alloc] initWithJSContextData:data]; + if (![executor _synchronouslyExecuteApplicationScript:applicationScript sourceURL:sourceURL JSContext:data.context error:error]) { + return nil; // error has been set by _synchronouslyExecuteApplicationScript: + } + return executor; +} + +- (instancetype)initWithJSContextData:(const RCTJSContextData &)data { if (self = [super init]) { - const RCTJSContextData data = JSContextProvider.data; _useCustomJSCLibrary = data.useCustomJSCLibrary; _valid = YES; _javaScriptThread = data.javaScriptThread; @@ -333,6 +349,30 @@ - (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvi return self; } +- (BOOL)_synchronouslyExecuteApplicationScript:(NSData *)script + sourceURL:(NSURL *)sourceURL + JSContext:(JSContext *)context + error:(NSError **)error +{ + BOOL isRAMBundle = NO; + script = loadPossiblyBundledApplicationScript(script, sourceURL, _performanceLogger, isRAMBundle, _randomAccessBundle, error); + if (!script) { + return NO; + } + if (isRAMBundle) { + registerNativeRequire(context, self); + } + NSError *returnedError = executeApplicationScript(script, sourceURL, _jscWrapper, _performanceLogger, _context.context.JSGlobalContextRef); + if (returnedError) { + if (error) { + *error = returnedError; + } + return NO; + } else { + return YES; + } +} + - (RCTJavaScriptContext *)context { RCTAssertThread(_javaScriptThread, @"Must be called on JS thread."); @@ -343,11 +383,6 @@ - (RCTJavaScriptContext *)context return _context; } -- (JSContext *)underlyingJSContext -{ - return self.context.context; -} - - (void)setUp { #if RCT_PROFILE @@ -662,36 +697,16 @@ - (void)executeApplicationScript:(NSData *)script RCTAssertParam(script); RCTAssertParam(sourceURL); - // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. - uint32_t magicNumber = NSSwapLittleIntToHost(*((uint32_t *)script.bytes)); - BOOL isRAMBundle = magicNumber == RCTRAMBundleMagicNumber; - if (isRAMBundle) { - [_performanceLogger markStartForTag:RCTPLRAMBundleLoad]; + BOOL isRAMBundle = NO; + { NSError *error; - script = loadRAMBundle(sourceURL, &error, _randomAccessBundle); - [_performanceLogger markStopForTag:RCTPLRAMBundleLoad]; - [_performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize]; - - // Reset the counters that the native require implementation uses - [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires]; - [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount]; - [_performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; - - if (error) { + script = loadPossiblyBundledApplicationScript(script, sourceURL, _performanceLogger, isRAMBundle, _randomAccessBundle, &error); + if (script == nil) { if (onComplete) { onComplete(error); } return; } - } else { - // JSStringCreateWithUTF8CString expects a null terminated C string. - // RAM Bundling already provides a null terminated one. - NSMutableData *nullTerminatedScript = [NSMutableData dataWithCapacity:script.length + 1]; - - [nullTerminatedScript appendData:script]; - [nullTerminatedScript appendBytes:"" length:1]; - - script = nullTerminatedScript; } [self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{ @@ -699,26 +714,63 @@ - (void)executeApplicationScript:(NSData *)script return; } if (isRAMBundle) { - __weak RCTJSCExecutor *weakSelf = self; - self.context.context[@"nativeRequire"] = ^(NSNumber *moduleID) { [weakSelf _nativeRequire:moduleID]; }; + registerNativeRequire(self.context.context, self); } - [self->_performanceLogger markStartForTag:RCTPLScriptExecution]; - NSError *error = executeApplicationScript(self->_jscWrapper, script, sourceURL, self->_context.context.JSGlobalContextRef); - [self->_performanceLogger markStopForTag:RCTPLScriptExecution]; + NSError *error = executeApplicationScript(script, sourceURL, self->_jscWrapper, self->_performanceLogger, + self->_context.context.JSGlobalContextRef); if (onComplete) { onComplete(error); } }), 0, @"js_call", (@{ @"url": sourceURL.absoluteString }))]; } -static NSError *executeApplicationScript(RCTJSCWrapper *jscWrapper, NSData *script, NSURL *sourceURL, JSGlobalContextRef ctx) +static NSData *loadPossiblyBundledApplicationScript(NSData *script, NSURL *sourceURL, + RCTPerformanceLogger *performanceLogger, + BOOL &isRAMBundle, RandomAccessBundleData &randomAccessBundle, + NSError **error) +{ + // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. + uint32_t magicNumber = NSSwapLittleIntToHost(*((uint32_t *)script.bytes)); + isRAMBundle = magicNumber == RCTRAMBundleMagicNumber; + if (isRAMBundle) { + [performanceLogger markStartForTag:RCTPLRAMBundleLoad]; + script = loadRAMBundle(sourceURL, error, randomAccessBundle); + [performanceLogger markStopForTag:RCTPLRAMBundleLoad]; + [performanceLogger setValue:script.length forTag:RCTPLRAMStartupCodeSize]; + + // Reset the counters that the native require implementation uses + [performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires]; + [performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount]; + [performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize]; + + return script; + } else { + // JSStringCreateWithUTF8CString expects a null terminated C string. + // RAM Bundling already provides a null terminated one. + NSMutableData *nullTerminatedScript = [NSMutableData dataWithCapacity:script.length + 1]; + [nullTerminatedScript appendData:script]; + [nullTerminatedScript appendBytes:"" length:1]; + return nullTerminatedScript; + } +} + +static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor) +{ + __weak RCTJSCExecutor *weakExecutor = executor; + context[@"nativeRequire"] = ^(NSNumber *moduleID) { [weakExecutor _nativeRequire:moduleID]; }; +} + +static NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJSCWrapper *jscWrapper, + RCTPerformanceLogger *performanceLogger, JSGlobalContextRef ctx) { + [performanceLogger markStartForTag:RCTPLScriptExecution]; JSValueRef jsError = NULL; JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes); JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String); JSValueRef result = jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError); jscWrapper->JSStringRelease(bundleURL); jscWrapper->JSStringRelease(execJSString); + [performanceLogger markStopForTag:RCTPLScriptExecution]; return result ? nil : RCTNSErrorFromJSError(jscWrapper, ctx, jsError); } From c7a590655ce6e42b31b215ad76ac909310546d03 Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Tue, 12 Jul 2016 05:13:32 -0700 Subject: [PATCH 085/241] Expose a method to synchronously load bundle if possible Summary: This diff exposes a new experimental method `[RCTJavaScriptLoader +attemptSynchronousLoadOfBundleAtURL:sourceLength:error:]`. It may be used if you know that a specific call site must load its JavaScript synchronously, or else fail entirely. This new API will succeed for file URLs that point to a RAM bundle. It will fail for non-RAM-bundle files and for HTTP URLs. This also cleans up the error domain and codes for this class. This should be the only externally visible change from this diff if you don't use the new API: the codes and domains you receive from the API may change slightly. They were pretty sloppy and undocumented before, so I think this change is for the better. Reviewed By: bnham Differential Revision: D3545956 fbshipit-source-id: 30e65f4e8330d2d68f3f50ade077fdc1db4a435e --- React/Base/RCTJavaScriptLoader.h | 27 ++- React/Base/RCTJavaScriptLoader.m | 287 ++++++++++++++++++++----------- 2 files changed, 213 insertions(+), 101 deletions(-) diff --git a/React/Base/RCTJavaScriptLoader.h b/React/Base/RCTJavaScriptLoader.h index 1d8a19ee57e330..bf51d31ae7159d 100755 --- a/React/Base/RCTJavaScriptLoader.h +++ b/React/Base/RCTJavaScriptLoader.h @@ -13,6 +13,18 @@ extern uint32_t const RCTRAMBundleMagicNumber; +extern NSString *const RCTJavaScriptLoaderErrorDomain; + +NS_ENUM(NSInteger) { + RCTJavaScriptLoaderErrorNoScriptURL = 1, + RCTJavaScriptLoaderErrorFailedOpeningFile = 2, + RCTJavaScriptLoaderErrorFailedReadingFile = 3, + RCTJavaScriptLoaderErrorFailedStatingFile = 3, + RCTJavaScriptLoaderErrorURLLoadFailed = 3, + + RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously = 1000, +}; + @class RCTBridge; /** @@ -22,6 +34,19 @@ extern uint32_t const RCTRAMBundleMagicNumber; */ @interface RCTJavaScriptLoader : NSObject -+ (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTSourceLoadBlock)onComplete; ++ (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComplete; + +/** + * @experimental + * Attempts to synchronously load the script at the given URL. The following two conditions must be met: + * 1. It must be a file URL. + * 2. It must point to a RAM bundle, or allowLoadingNonRAMBundles must be YES. + * If the URL does not meet those conditions, this method will return nil and supply an error with the domain + * RCTJavaScriptLoaderErrorDomain and the code RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously. + */ ++ (NSData *)attemptSynchronousLoadOfBundleAtURL:(NSURL *)scriptURL + sourceLength:(int64_t *)sourceLength + allowLoadingNonRAMBundles:(BOOL)allowLoadingNonRAMBundles + error:(NSError **)error; @end diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index a44dc5baf2cd3f..9e978a51c90d98 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -19,134 +19,221 @@ uint32_t const RCTRAMBundleMagicNumber = 0xFB0BD1E5; +NSString *const RCTJavaScriptLoaderErrorDomain = @"RCTJavaScriptLoaderErrorDomain"; + @implementation RCTJavaScriptLoader RCT_NOT_IMPLEMENTED(- (instancetype)init) + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComplete +{ + int64_t sourceLength; + NSError *error; + NSData *data = [self attemptSynchronousLoadOfBundleAtURL:scriptURL + sourceLength:&sourceLength + allowLoadingNonRAMBundles:NO // we'll do it async + error:&error]; + if (data) { + onComplete(nil, data, sourceLength); + return; + } + + const BOOL isCannotLoadSyncError = + [error.domain isEqualToString:RCTJavaScriptLoaderErrorDomain] + && error.code == RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously; + + if (isCannotLoadSyncError) { + attemptAsynchronousLoadOfBundleAtURL(scriptURL, onComplete); + } else { + onComplete(error, nil, 0); + } +} + ++ (NSData *)attemptSynchronousLoadOfBundleAtURL:(NSURL *)scriptURL + sourceLength:(int64_t *)sourceLength + allowLoadingNonRAMBundles:(BOOL)allowLoadingNonRAMBundles + error:(NSError **)error { NSString *unsanitizedScriptURLString = scriptURL.absoluteString; // Sanitize the script URL - scriptURL = [RCTConvert NSURL:unsanitizedScriptURLString]; + scriptURL = sanitizeURL(scriptURL); if (!scriptURL) { - NSString *errorDescription = [NSString stringWithFormat:@"No script URL provided." - @"unsanitizedScriptURLString:(%@)", unsanitizedScriptURLString]; - NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{ - NSLocalizedDescriptionKey: errorDescription - }]; - onComplete(error, nil, 0); - return; + if (error) { + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorNoScriptURL + userInfo:@{NSLocalizedDescriptionKey: + [NSString stringWithFormat:@"No script URL provided. " + @"unsanitizedScriptURLString:(%@)", unsanitizedScriptURLString]}]; + } + return nil; } // Load local script file - if (scriptURL.fileURL) { - // Load the first 4 bytes to check if the bundle is regular or RAM ("Random Access Modules" bundle). - // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. - // The benefit of RAM bundle over a regular bundle is that we can lazily inject - // modules into JSC as they're required. - FILE *bundle = fopen(scriptURL.path.UTF8String, "r"); - if (!bundle) { - onComplete(RCTErrorWithMessage([NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]), nil, 0); - return; + if (!scriptURL.fileURL) { + if (error) { + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously + userInfo:@{NSLocalizedDescriptionKey: + @"Cannot load non-file URLs synchronously"}]; } + return nil; + } - uint32_t magicNumber; - size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); - fclose(bundle); - if (readResult != 1) { - onComplete(RCTErrorWithMessage(@"Error reading bundle"), nil, 0); - return; + // Load the first 4 bytes to check if the bundle is regular or RAM ("Random Access Modules" bundle). + // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. + // The benefit of RAM bundle over a regular bundle is that we can lazily inject + // modules into JSC as they're required. + FILE *bundle = fopen(scriptURL.path.UTF8String, "r"); + if (!bundle) { + if (error) { + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorFailedOpeningFile + userInfo:@{NSLocalizedDescriptionKey: + [NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]}]; } + return nil; + } - magicNumber = NSSwapLittleIntToHost(magicNumber); - if (magicNumber == RCTRAMBundleMagicNumber) { - NSData *source = [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; - NSError *error = nil; - int64_t sourceLength = 0; - - struct stat statInfo; - if (stat(scriptURL.path.UTF8String, &statInfo) != 0) { - error = RCTErrorWithMessage(@"Error reading bundle"); - } else { - sourceLength = statInfo.st_size; - } - onComplete(error, source, sourceLength); - } else { - // Reading in a large bundle can be slow. Dispatch to the background queue to do it. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error = nil; - NSData *source = [NSData dataWithContentsOfFile:scriptURL.path - options:NSDataReadingMappedIfSafe - error:&error]; - onComplete(error, source, source.length); - }); + uint32_t magicNumber; + size_t readResult = fread(&magicNumber, sizeof(magicNumber), 1, bundle); + fclose(bundle); + if (readResult != 1) { + if (error) { + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorFailedReadingFile + userInfo:@{NSLocalizedDescriptionKey: + [NSString stringWithFormat:@"Error reading bundle %@", scriptURL.path]}]; } - return; + return nil; } - // Load remote script file - NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:scriptURL completionHandler: - ^(NSData *data, NSURLResponse *response, NSError *error) { - - // Handle general request errors - if (error) { - if ([error.domain isEqualToString:NSURLErrorDomain]) { - NSString *desc = [@"Could not connect to development server.\n\nEnsure the following:\n- Node server is running and available on the same network - run 'npm start' from react-native root\n- Node server URL is correctly set in AppDelegate\n\nURL: " stringByAppendingString:scriptURL.absoluteString]; - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: desc, - NSLocalizedFailureReasonErrorKey: error.localizedDescription, - NSUnderlyingErrorKey: error, - }; - error = [NSError errorWithDomain:@"JSServer" - code:error.code - userInfo:userInfo]; + magicNumber = NSSwapLittleIntToHost(magicNumber); + if (magicNumber != RCTRAMBundleMagicNumber) { + if (allowLoadingNonRAMBundles) { + NSData *source = [NSData dataWithContentsOfFile:scriptURL.path + options:NSDataReadingMappedIfSafe + error:error]; + if (sourceLength && source != nil) { + *sourceLength = source.length; } - onComplete(error, nil, 0); - return; + return source; } - // Parse response as text - NSStringEncoding encoding = NSUTF8StringEncoding; - if (response.textEncodingName != nil) { - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); - if (cfEncoding != kCFStringEncodingInvalidId) { - encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); - } + if (error) { + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously + userInfo:@{NSLocalizedDescriptionKey: + @"Cannot load non-RAM bundled files synchronously"}]; } - // Handle HTTP errors - if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode != 200) { - NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding]; - NSDictionary *userInfo; - NSDictionary *errorDetails = RCTJSONParse(rawText, nil); - if ([errorDetails isKindOfClass:[NSDictionary class]] && - [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) { - NSMutableArray *fakeStack = [NSMutableArray new]; - for (NSDictionary *err in errorDetails[@"errors"]) { - [fakeStack addObject: @{ - @"methodName": err[@"description"] ?: @"", - @"file": err[@"filename"] ?: @"", - @"lineNumber": err[@"lineNumber"] ?: @0 - }]; - } - userInfo = @{ - NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided", - @"stack": fakeStack, - }; - } else { - userInfo = @{NSLocalizedDescriptionKey: rawText}; - } - error = [NSError errorWithDomain:@"JSServer" - code:((NSHTTPURLResponse *)response).statusCode - userInfo:userInfo]; + return nil; + } - onComplete(error, nil, 0); - return; + struct stat statInfo; + if (stat(scriptURL.path.UTF8String, &statInfo) != 0) { + if (error) { + *error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorFailedStatingFile + userInfo:@{NSLocalizedDescriptionKey: + [NSString stringWithFormat:@"Error stating bundle %@", scriptURL.path]}]; } - onComplete(nil, data, data.length); - }]; + return nil; + } + if (sourceLength) { + *sourceLength = statInfo.st_size; + } + return [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)]; +} + +static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoadBlock onComplete) +{ + scriptURL = sanitizeURL(scriptURL); + + if (scriptURL.fileURL) { + // Reading in a large bundle can be slow. Dispatch to the background queue to do it. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSError *error = nil; + NSData *source = [NSData dataWithContentsOfFile:scriptURL.path + options:NSDataReadingMappedIfSafe + error:&error]; + onComplete(error, source, source.length); + }); + return; + } + // Load remote script file + NSURLSessionDataTask *task = + [[NSURLSession sharedSession] dataTaskWithURL:scriptURL completionHandler: + ^(NSData *data, NSURLResponse *response, NSError *error) { + + // Handle general request errors + if (error) { + if ([error.domain isEqualToString:NSURLErrorDomain]) { + error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorURLLoadFailed + userInfo: + @{ + NSLocalizedDescriptionKey: + [@"Could not connect to development server.\n\n" + "Ensure the following:\n" + "- Node server is running and available on the same network - run 'npm start' from react-native root\n" + "- Node server URL is correctly set in AppDelegate\n\n" + "URL: " stringByAppendingString:scriptURL.absoluteString], + NSLocalizedFailureReasonErrorKey: error.localizedDescription, + NSUnderlyingErrorKey: error, + }]; + } + onComplete(error, nil, 0); + return; + } + + // Parse response as text + NSStringEncoding encoding = NSUTF8StringEncoding; + if (response.textEncodingName != nil) { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (cfEncoding != kCFStringEncodingInvalidId) { + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + } + // Handle HTTP errors + if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode != 200) { + error = [NSError errorWithDomain:@"JSServer" + code:((NSHTTPURLResponse *)response).statusCode + userInfo:userInfoForRawResponse([[NSString alloc] initWithData:data encoding:encoding])]; + onComplete(error, nil, 0); + return; + } + onComplete(nil, data, data.length); + }]; [task resume]; } +static NSURL *sanitizeURL(NSURL *url) +{ + // Why we do this is lost to time. We probably shouldn't; passing a valid URL is the caller's responsibility not ours. + return [RCTConvert NSURL:url.absoluteString]; +} + +static NSDictionary *userInfoForRawResponse(NSString *rawText) +{ + NSDictionary *parsedResponse = RCTJSONParse(rawText, nil); + if (![parsedResponse isKindOfClass:[NSDictionary class]]) { + return @{NSLocalizedDescriptionKey: rawText}; + } + NSArray *errors = parsedResponse[@"errors"]; + if (![errors isKindOfClass:[NSArray class]]) { + return @{NSLocalizedDescriptionKey: rawText}; + } + NSMutableArray *fakeStack = [NSMutableArray new]; + for (NSDictionary *err in errors) { + [fakeStack addObject: + @{ + @"methodName": err[@"description"] ?: @"", + @"file": err[@"filename"] ?: @"", + @"lineNumber": err[@"lineNumber"] ?: @0 + }]; + } + return @{NSLocalizedDescriptionKey: parsedResponse[@"message"] ?: @"No message provided", @"stack": [fakeStack copy]}; +} + @end From 3be8c957b029b556daf9f0e0fa104bcb2e50a67f Mon Sep 17 00:00:00 2001 From: Adam Ernst Date: Tue, 12 Jul 2016 05:13:34 -0700 Subject: [PATCH 086/241] Minor cleanup to make loader unaware of bridge Summary: The loader should be a utility that stands alone. The bridge uses the loader, not the other way around. Reviewed By: bnham Differential Revision: D3546157 fbshipit-source-id: 91016afb629df1f8c83c8fca6f42649be0b046b0 --- React/Base/RCTBridgeDelegate.h | 4 ++-- React/Base/RCTJavaScriptLoader.h | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/React/Base/RCTBridgeDelegate.h b/React/Base/RCTBridgeDelegate.h index baa3f5db1c8fb8..02da39bdaeafb8 100644 --- a/React/Base/RCTBridgeDelegate.h +++ b/React/Base/RCTBridgeDelegate.h @@ -7,10 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source, int64_t sourceLength); - @class RCTBridge; +#import "RCTJavaScriptLoader.h" + @protocol RCTBridgeDelegate /** diff --git a/React/Base/RCTJavaScriptLoader.h b/React/Base/RCTJavaScriptLoader.h index bf51d31ae7159d..f98a7bdd2723a8 100755 --- a/React/Base/RCTJavaScriptLoader.h +++ b/React/Base/RCTJavaScriptLoader.h @@ -9,8 +9,6 @@ #import -#import "RCTBridgeDelegate.h" - extern uint32_t const RCTRAMBundleMagicNumber; extern NSString *const RCTJavaScriptLoaderErrorDomain; @@ -25,13 +23,8 @@ NS_ENUM(NSInteger) { RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously = 1000, }; -@class RCTBridge; +typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source, int64_t sourceLength); -/** - * Class that allows easy embedding, loading, life-cycle management of a - * JavaScript application inside of a native application. - * TODO: Incremental module loading. (low pri). - */ @interface RCTJavaScriptLoader : NSObject + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComplete; From 7b7ecdf3376219e6c1089f3822d9b8674c401ac7 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Tue, 12 Jul 2016 05:51:56 -0700 Subject: [PATCH 087/241] Cleanup warnings Reviewed By: majak Differential Revision: D3542606 fbshipit-source-id: 41246a012a32fafc4ddbb307c7b9919e3c203393 --- React/Base/RCTBatchedBridge.m | 15 ++++++++++----- React/Base/RCTBridge+Private.h | 6 ++++++ React/Base/RCTBridge.h | 4 ++-- React/Base/RCTBridge.m | 5 +---- React/Base/RCTKeyCommands.m | 2 +- React/Base/RCTPerformanceLogger.m | 2 +- React/Modules/RCTRedBox.m | 6 +++--- React/Modules/RCTTiming.m | 1 - React/Modules/RCTUIManager.m | 6 +++--- React/Views/RCTMapAnnotation.h | 8 ++++---- React/Views/RCTNavigator.m | 2 +- React/Views/UIView+React.h | 9 +++++---- 12 files changed, 37 insertions(+), 29 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 527c882ecb46b6..bd8ff7b41b22e5 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -62,10 +62,10 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge { RCTAssertParam(bridge); - if ((self = [super initWithBundleURL:bridge.bundleURL - moduleProvider:bridge.moduleProvider - launchOptions:bridge.launchOptions])) { - + if (self = [super initWithDelegate:bridge.delegate + bundleURL:bridge.bundleURL + moduleProvider:bridge.moduleProvider + launchOptions:bridge.launchOptions]) { _parentBridge = bridge; /** @@ -87,6 +87,11 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge return self; } +RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id)delegate + bundleURL:(NSURL *)bundleURL + moduleProvider:(RCTBridgeModuleProviderBlock)block + launchOptions:(NSDictionary *)launchOptions) + - (void)start { dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT); @@ -97,7 +102,7 @@ - (void)start dispatch_group_enter(initModulesAndLoadSource); __weak RCTBatchedBridge *weakSelf = self; __block NSData *sourceCode; - [self loadSource:^(NSError *error, NSData *source, int64_t sourceLength) { + [self loadSource:^(NSError *error, NSData *source, __unused int64_t sourceLength) { if (error) { dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf stopLoadingWithError:error]; diff --git a/React/Base/RCTBridge+Private.h b/React/Base/RCTBridge+Private.h index 08dd70557b03a4..14c3d6ab411ee5 100644 --- a/React/Base/RCTBridge+Private.h +++ b/React/Base/RCTBridge+Private.h @@ -18,6 +18,12 @@ RCTPerformanceLogger *_performanceLogger; } +// Private designated initializer +- (instancetype)initWithDelegate:(id)delegate + bundleURL:(NSURL *)bundleURL + moduleProvider:(RCTBridgeModuleProviderBlock)block + launchOptions:(NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; + // Used for the profiler flow events between JS and native @property (nonatomic, assign) int64_t flowID; @property (nonatomic, assign) CFMutableDictionaryRef flowIDMap; diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 956244ca67c56d..c6db0eca53d8cc 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -79,7 +79,7 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); * or configuration. */ - (instancetype)initWithDelegate:(id)delegate - launchOptions:(NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; + launchOptions:(NSDictionary *)launchOptions; /** * DEPRECATED: Use initWithDelegate:launchOptions: instead @@ -93,7 +93,7 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); */ - (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleProvider:(RCTBridgeModuleProviderBlock)block - launchOptions:(NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; + launchOptions:(NSDictionary *)launchOptions; /** * This method is used to call functions in the JavaScript application context. diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 775ddff9b64062..41f57d080158c3 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -126,15 +126,12 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL launchOptions:launchOptions]; } -/** - * Private designated initializer - */ - (instancetype)initWithDelegate:(id)delegate bundleURL:(NSURL *)bundleURL moduleProvider:(RCTBridgeModuleProviderBlock)block launchOptions:(NSDictionary *)launchOptions { - if ((self = [super init])) { + if (self = [super init]) { _performanceLogger = [RCTPerformanceLogger new]; [_performanceLogger markStartForTag:RCTPLBridgeStartup]; [_performanceLogger markStartForTag:RCTPLTTI]; diff --git a/React/Base/RCTKeyCommands.m b/React/Base/RCTKeyCommands.m index 49465c4c62c86c..93c21d08f20281 100644 --- a/React/Base/RCTKeyCommands.m +++ b/React/Base/RCTKeyCommands.m @@ -144,7 +144,7 @@ - (void)RCT_handleDoublePressKeyCommand:(UIKeyCommand *)key static NSTimeInterval lastCommand = 0; static NSTimeInterval lastDoubleCommand = 0; static NSString *lastInput = nil; - static UIKeyModifierFlags lastModifierFlags = nil; + static UIKeyModifierFlags lastModifierFlags = 0; if (firstPress) { for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { diff --git a/React/Base/RCTPerformanceLogger.m b/React/Base/RCTPerformanceLogger.m index 5046990214ef87..c1f990f41f5e23 100644 --- a/React/Base/RCTPerformanceLogger.m +++ b/React/Base/RCTPerformanceLogger.m @@ -118,7 +118,7 @@ - (int64_t)durationForTag:(RCTPLTag)tag return _data[tag][1] - _data[tag][0]; } -- (int64_t)valueForTag:(RCTPLTag)tag; +- (int64_t)valueForTag:(RCTPLTag)tag { return _data[tag][1]; } diff --git a/React/Modules/RCTRedBox.m b/React/Modules/RCTRedBox.m index 125b19f7381f94..34b47b90781527 100644 --- a/React/Modules/RCTRedBox.m +++ b/React/Modules/RCTRedBox.m @@ -383,7 +383,7 @@ - (void)invalidate [self dismiss]; } -- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame +- (void)redBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame { if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) { RCTLogWarn(@"Cannot open stack frame in editor because you're not connected to the packager."); @@ -402,7 +402,7 @@ - (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCT [[[NSURLSession sharedSession] dataTaskWithRequest:request] resume]; } -- (void)reloadFromRedBoxWindow:(RCTRedBoxWindow *)redBoxWindow { +- (void)reloadFromRedBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow { [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil]; } @@ -428,7 +428,7 @@ - (void)showErrorMessage:(NSString *)message withDetails:(NSString *)details {} - (void)showErrorMessage:(NSString *)message withRawStack:(NSString *)rawStack {} - (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack {} - (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack {} -- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow {} +- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate {} - (void)dismiss {} @end diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index ac6c53c77b8a12..a4038f1b9b6464 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -168,7 +168,6 @@ - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update NSDate *nextScheduledTarget = [NSDate distantFuture]; NSMutableArray *timersToCall = [NSMutableArray new]; for (_RCTTimer *timer in _timers.allValues) { - NSDate *target = timer.target; if ([timer updateFoundNeedsJSUpdate]) { [timersToCall addObject:timer.callbackID]; } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 2ad81d6d35cdc0..af667f2e8264d7 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -558,11 +558,11 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView * } RCTFrameData; // Construct arrays then hand off to main thread - NSInteger count = viewsWithNewFrames.count; + NSUInteger count = viewsWithNewFrames.count; NSMutableArray *reactTags = [[NSMutableArray alloc] initWithCapacity:count]; NSMutableData *framesData = [[NSMutableData alloc] initWithLength:sizeof(RCTFrameData) * count]; { - NSInteger index = 0; + NSUInteger index = 0; RCTFrameData *frameDataArray = (RCTFrameData *)framesData.mutableBytes; for (RCTShadowView *shadowView in viewsWithNewFrames) { reactTags[index] = shadowView.reactTag; @@ -849,7 +849,7 @@ - (void)_removeChildren:(NSArray *)children UIView *rootView = viewRegistry[rootReactTag]; [uiManager _purgeChildren:(NSArray> *)rootView.reactSubviews fromRegistry:(NSMutableDictionary> *)viewRegistry]; - [(NSMutableDictionary *)viewRegistry removeObjectForKey:rootReactTag]; + [(NSMutableDictionary *)viewRegistry removeObjectForKey:rootReactTag]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTUIManagerDidRemoveRootViewNotification object:uiManager diff --git a/React/Views/RCTMapAnnotation.h b/React/Views/RCTMapAnnotation.h index ec5b4edc32adcf..b273bfb093d0c6 100644 --- a/React/Views/RCTMapAnnotation.h +++ b/React/Views/RCTMapAnnotation.h @@ -17,10 +17,10 @@ @property (nonatomic, assign) BOOL animateDrop; @property (nonatomic, strong) UIColor *tintColor; @property (nonatomic, strong) UIImage *image; -@property (nonatomic, assign) NSInteger viewIndex; -@property (nonatomic, assign) NSInteger leftCalloutViewIndex; -@property (nonatomic, assign) NSInteger rightCalloutViewIndex; -@property (nonatomic, assign) NSInteger detailCalloutViewIndex; +@property (nonatomic, assign) NSUInteger viewIndex; +@property (nonatomic, assign) NSUInteger leftCalloutViewIndex; +@property (nonatomic, assign) NSUInteger rightCalloutViewIndex; +@property (nonatomic, assign) NSUInteger detailCalloutViewIndex; @property (nonatomic, assign) BOOL draggable; @end diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index 64de186799bbe7..5a987e63aa75f1 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -370,7 +370,7 @@ - (UIViewController *)reactViewController return _navigationController; } -- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +- (BOOL)gestureRecognizerShouldBegin:(__unused UIGestureRecognizer *)gestureRecognizer { return _navigationController.viewControllers.count > 1; } diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index 600a0b34772fba..6533890c402f9a 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -9,11 +9,9 @@ #import -@class RCTShadowView; - #import "RCTComponent.h" -//TODO: let's try to eliminate this category if possible +@class RCTShadowView; @interface UIView (React) @@ -73,11 +71,14 @@ - (void)reactDidMakeFirstResponder; - (BOOL)reactRespondsToTouch:(UITouch *)touch; +#if RCT_DEV + /** Tools for debugging */ -#if RCT_DEV + @property (nonatomic, strong, setter=_DEBUG_setReactShadowView:) RCTShadowView *_DEBUG_reactShadowView; + #endif @end From 2f73ca8f7675977f839ad5bfcb5045ff33621803 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Tue, 12 Jul 2016 05:51:57 -0700 Subject: [PATCH 088/241] Cleanup UIExplorer folder Summary: Move all JS to a js/ subfolder so we get some overview of this folder again. Reviewed By: bestander Differential Revision: D3542598 fbshipit-source-id: 7637133fe4152f4d39e461b443b38510272d5bc8 --- .../UIExplorer.xcodeproj/project.pbxproj | 24 +++++++++--------- Examples/UIExplorer/UIExplorer/AppDelegate.m | 5 ++-- .../RCTLoggingTests.m | 16 +++++++++--- .../testLayoutExample_1@2x.png | Bin .../testSliderExample_1@2x.png | Bin .../testSwitchExample_1@2x.png | Bin .../testTabBarExample_1@2x.png | Bin .../testTextExample_1@2x.png | Bin .../testViewExample_1@2x.png | Bin .../UIExplorerSnapshotTests.m | 2 +- .../{ => UIExplorerUnitTests}/TestBundle.js | 0 Examples/UIExplorer/android/app/build.gradle | 2 +- .../react/uiapp/UIExplorerApplication.java | 2 +- .../AccessibilityAndroidExample.android.js | 7 +++++ .../{ => js}/AccessibilityIOSExample.js | 7 +++++ .../{ => js}/ActionSheetIOSExample.js | 7 +++++ .../{ => js}/ActivityIndicatorExample.js | 7 +++++ .../{ => js}/AdSupportIOSExample.js | 7 +++++ Examples/UIExplorer/{ => js}/AlertExample.js | 7 +++++ .../UIExplorer/{ => js}/AlertIOSExample.js | 7 +++++ .../UIExplorer/{ => js}/AnimatedExample.js | 7 +++++ .../{ => js}/AnimatedGratuitousApp/AnExApp.js | 7 +++++ .../AnimatedGratuitousApp/AnExBobble.js | 7 +++++ .../AnimatedGratuitousApp/AnExChained.js | 7 +++++ .../AnimatedGratuitousApp/AnExScroll.js | 7 +++++ .../{ => js}/AnimatedGratuitousApp/AnExSet.js | 7 +++++ .../AnimatedGratuitousApp/AnExSlides.md | 0 .../AnimatedGratuitousApp/AnExTilt.js | 7 +++++ .../UIExplorer/{ => js}/AppStateExample.js | 0 .../{ => js}/AssetScaledImageExample.js | 7 +++++ .../{ => js}/AsyncStorageExample.js | 7 +++++ Examples/UIExplorer/{ => js}/BorderExample.js | 7 +++++ .../UIExplorer/{ => js}/BoxShadowExample.js | 7 +++++ .../UIExplorer/{ => js}/CameraRollExample.js | 7 +++++ .../UIExplorer/{ => js}/CameraRollView.js | 7 +++++ .../UIExplorer/{ => js}/ClipboardExample.js | 7 +++++ .../{ => js}/DatePickerAndroidExample.js | 7 +++++ .../{ => js}/DatePickerIOSExample.js | 7 +++++ Examples/UIExplorer/{ => js}/ExampleTypes.js | 7 +++++ .../UIExplorer/{ => js}/GeolocationExample.js | 7 +++++ .../{ => js}/ImageCapInsetsExample.js | 7 +++++ .../{ => js}/ImageEditingExample.js | 10 ++++++-- Examples/UIExplorer/{ => js}/ImageExample.js | 0 .../{ => js}/KeyboardAvoidingViewExample.js | 0 .../{ => js}/LayoutAnimationExample.js | 7 +++++ .../{ => js}/LayoutEventsExample.js | 7 +++++ Examples/UIExplorer/{ => js}/LayoutExample.js | 7 +++++ .../UIExplorer/{ => js}/LinkingExample.js | 7 +++++ .../UIExplorer/{ => js}/ListViewExample.js | 7 +++++ .../{ => js}/ListViewGridLayoutExample.js | 7 +++++ .../{ => js}/ListViewPagingExample.js | 7 +++++ .../UIExplorer/{ => js}/MapViewExample.js | 7 +++++ Examples/UIExplorer/{ => js}/ModalExample.js | 7 +++++ .../{ => js}/NativeAnimationsExample.js | 0 ...CardStack-NavigationHeader-Tabs-example.js | 0 .../NavigationCardStack-example.js | 0 .../NavigationExampleRow.js | 7 +++++ .../NavigationExperimentalExample.js | 0 ...gationTransitioner-AnimatedView-example.js | 0 ...Transitioner-AnimatedView-pager-example.js | 0 .../{ => js}/Navigator/BreadcrumbNavSample.js | 7 +++++ .../{ => js}/Navigator/JumpingNavSample.js | 7 +++++ .../{ => js}/Navigator/NavigationBarSample.js | 7 +++++ .../{ => js}/Navigator/NavigatorExample.js | 7 +++++ .../{ => js}/NavigatorIOSColorsExample.js | 7 +++++ .../{ => js}/NavigatorIOSExample.js | 0 .../UIExplorer/{ => js}/NetInfoExample.js | 7 +++++ .../{ => js}/PanResponderExample.js | 0 .../PermissionsExampleAndroid.android.js | 0 Examples/UIExplorer/{ => js}/PickerExample.js | 7 +++++ .../UIExplorer/{ => js}/PickerIOSExample.js | 7 +++++ .../{ => js}/PointerEventsExample.js | 7 +++++ .../ProgressBarAndroidExample.android.js | 7 +++++ .../{ => js}/ProgressViewIOSExample.js | 7 +++++ .../{ => js}/PushNotificationIOSExample.js | 7 +++++ .../{ => js}/RCTRootViewIOSExample.js | 7 +++++ .../{ => js}/RefreshControlExample.js | 7 +++++ .../RootViewSizeFlexibilityExampleApp.js | 7 +++++ .../UIExplorer/{ => js}/ScrollViewExample.js | 7 +++++ .../{ => js}/ScrollViewSimpleExample.js | 7 +++++ .../{ => js}/SegmentedControlIOSExample.js | 7 +++++ .../{ => js}/SetPropertiesExampleApp.js | 7 +++++ Examples/UIExplorer/{ => js}/SliderExample.js | 7 +++++ .../UIExplorer/{ => js}/SliderIOSExample.js | 7 +++++ .../UIExplorer/{ => js}/SnapshotExample.js | 7 +++++ .../UIExplorer/{ => js}/StatusBarExample.js | 7 +++++ Examples/UIExplorer/{ => js}/SwitchExample.js | 7 +++++ .../UIExplorer/{ => js}/TabBarIOSExample.js | 7 +++++ .../{ => js}/TextExample.android.js | 7 +++++ .../UIExplorer/{ => js}/TextExample.ios.js | 0 .../{ => js}/TextInputExample.android.js | 7 +++++ .../{ => js}/TextInputExample.ios.js | 7 +++++ .../{ => js}/Thumbnails/bandaged.png | Bin .../UIExplorer/{ => js}/Thumbnails/call.png | Bin .../{ => js}/Thumbnails/dislike.png | Bin .../UIExplorer/{ => js}/Thumbnails/fist.png | Bin .../{ => js}/Thumbnails/flowers.png | Bin .../UIExplorer/{ => js}/Thumbnails/heart.png | Bin .../UIExplorer/{ => js}/Thumbnails/like.png | Bin .../UIExplorer/{ => js}/Thumbnails/liking.png | Bin .../UIExplorer/{ => js}/Thumbnails/party.png | Bin .../UIExplorer/{ => js}/Thumbnails/poke.png | Bin .../{ => js}/Thumbnails/superlike.png | Bin .../{ => js}/Thumbnails/victory.png | Bin .../{ => js}/TimePickerAndroidExample.js | 7 +++++ Examples/UIExplorer/{ => js}/TimerExample.js | 7 +++++ .../{ => js}/ToastAndroidExample.android.js | 7 +++++ .../{ => js}/ToolbarAndroidExample.android.js | 7 +++++ .../UIExplorer/{ => js}/TouchableExample.js | 7 +++++ .../UIExplorer/{ => js}/TransformExample.js | 7 +++++ .../{ => js}/TransparentHitTestExample.js | 0 .../UIExplorer/{ => js}/UIExplorerActions.js | 0 .../{ => js}/UIExplorerApp.android.js | 0 .../UIExplorer/{ => js}/UIExplorerApp.ios.js | 0 .../UIExplorer/{ => js}/UIExplorerBlock.js | 7 +++++ .../UIExplorer/{ => js}/UIExplorerButton.js | 7 +++++ .../{ => js}/UIExplorerExampleList.js | 0 .../{ => js}/UIExplorerList.android.js | 0 .../UIExplorer/{ => js}/UIExplorerList.ios.js | 0 .../{ => js}/UIExplorerNavigationReducer.js | 0 .../UIExplorer/{ => js}/UIExplorerPage.js | 7 +++++ .../{ => js}/UIExplorerStateTitleMap.js | 0 .../UIExplorer/{ => js}/UIExplorerTitle.js | 7 +++++ Examples/UIExplorer/{ => js}/URIActionMap.js | 7 +++++ .../UIExplorer/{ => js}/VibrationExample.js | 7 +++++ .../{ => js}/VibrationIOSExample.js | 7 +++++ Examples/UIExplorer/{ => js}/ViewExample.js | 0 .../ViewPagerAndroidExample.android.js | 0 .../UIExplorer/{ => js}/WebSocketExample.js | 0 .../UIExplorer/{ => js}/WebViewExample.js | 7 +++++ .../UIExplorer/{ => js}/XHRExample.android.js | 7 +++++ .../UIExplorer/{ => js}/XHRExample.ios.js | 0 .../UIExplorer/{ => js}/XHRExampleCookies.js | 7 +++++ .../UIExplorer/{ => js}/XHRExampleFetch.js | 7 +++++ .../UIExplorer/{ => js}/XHRExampleHeaders.js | 7 +++++ .../{ => js}/XHRExampleOnTimeOut.js | 7 +++++ Examples/UIExplorer/{ => js}/bunny.png | Bin .../UIExplorer/{ => js}/createExamplePage.js | 7 +++++ Examples/UIExplorer/{ => js}/flux@3x.png | Bin Examples/UIExplorer/{ => js}/hawk.png | Bin Examples/UIExplorer/{ => js}/helloworld.html | 0 Examples/UIExplorer/{ => js}/relay@3x.png | Bin Examples/UIExplorer/{ => js}/slider-left.png | Bin .../UIExplorer/{ => js}/slider-left@2x.png | Bin Examples/UIExplorer/{ => js}/slider-right.png | Bin .../UIExplorer/{ => js}/slider-right@2x.png | Bin Examples/UIExplorer/{ => js}/slider.png | Bin Examples/UIExplorer/{ => js}/slider@2x.png | Bin .../{ => js}/uie_comment_highlighted@2x.png | Bin .../{ => js}/uie_comment_normal@2x.png | Bin .../UIExplorer/{ => js}/uie_thumb_big.png | Bin .../{ => js}/uie_thumb_normal@2x.png | Bin .../{ => js}/uie_thumb_selected@2x.png | Bin .../{ => js}/websocket_test_server.js | 7 +++++ 154 files changed, 633 insertions(+), 23 deletions(-) rename Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/{Examples-UIExplorer-UIExplorerApp.ios => Examples-UIExplorer-js-UIExplorerApp.ios}/testLayoutExample_1@2x.png (100%) rename Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/{Examples-UIExplorer-UIExplorerApp.ios => Examples-UIExplorer-js-UIExplorerApp.ios}/testSliderExample_1@2x.png (100%) rename Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/{Examples-UIExplorer-UIExplorerApp.ios => Examples-UIExplorer-js-UIExplorerApp.ios}/testSwitchExample_1@2x.png (100%) rename Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/{Examples-UIExplorer-UIExplorerApp.ios => Examples-UIExplorer-js-UIExplorerApp.ios}/testTabBarExample_1@2x.png (100%) rename Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/{Examples-UIExplorer-UIExplorerApp.ios => Examples-UIExplorer-js-UIExplorerApp.ios}/testTextExample_1@2x.png (100%) rename Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/{Examples-UIExplorer-UIExplorerApp.ios => Examples-UIExplorer-js-UIExplorerApp.ios}/testViewExample_1@2x.png (100%) rename Examples/UIExplorer/{ => UIExplorerUnitTests}/TestBundle.js (100%) rename Examples/UIExplorer/{ => js}/AccessibilityAndroidExample.android.js (95%) rename Examples/UIExplorer/{ => js}/AccessibilityIOSExample.js (86%) rename Examples/UIExplorer/{ => js}/ActionSheetIOSExample.js (94%) rename Examples/UIExplorer/{ => js}/ActivityIndicatorExample.js (92%) rename Examples/UIExplorer/{ => js}/AdSupportIOSExample.js (88%) rename Examples/UIExplorer/{ => js}/AlertExample.js (92%) rename Examples/UIExplorer/{ => js}/AlertIOSExample.js (93%) rename Examples/UIExplorer/{ => js}/AnimatedExample.js (95%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExApp.js (97%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExBobble.js (94%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExChained.js (93%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExScroll.js (92%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExSet.js (93%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExSlides.md (100%) rename Examples/UIExplorer/{ => js}/AnimatedGratuitousApp/AnExTilt.js (93%) rename Examples/UIExplorer/{ => js}/AppStateExample.js (100%) rename Examples/UIExplorer/{ => js}/AssetScaledImageExample.js (88%) rename Examples/UIExplorer/{ => js}/AsyncStorageExample.js (91%) rename Examples/UIExplorer/{ => js}/BorderExample.js (95%) rename Examples/UIExplorer/{ => js}/BoxShadowExample.js (88%) rename Examples/UIExplorer/{ => js}/CameraRollExample.js (92%) rename Examples/UIExplorer/{ => js}/CameraRollView.js (95%) rename Examples/UIExplorer/{ => js}/ClipboardExample.js (84%) rename Examples/UIExplorer/{ => js}/DatePickerAndroidExample.js (93%) rename Examples/UIExplorer/{ => js}/DatePickerIOSExample.js (93%) rename Examples/UIExplorer/{ => js}/ExampleTypes.js (75%) rename Examples/UIExplorer/{ => js}/GeolocationExample.js (88%) rename Examples/UIExplorer/{ => js}/ImageCapInsetsExample.js (87%) rename Examples/UIExplorer/{ => js}/ImageEditingExample.js (96%) rename Examples/UIExplorer/{ => js}/ImageExample.js (100%) rename Examples/UIExplorer/{ => js}/KeyboardAvoidingViewExample.js (100%) rename Examples/UIExplorer/{ => js}/LayoutAnimationExample.js (92%) rename Examples/UIExplorer/{ => js}/LayoutEventsExample.js (93%) rename Examples/UIExplorer/{ => js}/LayoutExample.js (95%) rename Examples/UIExplorer/{ => js}/LinkingExample.js (88%) rename Examples/UIExplorer/{ => js}/ListViewExample.js (93%) rename Examples/UIExplorer/{ => js}/ListViewGridLayoutExample.js (93%) rename Examples/UIExplorer/{ => js}/ListViewPagingExample.js (96%) rename Examples/UIExplorer/{ => js}/MapViewExample.js (97%) rename Examples/UIExplorer/{ => js}/ModalExample.js (94%) rename Examples/UIExplorer/{ => js}/NativeAnimationsExample.js (100%) rename Examples/UIExplorer/{ => js}/NavigationExperimental/NavigationCardStack-NavigationHeader-Tabs-example.js (100%) rename Examples/UIExplorer/{ => js}/NavigationExperimental/NavigationCardStack-example.js (100%) rename Examples/UIExplorer/{ => js}/NavigationExperimental/NavigationExampleRow.js (84%) rename Examples/UIExplorer/{ => js}/NavigationExperimental/NavigationExperimentalExample.js (100%) rename Examples/UIExplorer/{ => js}/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js (100%) rename Examples/UIExplorer/{ => js}/NavigationExperimental/NavigationTransitioner-AnimatedView-pager-example.js (100%) rename Examples/UIExplorer/{ => js}/Navigator/BreadcrumbNavSample.js (92%) rename Examples/UIExplorer/{ => js}/Navigator/JumpingNavSample.js (94%) rename Examples/UIExplorer/{ => js}/Navigator/NavigationBarSample.js (94%) rename Examples/UIExplorer/{ => js}/Navigator/NavigatorExample.js (94%) rename Examples/UIExplorer/{ => js}/NavigatorIOSColorsExample.js (87%) rename Examples/UIExplorer/{ => js}/NavigatorIOSExample.js (100%) rename Examples/UIExplorer/{ => js}/NetInfoExample.js (93%) rename Examples/UIExplorer/{ => js}/PanResponderExample.js (100%) rename Examples/UIExplorer/{ => js}/PermissionsExampleAndroid.android.js (100%) rename Examples/UIExplorer/{ => js}/PickerExample.js (93%) rename Examples/UIExplorer/{ => js}/PickerIOSExample.js (93%) rename Examples/UIExplorer/{ => js}/PointerEventsExample.js (95%) rename Examples/UIExplorer/{ => js}/ProgressBarAndroidExample.android.js (88%) rename Examples/UIExplorer/{ => js}/ProgressViewIOSExample.js (88%) rename Examples/UIExplorer/{ => js}/PushNotificationIOSExample.js (93%) rename Examples/UIExplorer/{ => js}/RCTRootViewIOSExample.js (90%) rename Examples/UIExplorer/{ => js}/RefreshControlExample.js (91%) rename Examples/UIExplorer/{ => js}/RootViewSizeFlexibilityExampleApp.js (86%) rename Examples/UIExplorer/{ => js}/ScrollViewExample.js (94%) rename Examples/UIExplorer/{ => js}/ScrollViewSimpleExample.js (87%) rename Examples/UIExplorer/{ => js}/SegmentedControlIOSExample.js (93%) rename Examples/UIExplorer/{ => js}/SetPropertiesExampleApp.js (79%) rename Examples/UIExplorer/{ => js}/SliderExample.js (92%) rename Examples/UIExplorer/{ => js}/SliderIOSExample.js (90%) rename Examples/UIExplorer/{ => js}/SnapshotExample.js (85%) rename Examples/UIExplorer/{ => js}/StatusBarExample.js (97%) rename Examples/UIExplorer/{ => js}/SwitchExample.js (93%) rename Examples/UIExplorer/{ => js}/TabBarIOSExample.js (93%) rename Examples/UIExplorer/{ => js}/TextExample.android.js (98%) rename Examples/UIExplorer/{ => js}/TextExample.ios.js (100%) rename Examples/UIExplorer/{ => js}/TextInputExample.android.js (98%) rename Examples/UIExplorer/{ => js}/TextInputExample.ios.js (98%) rename Examples/UIExplorer/{ => js}/Thumbnails/bandaged.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/call.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/dislike.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/fist.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/flowers.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/heart.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/like.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/liking.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/party.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/poke.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/superlike.png (100%) rename Examples/UIExplorer/{ => js}/Thumbnails/victory.png (100%) rename Examples/UIExplorer/{ => js}/TimePickerAndroidExample.js (92%) rename Examples/UIExplorer/{ => js}/TimerExample.js (95%) rename Examples/UIExplorer/{ => js}/ToastAndroidExample.android.js (87%) rename Examples/UIExplorer/{ => js}/ToolbarAndroidExample.android.js (93%) rename Examples/UIExplorer/{ => js}/TouchableExample.js (97%) rename Examples/UIExplorer/{ => js}/TransformExample.js (95%) rename Examples/UIExplorer/{ => js}/TransparentHitTestExample.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerActions.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerApp.android.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerApp.ios.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerBlock.js (88%) rename Examples/UIExplorer/{ => js}/UIExplorerButton.js (82%) rename Examples/UIExplorer/{ => js}/UIExplorerExampleList.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerList.android.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerList.ios.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerNavigationReducer.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerPage.js (87%) rename Examples/UIExplorer/{ => js}/UIExplorerStateTitleMap.js (100%) rename Examples/UIExplorer/{ => js}/UIExplorerTitle.js (81%) rename Examples/UIExplorer/{ => js}/URIActionMap.js (85%) rename Examples/UIExplorer/{ => js}/VibrationExample.js (89%) rename Examples/UIExplorer/{ => js}/VibrationIOSExample.js (82%) rename Examples/UIExplorer/{ => js}/ViewExample.js (100%) rename Examples/UIExplorer/{ => js}/ViewPagerAndroidExample.android.js (100%) rename Examples/UIExplorer/{ => js}/WebSocketExample.js (100%) rename Examples/UIExplorer/{ => js}/WebViewExample.js (96%) rename Examples/UIExplorer/{ => js}/XHRExample.android.js (97%) rename Examples/UIExplorer/{ => js}/XHRExample.ios.js (100%) rename Examples/UIExplorer/{ => js}/XHRExampleCookies.js (92%) rename Examples/UIExplorer/{ => js}/XHRExampleFetch.js (89%) rename Examples/UIExplorer/{ => js}/XHRExampleHeaders.js (90%) rename Examples/UIExplorer/{ => js}/XHRExampleOnTimeOut.js (89%) rename Examples/UIExplorer/{ => js}/bunny.png (100%) rename Examples/UIExplorer/{ => js}/createExamplePage.js (90%) rename Examples/UIExplorer/{ => js}/flux@3x.png (100%) rename Examples/UIExplorer/{ => js}/hawk.png (100%) rename Examples/UIExplorer/{ => js}/helloworld.html (100%) rename Examples/UIExplorer/{ => js}/relay@3x.png (100%) rename Examples/UIExplorer/{ => js}/slider-left.png (100%) rename Examples/UIExplorer/{ => js}/slider-left@2x.png (100%) rename Examples/UIExplorer/{ => js}/slider-right.png (100%) rename Examples/UIExplorer/{ => js}/slider-right@2x.png (100%) rename Examples/UIExplorer/{ => js}/slider.png (100%) rename Examples/UIExplorer/{ => js}/slider@2x.png (100%) rename Examples/UIExplorer/{ => js}/uie_comment_highlighted@2x.png (100%) rename Examples/UIExplorer/{ => js}/uie_comment_normal@2x.png (100%) rename Examples/UIExplorer/{ => js}/uie_thumb_big.png (100%) rename Examples/UIExplorer/{ => js}/uie_thumb_normal@2x.png (100%) rename Examples/UIExplorer/{ => js}/uie_thumb_selected@2x.png (100%) rename Examples/UIExplorer/{ => js}/websocket_test_server.js (81%) diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index a23d3d532e4c12..aa4bcf20a5ef6c 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1861C04AB9F0091BED0 /* flux@3x.png */; }; 1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1871C04AB9F0091BED0 /* hawk.png */; }; 1323F18C1C04AB9F0091BED0 /* uie_thumb_big.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1881C04AB9F0091BED0 /* uie_thumb_big.png */; }; - 1323F18F1C04ABEB0091BED0 /* Thumbnails in Resources */ = {isa = PBXBuildFile; fileRef = 1323F18E1C04ABEB0091BED0 /* Thumbnails */; }; 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FE81AA91428003F314A /* libRCTImage.a */; }; 134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; }; 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; }; @@ -63,8 +62,9 @@ 27B885561BED29AF00008352 /* RCTRootViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */; }; 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */; }; 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; + 3D299BAF1D33EBFA00FA1057 /* RCTLoggingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D299BAE1D33EBFA00FA1057 /* RCTLoggingTests.m */; }; 3DB99D0C1BA0340600302749 /* UIExplorerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */; }; - 3DEFCE371D33A67200256E76 /* TestBundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 3DEFCE351D33A61500256E76 /* TestBundle.js */; }; + 3DD981D61D33C6FB007DC7BE /* TestBundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 3DD981D51D33C6FB007DC7BE /* TestBundle.js */; }; 68FF44381CF6111500720EFD /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; @@ -192,11 +192,10 @@ 004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = ""; }; 13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitNotificationRaceTests.m; sourceTree = ""; }; - 1323F1851C04AB9F0091BED0 /* bunny.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bunny.png; sourceTree = ""; }; - 1323F1861C04AB9F0091BED0 /* flux@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flux@3x.png"; sourceTree = ""; }; - 1323F1871C04AB9F0091BED0 /* hawk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = hawk.png; sourceTree = ""; }; - 1323F1881C04AB9F0091BED0 /* uie_thumb_big.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = uie_thumb_big.png; sourceTree = ""; }; - 1323F18E1C04ABEB0091BED0 /* Thumbnails */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Thumbnails; sourceTree = ""; }; + 1323F1851C04AB9F0091BED0 /* bunny.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = bunny.png; path = js/bunny.png; sourceTree = ""; }; + 1323F1861C04AB9F0091BED0 /* flux@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "flux@3x.png"; path = "js/flux@3x.png"; sourceTree = ""; }; + 1323F1871C04AB9F0091BED0 /* hawk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = hawk.png; path = js/hawk.png; sourceTree = ""; }; + 1323F1881C04AB9F0091BED0 /* uie_thumb_big.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = uie_thumb_big.png; path = js/uie_thumb_big.png; sourceTree = ""; }; 13417FE31AA91428003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; 134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; @@ -251,8 +250,9 @@ 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FlexibleSizeExampleView.m; path = UIExplorer/NativeExampleViews/FlexibleSizeExampleView.m; sourceTree = ""; }; 27F441EA1BEBE5030039B79C /* FlexibleSizeExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexibleSizeExampleView.h; path = UIExplorer/NativeExampleViews/FlexibleSizeExampleView.h; sourceTree = ""; }; 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; + 3D299BAE1D33EBFA00FA1057 /* RCTLoggingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLoggingTests.m; sourceTree = ""; }; 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerIntegrationTests.m; sourceTree = ""; }; - 3DEFCE351D33A61500256E76 /* TestBundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = TestBundle.js; sourceTree = ""; }; + 3DD981D51D33C6FB007DC7BE /* TestBundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = TestBundle.js; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBundleURLProviderTests.m; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; @@ -340,7 +340,6 @@ 1323F18D1C04ABAC0091BED0 /* Supporting Files */ = { isa = PBXGroup; children = ( - 3DEFCE351D33A61500256E76 /* TestBundle.js */, 13B07FB61A68108700A75B9A /* Info.plist */, 1323F1851C04AB9F0091BED0 /* bunny.png */, 1323F1861C04AB9F0091BED0 /* flux@3x.png */, @@ -415,7 +414,6 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB71A68108700A75B9A /* main.m */, - 1323F18E1C04ABEB0091BED0 /* Thumbnails */, 1323F18D1C04ABAC0091BED0 /* Supporting Files */, ); name = UIExplorer; @@ -454,6 +452,7 @@ 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */, 13BCE84E1C9C209600DD7AAD /* RCTComponentPropsTests.m */, 143BC57E1B21E18100462512 /* Info.plist */, + 3DD981D51D33C6FB007DC7BE /* TestBundle.js */, 14D6D7101B220EB3001FB087 /* libOCMock.a */, 14D6D7011B220AE3001FB087 /* OCMock */, ); @@ -463,6 +462,7 @@ 143BC5961B21E3E100462512 /* UIExplorerIntegrationTests */ = { isa = PBXGroup; children = ( + 3D299BAE1D33EBFA00FA1057 /* RCTLoggingTests.m */, 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */, 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */, 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */, @@ -862,7 +862,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3DEFCE371D33A67200256E76 /* TestBundle.js in Resources */, + 3DD981D61D33C6FB007DC7BE /* TestBundle.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -871,7 +871,6 @@ buildActionMask = 2147483647; files = ( 1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */, - 1323F18F1C04ABEB0091BED0 /* Thumbnails in Resources */, 1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, @@ -934,6 +933,7 @@ files = ( 3DB99D0C1BA0340600302749 /* UIExplorerIntegrationTests.m in Sources */, 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */, + 3D299BAF1D33EBFA00FA1057 /* RCTLoggingTests.m in Sources */, 143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */, 27B885561BED29AF00008352 /* RCTRootViewIntegrationTests.m in Sources */, ); diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m index 1ca2716f27b630..7e1273141446e4 100644 --- a/Examples/UIExplorer/UIExplorer/AppDelegate.m +++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m @@ -34,8 +34,7 @@ - (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWith NSDictionary *initProps = nil; NSString *_routeUri = [[NSUserDefaults standardUserDefaults] stringForKey:@"route"]; if (_routeUri) { - initProps = @{@"exampleFromAppetizeParams": - [NSString stringWithFormat:@"rnuiexplorer://example/%@Example", _routeUri]}; + initProps = @{@"exampleFromAppetizeParams": [NSString stringWithFormat:@"rnuiexplorer://example/%@Example", _routeUri]}; } RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:_bridge @@ -68,7 +67,7 @@ - (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge * on the same Wi-Fi network. */ - sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?platform=ios&dev=true"]; + sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/js/UIExplorerApp.ios.bundle?platform=ios&dev=true"]; /** * OPTION 2 diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m index fecc3b3a8fbdb9..165bed01aa8f89 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m @@ -36,7 +36,7 @@ - (void)setUp { NSURL *scriptURL; if (getenv("CI_USE_PACKAGER")) { - NSString *app = @"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp"; + NSString *app = @"IntegrationTests/IntegrationTestsApp"; scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]]; RCTAssert(scriptURL != nil, @"No scriptURL set"); } else { @@ -45,14 +45,14 @@ - (void)setUp } _bridge = [[RCTBridge alloc] initWithBundleURL:scriptURL moduleProvider:NULL launchOptions:nil]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:60]; while (date.timeIntervalSinceNow > 0 && _bridge.loading) { [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; } XCTAssertFalse(_bridge.loading); _logSem = dispatch_semaphore_create(0); - RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, __unused NSString *fileName, __unused NSNumber *lineNumber, NSString *message) { if (source == RCTLogSourceJavaScript) { self->_lastLogLevel = level; self->_lastLogSource = source; @@ -96,6 +96,11 @@ - (void)testLogging [_bridge enqueueJSCall:@"LoggingTestModule.logErrorToConsole" args:@[@"Invoking console.error"]]; dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + // For local bundles, we'll first get a warning about symbolication + if ([_bridge.bundleURL isFileURL]) { + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + } + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); XCTAssertEqualObjects(_lastLogMessage, @"Invoking console.error"); @@ -103,6 +108,11 @@ - (void)testLogging [_bridge enqueueJSCall:@"LoggingTestModule.throwError" args:@[@"Throwing an error"]]; dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + // For local bundles, we'll first get a warning about symbolication + if ([_bridge.bundleURL isFileURL]) { + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + } + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); XCTAssertEqualObjects(_lastLogMessage, @"Throwing an error"); diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testLayoutExample_1@2x.png similarity index 100% rename from Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExample_1@2x.png rename to Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testLayoutExample_1@2x.png diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testSliderExample_1@2x.png similarity index 100% rename from Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExample_1@2x.png rename to Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testSliderExample_1@2x.png diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testSwitchExample_1@2x.png similarity index 100% rename from Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExample_1@2x.png rename to Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testSwitchExample_1@2x.png diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTabBarExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testTabBarExample_1@2x.png similarity index 100% rename from Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTabBarExample_1@2x.png rename to Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testTabBarExample_1@2x.png diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testTextExample_1@2x.png similarity index 100% rename from Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExample_1@2x.png rename to Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testTextExample_1@2x.png diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testViewExample_1@2x.png similarity index 100% rename from Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExample_1@2x.png rename to Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-js-UIExplorerApp.ios/testViewExample_1@2x.png diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m index b2a242c214a46f..9de51c3ed59ef6 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m @@ -32,7 +32,7 @@ @implementation UIExplorerSnapshotTests - (void)setUp { - _runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp.ios", nil); + _runner = RCTInitRunnerForApp(@"Examples/UIExplorer/js/UIExplorerApp.ios", nil); _runner.recordMode = NO; } diff --git a/Examples/UIExplorer/TestBundle.js b/Examples/UIExplorer/UIExplorerUnitTests/TestBundle.js similarity index 100% rename from Examples/UIExplorer/TestBundle.js rename to Examples/UIExplorer/UIExplorerUnitTests/TestBundle.js diff --git a/Examples/UIExplorer/android/app/build.gradle b/Examples/UIExplorer/android/app/build.gradle index ea65dc6a8cc136..6a24213d1785b7 100644 --- a/Examples/UIExplorer/android/app/build.gradle +++ b/Examples/UIExplorer/android/app/build.gradle @@ -59,7 +59,7 @@ import com.android.build.OutputFile project.ext.react = [ bundleAssetName: "UIExplorerApp.android.bundle", - entryFile: file("../../UIExplorerApp.android.js"), + entryFile: file("../../js/UIExplorerApp.android.js"), root: "../../../../", inputExcludes: ["android/**", "./**"] ] diff --git a/Examples/UIExplorer/android/app/src/main/java/com/facebook/react/uiapp/UIExplorerApplication.java b/Examples/UIExplorer/android/app/src/main/java/com/facebook/react/uiapp/UIExplorerApplication.java index 87dbd3735d4b3d..897f9489dbdec2 100644 --- a/Examples/UIExplorer/android/app/src/main/java/com/facebook/react/uiapp/UIExplorerApplication.java +++ b/Examples/UIExplorer/android/app/src/main/java/com/facebook/react/uiapp/UIExplorerApplication.java @@ -30,7 +30,7 @@ public class UIExplorerApplication extends Application implements ReactApplicati private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public String getJSMainModuleName() { - return "Examples/UIExplorer/UIExplorerApp.android"; + return "Examples/UIExplorer/js/UIExplorerApp.android"; } @Override diff --git a/Examples/UIExplorer/AccessibilityAndroidExample.android.js b/Examples/UIExplorer/js/AccessibilityAndroidExample.android.js similarity index 95% rename from Examples/UIExplorer/AccessibilityAndroidExample.android.js rename to Examples/UIExplorer/js/AccessibilityAndroidExample.android.js index ff1f215dab70cb..9b3c5789ab14b8 100644 --- a/Examples/UIExplorer/AccessibilityAndroidExample.android.js +++ b/Examples/UIExplorer/js/AccessibilityAndroidExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AccessibilityIOSExample.js b/Examples/UIExplorer/js/AccessibilityIOSExample.js similarity index 86% rename from Examples/UIExplorer/AccessibilityIOSExample.js rename to Examples/UIExplorer/js/AccessibilityIOSExample.js index ca5628d6e407fb..84b49c4b1bbed7 100644 --- a/Examples/UIExplorer/AccessibilityIOSExample.js +++ b/Examples/UIExplorer/js/AccessibilityIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ActionSheetIOSExample.js b/Examples/UIExplorer/js/ActionSheetIOSExample.js similarity index 94% rename from Examples/UIExplorer/ActionSheetIOSExample.js rename to Examples/UIExplorer/js/ActionSheetIOSExample.js index 4be6c776071621..183e9cff90615e 100644 --- a/Examples/UIExplorer/ActionSheetIOSExample.js +++ b/Examples/UIExplorer/js/ActionSheetIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ActivityIndicatorExample.js b/Examples/UIExplorer/js/ActivityIndicatorExample.js similarity index 92% rename from Examples/UIExplorer/ActivityIndicatorExample.js rename to Examples/UIExplorer/js/ActivityIndicatorExample.js index 3ebf8c109b9ca1..4832b1d0eb1958 100644 --- a/Examples/UIExplorer/ActivityIndicatorExample.js +++ b/Examples/UIExplorer/js/ActivityIndicatorExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AdSupportIOSExample.js b/Examples/UIExplorer/js/AdSupportIOSExample.js similarity index 88% rename from Examples/UIExplorer/AdSupportIOSExample.js rename to Examples/UIExplorer/js/AdSupportIOSExample.js index 7a98fb1dcbdb36..5f6c4227fe0a8b 100644 --- a/Examples/UIExplorer/AdSupportIOSExample.js +++ b/Examples/UIExplorer/js/AdSupportIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AlertExample.js b/Examples/UIExplorer/js/AlertExample.js similarity index 92% rename from Examples/UIExplorer/AlertExample.js rename to Examples/UIExplorer/js/AlertExample.js index 85416e0696832b..8ee4813afdd3a5 100644 --- a/Examples/UIExplorer/AlertExample.js +++ b/Examples/UIExplorer/js/AlertExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AlertIOSExample.js b/Examples/UIExplorer/js/AlertIOSExample.js similarity index 93% rename from Examples/UIExplorer/AlertIOSExample.js rename to Examples/UIExplorer/js/AlertIOSExample.js index 85d66d4a62cc99..445824368bafd5 100644 --- a/Examples/UIExplorer/AlertIOSExample.js +++ b/Examples/UIExplorer/js/AlertIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedExample.js b/Examples/UIExplorer/js/AnimatedExample.js similarity index 95% rename from Examples/UIExplorer/AnimatedExample.js rename to Examples/UIExplorer/js/AnimatedExample.js index d11abbf9024442..461fd62ac3291f 100644 --- a/Examples/UIExplorer/AnimatedExample.js +++ b/Examples/UIExplorer/js/AnimatedExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExApp.js b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExApp.js similarity index 97% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExApp.js rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExApp.js index 20153889ab471e..d8e3addcf314e2 100644 --- a/Examples/UIExplorer/AnimatedGratuitousApp/AnExApp.js +++ b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExApp.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExBobble.js b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExBobble.js similarity index 94% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExBobble.js rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExBobble.js index ebf26f0138b368..94d2ce1a3ef541 100644 --- a/Examples/UIExplorer/AnimatedGratuitousApp/AnExBobble.js +++ b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExBobble.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExChained.js b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExChained.js similarity index 93% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExChained.js rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExChained.js index aad9c5839c19a2..2707620d8d6c2f 100644 --- a/Examples/UIExplorer/AnimatedGratuitousApp/AnExChained.js +++ b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExChained.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExScroll.js b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExScroll.js similarity index 92% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExScroll.js rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExScroll.js index 40ad63e3e34894..b195b107237702 100644 --- a/Examples/UIExplorer/AnimatedGratuitousApp/AnExScroll.js +++ b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExScroll.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExSet.js b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExSet.js similarity index 93% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExSet.js rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExSet.js index 1df1f084c91730..464cf887a6e6a6 100644 --- a/Examples/UIExplorer/AnimatedGratuitousApp/AnExSet.js +++ b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExSet.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExSlides.md b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExSlides.md similarity index 100% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExSlides.md rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExSlides.md diff --git a/Examples/UIExplorer/AnimatedGratuitousApp/AnExTilt.js b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExTilt.js similarity index 93% rename from Examples/UIExplorer/AnimatedGratuitousApp/AnExTilt.js rename to Examples/UIExplorer/js/AnimatedGratuitousApp/AnExTilt.js index 31c23c3bcb5d20..5acee72cf1a7a4 100644 --- a/Examples/UIExplorer/AnimatedGratuitousApp/AnExTilt.js +++ b/Examples/UIExplorer/js/AnimatedGratuitousApp/AnExTilt.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AppStateExample.js b/Examples/UIExplorer/js/AppStateExample.js similarity index 100% rename from Examples/UIExplorer/AppStateExample.js rename to Examples/UIExplorer/js/AppStateExample.js diff --git a/Examples/UIExplorer/AssetScaledImageExample.js b/Examples/UIExplorer/js/AssetScaledImageExample.js similarity index 88% rename from Examples/UIExplorer/AssetScaledImageExample.js rename to Examples/UIExplorer/js/AssetScaledImageExample.js index 6b68e2aabb04d0..3d5c12a4d06aff 100644 --- a/Examples/UIExplorer/AssetScaledImageExample.js +++ b/Examples/UIExplorer/js/AssetScaledImageExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/AsyncStorageExample.js b/Examples/UIExplorer/js/AsyncStorageExample.js similarity index 91% rename from Examples/UIExplorer/AsyncStorageExample.js rename to Examples/UIExplorer/js/AsyncStorageExample.js index ac8bd78899abff..8d26f7d32e5246 100644 --- a/Examples/UIExplorer/AsyncStorageExample.js +++ b/Examples/UIExplorer/js/AsyncStorageExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/js/BorderExample.js similarity index 95% rename from Examples/UIExplorer/BorderExample.js rename to Examples/UIExplorer/js/BorderExample.js index 231b64f2c9e084..f68a81fc061805 100644 --- a/Examples/UIExplorer/BorderExample.js +++ b/Examples/UIExplorer/js/BorderExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/BoxShadowExample.js b/Examples/UIExplorer/js/BoxShadowExample.js similarity index 88% rename from Examples/UIExplorer/BoxShadowExample.js rename to Examples/UIExplorer/js/BoxShadowExample.js index 81ed1359b5c37d..90e40af2544069 100644 --- a/Examples/UIExplorer/BoxShadowExample.js +++ b/Examples/UIExplorer/js/BoxShadowExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/CameraRollExample.js b/Examples/UIExplorer/js/CameraRollExample.js similarity index 92% rename from Examples/UIExplorer/CameraRollExample.js rename to Examples/UIExplorer/js/CameraRollExample.js index 0dcad8adba5e10..1a665321c6c154 100644 --- a/Examples/UIExplorer/CameraRollExample.js +++ b/Examples/UIExplorer/js/CameraRollExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/CameraRollView.js b/Examples/UIExplorer/js/CameraRollView.js similarity index 95% rename from Examples/UIExplorer/CameraRollView.js rename to Examples/UIExplorer/js/CameraRollView.js index f5f428f95a01f1..238f00dc593448 100644 --- a/Examples/UIExplorer/CameraRollView.js +++ b/Examples/UIExplorer/js/CameraRollView.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ClipboardExample.js b/Examples/UIExplorer/js/ClipboardExample.js similarity index 84% rename from Examples/UIExplorer/ClipboardExample.js rename to Examples/UIExplorer/js/ClipboardExample.js index 59373f9fd198d7..0b7399b20ededf 100644 --- a/Examples/UIExplorer/ClipboardExample.js +++ b/Examples/UIExplorer/js/ClipboardExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/DatePickerAndroidExample.js b/Examples/UIExplorer/js/DatePickerAndroidExample.js similarity index 93% rename from Examples/UIExplorer/DatePickerAndroidExample.js rename to Examples/UIExplorer/js/DatePickerAndroidExample.js index 42740cf90229c3..c6abe38a03c324 100644 --- a/Examples/UIExplorer/DatePickerAndroidExample.js +++ b/Examples/UIExplorer/js/DatePickerAndroidExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/DatePickerIOSExample.js b/Examples/UIExplorer/js/DatePickerIOSExample.js similarity index 93% rename from Examples/UIExplorer/DatePickerIOSExample.js rename to Examples/UIExplorer/js/DatePickerIOSExample.js index a3dd7876c7e6f0..8d00dd87776b0b 100644 --- a/Examples/UIExplorer/DatePickerIOSExample.js +++ b/Examples/UIExplorer/js/DatePickerIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ExampleTypes.js b/Examples/UIExplorer/js/ExampleTypes.js similarity index 75% rename from Examples/UIExplorer/ExampleTypes.js rename to Examples/UIExplorer/js/ExampleTypes.js index 691f4c6cf22f72..3863bb5bb6e977 100644 --- a/Examples/UIExplorer/ExampleTypes.js +++ b/Examples/UIExplorer/js/ExampleTypes.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/GeolocationExample.js b/Examples/UIExplorer/js/GeolocationExample.js similarity index 88% rename from Examples/UIExplorer/GeolocationExample.js rename to Examples/UIExplorer/js/GeolocationExample.js index 230127d665adcb..ea4517159b0964 100644 --- a/Examples/UIExplorer/GeolocationExample.js +++ b/Examples/UIExplorer/js/GeolocationExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ImageCapInsetsExample.js b/Examples/UIExplorer/js/ImageCapInsetsExample.js similarity index 87% rename from Examples/UIExplorer/ImageCapInsetsExample.js rename to Examples/UIExplorer/js/ImageCapInsetsExample.js index 48a9a986298f05..fe80a86124cf33 100644 --- a/Examples/UIExplorer/ImageCapInsetsExample.js +++ b/Examples/UIExplorer/js/ImageCapInsetsExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ImageEditingExample.js b/Examples/UIExplorer/js/ImageEditingExample.js similarity index 96% rename from Examples/UIExplorer/ImageEditingExample.js rename to Examples/UIExplorer/js/ImageEditingExample.js index 20e710f9215c9b..32cda68294235f 100644 --- a/Examples/UIExplorer/ImageEditingExample.js +++ b/Examples/UIExplorer/js/ImageEditingExample.js @@ -1,5 +1,11 @@ - -/** +/* + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/js/ImageExample.js similarity index 100% rename from Examples/UIExplorer/ImageExample.js rename to Examples/UIExplorer/js/ImageExample.js diff --git a/Examples/UIExplorer/KeyboardAvoidingViewExample.js b/Examples/UIExplorer/js/KeyboardAvoidingViewExample.js similarity index 100% rename from Examples/UIExplorer/KeyboardAvoidingViewExample.js rename to Examples/UIExplorer/js/KeyboardAvoidingViewExample.js diff --git a/Examples/UIExplorer/LayoutAnimationExample.js b/Examples/UIExplorer/js/LayoutAnimationExample.js similarity index 92% rename from Examples/UIExplorer/LayoutAnimationExample.js rename to Examples/UIExplorer/js/LayoutAnimationExample.js index ee2038dbfc9d69..10efb402b58df3 100644 --- a/Examples/UIExplorer/LayoutAnimationExample.js +++ b/Examples/UIExplorer/js/LayoutAnimationExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/LayoutEventsExample.js b/Examples/UIExplorer/js/LayoutEventsExample.js similarity index 93% rename from Examples/UIExplorer/LayoutEventsExample.js rename to Examples/UIExplorer/js/LayoutEventsExample.js index 8325194847c2cc..097b76cc88733b 100644 --- a/Examples/UIExplorer/LayoutEventsExample.js +++ b/Examples/UIExplorer/js/LayoutEventsExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/LayoutExample.js b/Examples/UIExplorer/js/LayoutExample.js similarity index 95% rename from Examples/UIExplorer/LayoutExample.js rename to Examples/UIExplorer/js/LayoutExample.js index 6d1095ae527bce..3b65fcbadce3da 100644 --- a/Examples/UIExplorer/LayoutExample.js +++ b/Examples/UIExplorer/js/LayoutExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/LinkingExample.js b/Examples/UIExplorer/js/LinkingExample.js similarity index 88% rename from Examples/UIExplorer/LinkingExample.js rename to Examples/UIExplorer/js/LinkingExample.js index 390d00314623ba..594209fe112f3b 100644 --- a/Examples/UIExplorer/LinkingExample.js +++ b/Examples/UIExplorer/js/LinkingExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ListViewExample.js b/Examples/UIExplorer/js/ListViewExample.js similarity index 93% rename from Examples/UIExplorer/ListViewExample.js rename to Examples/UIExplorer/js/ListViewExample.js index 42e30de38fb6cb..a392e17a10dac3 100644 --- a/Examples/UIExplorer/ListViewExample.js +++ b/Examples/UIExplorer/js/ListViewExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ListViewGridLayoutExample.js b/Examples/UIExplorer/js/ListViewGridLayoutExample.js similarity index 93% rename from Examples/UIExplorer/ListViewGridLayoutExample.js rename to Examples/UIExplorer/js/ListViewGridLayoutExample.js index eadba937157dc7..4ef2a110d9a95c 100644 --- a/Examples/UIExplorer/ListViewGridLayoutExample.js +++ b/Examples/UIExplorer/js/ListViewGridLayoutExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ListViewPagingExample.js b/Examples/UIExplorer/js/ListViewPagingExample.js similarity index 96% rename from Examples/UIExplorer/ListViewPagingExample.js rename to Examples/UIExplorer/js/ListViewPagingExample.js index 4d31db208c1bbd..89c6c4b4b80a08 100644 --- a/Examples/UIExplorer/ListViewPagingExample.js +++ b/Examples/UIExplorer/js/ListViewPagingExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/js/MapViewExample.js similarity index 97% rename from Examples/UIExplorer/MapViewExample.js rename to Examples/UIExplorer/js/MapViewExample.js index d393702280a7ec..9851f0d0fca3be 100644 --- a/Examples/UIExplorer/MapViewExample.js +++ b/Examples/UIExplorer/js/MapViewExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ModalExample.js b/Examples/UIExplorer/js/ModalExample.js similarity index 94% rename from Examples/UIExplorer/ModalExample.js rename to Examples/UIExplorer/js/ModalExample.js index 1717fb0e098192..deb838960c14e2 100644 --- a/Examples/UIExplorer/ModalExample.js +++ b/Examples/UIExplorer/js/ModalExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/NativeAnimationsExample.js b/Examples/UIExplorer/js/NativeAnimationsExample.js similarity index 100% rename from Examples/UIExplorer/NativeAnimationsExample.js rename to Examples/UIExplorer/js/NativeAnimationsExample.js diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCardStack-NavigationHeader-Tabs-example.js b/Examples/UIExplorer/js/NavigationExperimental/NavigationCardStack-NavigationHeader-Tabs-example.js similarity index 100% rename from Examples/UIExplorer/NavigationExperimental/NavigationCardStack-NavigationHeader-Tabs-example.js rename to Examples/UIExplorer/js/NavigationExperimental/NavigationCardStack-NavigationHeader-Tabs-example.js diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCardStack-example.js b/Examples/UIExplorer/js/NavigationExperimental/NavigationCardStack-example.js similarity index 100% rename from Examples/UIExplorer/NavigationExperimental/NavigationCardStack-example.js rename to Examples/UIExplorer/js/NavigationExperimental/NavigationCardStack-example.js diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationExampleRow.js b/Examples/UIExplorer/js/NavigationExperimental/NavigationExampleRow.js similarity index 84% rename from Examples/UIExplorer/NavigationExperimental/NavigationExampleRow.js rename to Examples/UIExplorer/js/NavigationExperimental/NavigationExampleRow.js index 94a874a9a0bf8b..9f6b27a184a954 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationExampleRow.js +++ b/Examples/UIExplorer/js/NavigationExperimental/NavigationExampleRow.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationExperimentalExample.js b/Examples/UIExplorer/js/NavigationExperimental/NavigationExperimentalExample.js similarity index 100% rename from Examples/UIExplorer/NavigationExperimental/NavigationExperimentalExample.js rename to Examples/UIExplorer/js/NavigationExperimental/NavigationExperimentalExample.js diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js b/Examples/UIExplorer/js/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js similarity index 100% rename from Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js rename to Examples/UIExplorer/js/NavigationExperimental/NavigationTransitioner-AnimatedView-example.js diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-pager-example.js b/Examples/UIExplorer/js/NavigationExperimental/NavigationTransitioner-AnimatedView-pager-example.js similarity index 100% rename from Examples/UIExplorer/NavigationExperimental/NavigationTransitioner-AnimatedView-pager-example.js rename to Examples/UIExplorer/js/NavigationExperimental/NavigationTransitioner-AnimatedView-pager-example.js diff --git a/Examples/UIExplorer/Navigator/BreadcrumbNavSample.js b/Examples/UIExplorer/js/Navigator/BreadcrumbNavSample.js similarity index 92% rename from Examples/UIExplorer/Navigator/BreadcrumbNavSample.js rename to Examples/UIExplorer/js/Navigator/BreadcrumbNavSample.js index 368944125b83f2..183ff03df368c9 100644 --- a/Examples/UIExplorer/Navigator/BreadcrumbNavSample.js +++ b/Examples/UIExplorer/js/Navigator/BreadcrumbNavSample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/Navigator/JumpingNavSample.js b/Examples/UIExplorer/js/Navigator/JumpingNavSample.js similarity index 94% rename from Examples/UIExplorer/Navigator/JumpingNavSample.js rename to Examples/UIExplorer/js/Navigator/JumpingNavSample.js index 4f63f2d477ea77..ef3e09da79c06d 100644 --- a/Examples/UIExplorer/Navigator/JumpingNavSample.js +++ b/Examples/UIExplorer/js/Navigator/JumpingNavSample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/Navigator/NavigationBarSample.js b/Examples/UIExplorer/js/Navigator/NavigationBarSample.js similarity index 94% rename from Examples/UIExplorer/Navigator/NavigationBarSample.js rename to Examples/UIExplorer/js/Navigator/NavigationBarSample.js index a0b4461aa75664..b11629de082c32 100644 --- a/Examples/UIExplorer/Navigator/NavigationBarSample.js +++ b/Examples/UIExplorer/js/Navigator/NavigationBarSample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/Navigator/NavigatorExample.js b/Examples/UIExplorer/js/Navigator/NavigatorExample.js similarity index 94% rename from Examples/UIExplorer/Navigator/NavigatorExample.js rename to Examples/UIExplorer/js/Navigator/NavigatorExample.js index 1da8434b7595f3..45158f6427a78d 100644 --- a/Examples/UIExplorer/Navigator/NavigatorExample.js +++ b/Examples/UIExplorer/js/Navigator/NavigatorExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/NavigatorIOSColorsExample.js b/Examples/UIExplorer/js/NavigatorIOSColorsExample.js similarity index 87% rename from Examples/UIExplorer/NavigatorIOSColorsExample.js rename to Examples/UIExplorer/js/NavigatorIOSColorsExample.js index 2f527c1c639695..033d56f5db527c 100644 --- a/Examples/UIExplorer/NavigatorIOSColorsExample.js +++ b/Examples/UIExplorer/js/NavigatorIOSColorsExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/js/NavigatorIOSExample.js similarity index 100% rename from Examples/UIExplorer/NavigatorIOSExample.js rename to Examples/UIExplorer/js/NavigatorIOSExample.js diff --git a/Examples/UIExplorer/NetInfoExample.js b/Examples/UIExplorer/js/NetInfoExample.js similarity index 93% rename from Examples/UIExplorer/NetInfoExample.js rename to Examples/UIExplorer/js/NetInfoExample.js index e64886ea52e8a8..540a84639e4e72 100644 --- a/Examples/UIExplorer/NetInfoExample.js +++ b/Examples/UIExplorer/js/NetInfoExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/PanResponderExample.js b/Examples/UIExplorer/js/PanResponderExample.js similarity index 100% rename from Examples/UIExplorer/PanResponderExample.js rename to Examples/UIExplorer/js/PanResponderExample.js diff --git a/Examples/UIExplorer/PermissionsExampleAndroid.android.js b/Examples/UIExplorer/js/PermissionsExampleAndroid.android.js similarity index 100% rename from Examples/UIExplorer/PermissionsExampleAndroid.android.js rename to Examples/UIExplorer/js/PermissionsExampleAndroid.android.js diff --git a/Examples/UIExplorer/PickerExample.js b/Examples/UIExplorer/js/PickerExample.js similarity index 93% rename from Examples/UIExplorer/PickerExample.js rename to Examples/UIExplorer/js/PickerExample.js index df29e3178da3aa..c45014d2c0dc52 100644 --- a/Examples/UIExplorer/PickerExample.js +++ b/Examples/UIExplorer/js/PickerExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/PickerIOSExample.js b/Examples/UIExplorer/js/PickerIOSExample.js similarity index 93% rename from Examples/UIExplorer/PickerIOSExample.js rename to Examples/UIExplorer/js/PickerIOSExample.js index 5530135386a68a..5b8e1988fc60d3 100644 --- a/Examples/UIExplorer/PickerIOSExample.js +++ b/Examples/UIExplorer/js/PickerIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/PointerEventsExample.js b/Examples/UIExplorer/js/PointerEventsExample.js similarity index 95% rename from Examples/UIExplorer/PointerEventsExample.js rename to Examples/UIExplorer/js/PointerEventsExample.js index 9e40ed2fac950e..5b15ebb2714a2d 100644 --- a/Examples/UIExplorer/PointerEventsExample.js +++ b/Examples/UIExplorer/js/PointerEventsExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ProgressBarAndroidExample.android.js b/Examples/UIExplorer/js/ProgressBarAndroidExample.android.js similarity index 88% rename from Examples/UIExplorer/ProgressBarAndroidExample.android.js rename to Examples/UIExplorer/js/ProgressBarAndroidExample.android.js index 2e08ab73801072..81be4637cc85a8 100644 --- a/Examples/UIExplorer/ProgressBarAndroidExample.android.js +++ b/Examples/UIExplorer/js/ProgressBarAndroidExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ProgressViewIOSExample.js b/Examples/UIExplorer/js/ProgressViewIOSExample.js similarity index 88% rename from Examples/UIExplorer/ProgressViewIOSExample.js rename to Examples/UIExplorer/js/ProgressViewIOSExample.js index 2cc816015487e7..9967d0ff741cfe 100644 --- a/Examples/UIExplorer/ProgressViewIOSExample.js +++ b/Examples/UIExplorer/js/ProgressViewIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/PushNotificationIOSExample.js b/Examples/UIExplorer/js/PushNotificationIOSExample.js similarity index 93% rename from Examples/UIExplorer/PushNotificationIOSExample.js rename to Examples/UIExplorer/js/PushNotificationIOSExample.js index ee137d83a1a7c2..064a44580661cd 100644 --- a/Examples/UIExplorer/PushNotificationIOSExample.js +++ b/Examples/UIExplorer/js/PushNotificationIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/RCTRootViewIOSExample.js b/Examples/UIExplorer/js/RCTRootViewIOSExample.js similarity index 90% rename from Examples/UIExplorer/RCTRootViewIOSExample.js rename to Examples/UIExplorer/js/RCTRootViewIOSExample.js index 349b022d9a4fff..1228f2f299bfcb 100644 --- a/Examples/UIExplorer/RCTRootViewIOSExample.js +++ b/Examples/UIExplorer/js/RCTRootViewIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/RefreshControlExample.js b/Examples/UIExplorer/js/RefreshControlExample.js similarity index 91% rename from Examples/UIExplorer/RefreshControlExample.js rename to Examples/UIExplorer/js/RefreshControlExample.js index e3fb6e184d82b6..9d85274cb2028c 100644 --- a/Examples/UIExplorer/RefreshControlExample.js +++ b/Examples/UIExplorer/js/RefreshControlExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/RootViewSizeFlexibilityExampleApp.js b/Examples/UIExplorer/js/RootViewSizeFlexibilityExampleApp.js similarity index 86% rename from Examples/UIExplorer/RootViewSizeFlexibilityExampleApp.js rename to Examples/UIExplorer/js/RootViewSizeFlexibilityExampleApp.js index 285404a3854c4e..4fe03451b3333b 100644 --- a/Examples/UIExplorer/RootViewSizeFlexibilityExampleApp.js +++ b/Examples/UIExplorer/js/RootViewSizeFlexibilityExampleApp.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ScrollViewExample.js b/Examples/UIExplorer/js/ScrollViewExample.js similarity index 94% rename from Examples/UIExplorer/ScrollViewExample.js rename to Examples/UIExplorer/js/ScrollViewExample.js index 358c8f66a4e722..de6ac3f7b0a00b 100644 --- a/Examples/UIExplorer/ScrollViewExample.js +++ b/Examples/UIExplorer/js/ScrollViewExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ScrollViewSimpleExample.js b/Examples/UIExplorer/js/ScrollViewSimpleExample.js similarity index 87% rename from Examples/UIExplorer/ScrollViewSimpleExample.js rename to Examples/UIExplorer/js/ScrollViewSimpleExample.js index a2bd4462171220..60efdad42397e4 100644 --- a/Examples/UIExplorer/ScrollViewSimpleExample.js +++ b/Examples/UIExplorer/js/ScrollViewSimpleExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/SegmentedControlIOSExample.js b/Examples/UIExplorer/js/SegmentedControlIOSExample.js similarity index 93% rename from Examples/UIExplorer/SegmentedControlIOSExample.js rename to Examples/UIExplorer/js/SegmentedControlIOSExample.js index bdac8e9e451d35..4a23037a29c857 100644 --- a/Examples/UIExplorer/SegmentedControlIOSExample.js +++ b/Examples/UIExplorer/js/SegmentedControlIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/SetPropertiesExampleApp.js b/Examples/UIExplorer/js/SetPropertiesExampleApp.js similarity index 79% rename from Examples/UIExplorer/SetPropertiesExampleApp.js rename to Examples/UIExplorer/js/SetPropertiesExampleApp.js index 6d0be80347d33f..db17baaa9276a5 100644 --- a/Examples/UIExplorer/SetPropertiesExampleApp.js +++ b/Examples/UIExplorer/js/SetPropertiesExampleApp.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/SliderExample.js b/Examples/UIExplorer/js/SliderExample.js similarity index 92% rename from Examples/UIExplorer/SliderExample.js rename to Examples/UIExplorer/js/SliderExample.js index a987671b50f53c..ff7b31de9ab10a 100644 --- a/Examples/UIExplorer/SliderExample.js +++ b/Examples/UIExplorer/js/SliderExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/SliderIOSExample.js b/Examples/UIExplorer/js/SliderIOSExample.js similarity index 90% rename from Examples/UIExplorer/SliderIOSExample.js rename to Examples/UIExplorer/js/SliderIOSExample.js index eaabfeaa86d27d..8cd69a825a71c0 100644 --- a/Examples/UIExplorer/SliderIOSExample.js +++ b/Examples/UIExplorer/js/SliderIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/SnapshotExample.js b/Examples/UIExplorer/js/SnapshotExample.js similarity index 85% rename from Examples/UIExplorer/SnapshotExample.js rename to Examples/UIExplorer/js/SnapshotExample.js index bb9ddb7ad8d1ee..1a3e5c181cd92f 100644 --- a/Examples/UIExplorer/SnapshotExample.js +++ b/Examples/UIExplorer/js/SnapshotExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/StatusBarExample.js b/Examples/UIExplorer/js/StatusBarExample.js similarity index 97% rename from Examples/UIExplorer/StatusBarExample.js rename to Examples/UIExplorer/js/StatusBarExample.js index 7026a8f29aee53..5a213a32c4ccbd 100644 --- a/Examples/UIExplorer/StatusBarExample.js +++ b/Examples/UIExplorer/js/StatusBarExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/SwitchExample.js b/Examples/UIExplorer/js/SwitchExample.js similarity index 93% rename from Examples/UIExplorer/SwitchExample.js rename to Examples/UIExplorer/js/SwitchExample.js index dc4d1e391da789..0fc64562688bdf 100644 --- a/Examples/UIExplorer/SwitchExample.js +++ b/Examples/UIExplorer/js/SwitchExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TabBarIOSExample.js b/Examples/UIExplorer/js/TabBarIOSExample.js similarity index 93% rename from Examples/UIExplorer/TabBarIOSExample.js rename to Examples/UIExplorer/js/TabBarIOSExample.js index 435f2de21edcab..30a28c6b348537 100644 --- a/Examples/UIExplorer/TabBarIOSExample.js +++ b/Examples/UIExplorer/js/TabBarIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TextExample.android.js b/Examples/UIExplorer/js/TextExample.android.js similarity index 98% rename from Examples/UIExplorer/TextExample.android.js rename to Examples/UIExplorer/js/TextExample.android.js index 230bd79bb170e9..53715fa84948f0 100644 --- a/Examples/UIExplorer/TextExample.android.js +++ b/Examples/UIExplorer/js/TextExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/js/TextExample.ios.js similarity index 100% rename from Examples/UIExplorer/TextExample.ios.js rename to Examples/UIExplorer/js/TextExample.ios.js diff --git a/Examples/UIExplorer/TextInputExample.android.js b/Examples/UIExplorer/js/TextInputExample.android.js similarity index 98% rename from Examples/UIExplorer/TextInputExample.android.js rename to Examples/UIExplorer/js/TextInputExample.android.js index 4891ae8e4e8e9b..83731531de64b7 100644 --- a/Examples/UIExplorer/TextInputExample.android.js +++ b/Examples/UIExplorer/js/TextInputExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TextInputExample.ios.js b/Examples/UIExplorer/js/TextInputExample.ios.js similarity index 98% rename from Examples/UIExplorer/TextInputExample.ios.js rename to Examples/UIExplorer/js/TextInputExample.ios.js index bebce92032d962..6c166b391050d8 100644 --- a/Examples/UIExplorer/TextInputExample.ios.js +++ b/Examples/UIExplorer/js/TextInputExample.ios.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/Thumbnails/bandaged.png b/Examples/UIExplorer/js/Thumbnails/bandaged.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/bandaged.png rename to Examples/UIExplorer/js/Thumbnails/bandaged.png diff --git a/Examples/UIExplorer/Thumbnails/call.png b/Examples/UIExplorer/js/Thumbnails/call.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/call.png rename to Examples/UIExplorer/js/Thumbnails/call.png diff --git a/Examples/UIExplorer/Thumbnails/dislike.png b/Examples/UIExplorer/js/Thumbnails/dislike.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/dislike.png rename to Examples/UIExplorer/js/Thumbnails/dislike.png diff --git a/Examples/UIExplorer/Thumbnails/fist.png b/Examples/UIExplorer/js/Thumbnails/fist.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/fist.png rename to Examples/UIExplorer/js/Thumbnails/fist.png diff --git a/Examples/UIExplorer/Thumbnails/flowers.png b/Examples/UIExplorer/js/Thumbnails/flowers.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/flowers.png rename to Examples/UIExplorer/js/Thumbnails/flowers.png diff --git a/Examples/UIExplorer/Thumbnails/heart.png b/Examples/UIExplorer/js/Thumbnails/heart.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/heart.png rename to Examples/UIExplorer/js/Thumbnails/heart.png diff --git a/Examples/UIExplorer/Thumbnails/like.png b/Examples/UIExplorer/js/Thumbnails/like.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/like.png rename to Examples/UIExplorer/js/Thumbnails/like.png diff --git a/Examples/UIExplorer/Thumbnails/liking.png b/Examples/UIExplorer/js/Thumbnails/liking.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/liking.png rename to Examples/UIExplorer/js/Thumbnails/liking.png diff --git a/Examples/UIExplorer/Thumbnails/party.png b/Examples/UIExplorer/js/Thumbnails/party.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/party.png rename to Examples/UIExplorer/js/Thumbnails/party.png diff --git a/Examples/UIExplorer/Thumbnails/poke.png b/Examples/UIExplorer/js/Thumbnails/poke.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/poke.png rename to Examples/UIExplorer/js/Thumbnails/poke.png diff --git a/Examples/UIExplorer/Thumbnails/superlike.png b/Examples/UIExplorer/js/Thumbnails/superlike.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/superlike.png rename to Examples/UIExplorer/js/Thumbnails/superlike.png diff --git a/Examples/UIExplorer/Thumbnails/victory.png b/Examples/UIExplorer/js/Thumbnails/victory.png similarity index 100% rename from Examples/UIExplorer/Thumbnails/victory.png rename to Examples/UIExplorer/js/Thumbnails/victory.png diff --git a/Examples/UIExplorer/TimePickerAndroidExample.js b/Examples/UIExplorer/js/TimePickerAndroidExample.js similarity index 92% rename from Examples/UIExplorer/TimePickerAndroidExample.js rename to Examples/UIExplorer/js/TimePickerAndroidExample.js index 60ef949ddf4d75..db3d54acf558ce 100644 --- a/Examples/UIExplorer/TimePickerAndroidExample.js +++ b/Examples/UIExplorer/js/TimePickerAndroidExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TimerExample.js b/Examples/UIExplorer/js/TimerExample.js similarity index 95% rename from Examples/UIExplorer/TimerExample.js rename to Examples/UIExplorer/js/TimerExample.js index 8fdc92081f045d..3b365ce2c88259 100644 --- a/Examples/UIExplorer/TimerExample.js +++ b/Examples/UIExplorer/js/TimerExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ToastAndroidExample.android.js b/Examples/UIExplorer/js/ToastAndroidExample.android.js similarity index 87% rename from Examples/UIExplorer/ToastAndroidExample.android.js rename to Examples/UIExplorer/js/ToastAndroidExample.android.js index 9bb8aaa0b7aac1..53cd3a95a85d29 100644 --- a/Examples/UIExplorer/ToastAndroidExample.android.js +++ b/Examples/UIExplorer/js/ToastAndroidExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ToolbarAndroidExample.android.js b/Examples/UIExplorer/js/ToolbarAndroidExample.android.js similarity index 93% rename from Examples/UIExplorer/ToolbarAndroidExample.android.js rename to Examples/UIExplorer/js/ToolbarAndroidExample.android.js index 95decb766816dc..b0fb8efbc529c4 100644 --- a/Examples/UIExplorer/ToolbarAndroidExample.android.js +++ b/Examples/UIExplorer/js/ToolbarAndroidExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TouchableExample.js b/Examples/UIExplorer/js/TouchableExample.js similarity index 97% rename from Examples/UIExplorer/TouchableExample.js rename to Examples/UIExplorer/js/TouchableExample.js index 12f417b2d4e2fd..69e3819c19bd3e 100644 --- a/Examples/UIExplorer/TouchableExample.js +++ b/Examples/UIExplorer/js/TouchableExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TransformExample.js b/Examples/UIExplorer/js/TransformExample.js similarity index 95% rename from Examples/UIExplorer/TransformExample.js rename to Examples/UIExplorer/js/TransformExample.js index c85cac0fd0ec44..a13e9c34575453 100644 --- a/Examples/UIExplorer/TransformExample.js +++ b/Examples/UIExplorer/js/TransformExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/TransparentHitTestExample.js b/Examples/UIExplorer/js/TransparentHitTestExample.js similarity index 100% rename from Examples/UIExplorer/TransparentHitTestExample.js rename to Examples/UIExplorer/js/TransparentHitTestExample.js diff --git a/Examples/UIExplorer/UIExplorerActions.js b/Examples/UIExplorer/js/UIExplorerActions.js similarity index 100% rename from Examples/UIExplorer/UIExplorerActions.js rename to Examples/UIExplorer/js/UIExplorerActions.js diff --git a/Examples/UIExplorer/UIExplorerApp.android.js b/Examples/UIExplorer/js/UIExplorerApp.android.js similarity index 100% rename from Examples/UIExplorer/UIExplorerApp.android.js rename to Examples/UIExplorer/js/UIExplorerApp.android.js diff --git a/Examples/UIExplorer/UIExplorerApp.ios.js b/Examples/UIExplorer/js/UIExplorerApp.ios.js similarity index 100% rename from Examples/UIExplorer/UIExplorerApp.ios.js rename to Examples/UIExplorer/js/UIExplorerApp.ios.js diff --git a/Examples/UIExplorer/UIExplorerBlock.js b/Examples/UIExplorer/js/UIExplorerBlock.js similarity index 88% rename from Examples/UIExplorer/UIExplorerBlock.js rename to Examples/UIExplorer/js/UIExplorerBlock.js index 9a3c7a5498c709..160fcbc312b998 100644 --- a/Examples/UIExplorer/UIExplorerBlock.js +++ b/Examples/UIExplorer/js/UIExplorerBlock.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/UIExplorerButton.js b/Examples/UIExplorer/js/UIExplorerButton.js similarity index 82% rename from Examples/UIExplorer/UIExplorerButton.js rename to Examples/UIExplorer/js/UIExplorerButton.js index ba6c213244f5bd..51d5e1a66474d2 100644 --- a/Examples/UIExplorer/UIExplorerButton.js +++ b/Examples/UIExplorer/js/UIExplorerButton.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/UIExplorerExampleList.js b/Examples/UIExplorer/js/UIExplorerExampleList.js similarity index 100% rename from Examples/UIExplorer/UIExplorerExampleList.js rename to Examples/UIExplorer/js/UIExplorerExampleList.js diff --git a/Examples/UIExplorer/UIExplorerList.android.js b/Examples/UIExplorer/js/UIExplorerList.android.js similarity index 100% rename from Examples/UIExplorer/UIExplorerList.android.js rename to Examples/UIExplorer/js/UIExplorerList.android.js diff --git a/Examples/UIExplorer/UIExplorerList.ios.js b/Examples/UIExplorer/js/UIExplorerList.ios.js similarity index 100% rename from Examples/UIExplorer/UIExplorerList.ios.js rename to Examples/UIExplorer/js/UIExplorerList.ios.js diff --git a/Examples/UIExplorer/UIExplorerNavigationReducer.js b/Examples/UIExplorer/js/UIExplorerNavigationReducer.js similarity index 100% rename from Examples/UIExplorer/UIExplorerNavigationReducer.js rename to Examples/UIExplorer/js/UIExplorerNavigationReducer.js diff --git a/Examples/UIExplorer/UIExplorerPage.js b/Examples/UIExplorer/js/UIExplorerPage.js similarity index 87% rename from Examples/UIExplorer/UIExplorerPage.js rename to Examples/UIExplorer/js/UIExplorerPage.js index 3aaf4119cf98ef..3991fef1169eab 100644 --- a/Examples/UIExplorer/UIExplorerPage.js +++ b/Examples/UIExplorer/js/UIExplorerPage.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/UIExplorerStateTitleMap.js b/Examples/UIExplorer/js/UIExplorerStateTitleMap.js similarity index 100% rename from Examples/UIExplorer/UIExplorerStateTitleMap.js rename to Examples/UIExplorer/js/UIExplorerStateTitleMap.js diff --git a/Examples/UIExplorer/UIExplorerTitle.js b/Examples/UIExplorer/js/UIExplorerTitle.js similarity index 81% rename from Examples/UIExplorer/UIExplorerTitle.js rename to Examples/UIExplorer/js/UIExplorerTitle.js index 81a74dc279812c..33feaf705a2238 100644 --- a/Examples/UIExplorer/UIExplorerTitle.js +++ b/Examples/UIExplorer/js/UIExplorerTitle.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/URIActionMap.js b/Examples/UIExplorer/js/URIActionMap.js similarity index 85% rename from Examples/UIExplorer/URIActionMap.js rename to Examples/UIExplorer/js/URIActionMap.js index 2940e0736001b8..63051ea7573e35 100644 --- a/Examples/UIExplorer/URIActionMap.js +++ b/Examples/UIExplorer/js/URIActionMap.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/VibrationExample.js b/Examples/UIExplorer/js/VibrationExample.js similarity index 89% rename from Examples/UIExplorer/VibrationExample.js rename to Examples/UIExplorer/js/VibrationExample.js index e4948e54cb5811..554ae2f1f5514d 100644 --- a/Examples/UIExplorer/VibrationExample.js +++ b/Examples/UIExplorer/js/VibrationExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/VibrationIOSExample.js b/Examples/UIExplorer/js/VibrationIOSExample.js similarity index 82% rename from Examples/UIExplorer/VibrationIOSExample.js rename to Examples/UIExplorer/js/VibrationIOSExample.js index cce819f994ef52..59d1b3e352016e 100644 --- a/Examples/UIExplorer/VibrationIOSExample.js +++ b/Examples/UIExplorer/js/VibrationIOSExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/ViewExample.js b/Examples/UIExplorer/js/ViewExample.js similarity index 100% rename from Examples/UIExplorer/ViewExample.js rename to Examples/UIExplorer/js/ViewExample.js diff --git a/Examples/UIExplorer/ViewPagerAndroidExample.android.js b/Examples/UIExplorer/js/ViewPagerAndroidExample.android.js similarity index 100% rename from Examples/UIExplorer/ViewPagerAndroidExample.android.js rename to Examples/UIExplorer/js/ViewPagerAndroidExample.android.js diff --git a/Examples/UIExplorer/WebSocketExample.js b/Examples/UIExplorer/js/WebSocketExample.js similarity index 100% rename from Examples/UIExplorer/WebSocketExample.js rename to Examples/UIExplorer/js/WebSocketExample.js diff --git a/Examples/UIExplorer/WebViewExample.js b/Examples/UIExplorer/js/WebViewExample.js similarity index 96% rename from Examples/UIExplorer/WebViewExample.js rename to Examples/UIExplorer/js/WebViewExample.js index 9e58ae67c5fa11..d41ea15a108396 100644 --- a/Examples/UIExplorer/WebViewExample.js +++ b/Examples/UIExplorer/js/WebViewExample.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/XHRExample.android.js b/Examples/UIExplorer/js/XHRExample.android.js similarity index 97% rename from Examples/UIExplorer/XHRExample.android.js rename to Examples/UIExplorer/js/XHRExample.android.js index 991f88d4528671..c6b0b446f62a13 100644 --- a/Examples/UIExplorer/XHRExample.android.js +++ b/Examples/UIExplorer/js/XHRExample.android.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/XHRExample.ios.js b/Examples/UIExplorer/js/XHRExample.ios.js similarity index 100% rename from Examples/UIExplorer/XHRExample.ios.js rename to Examples/UIExplorer/js/XHRExample.ios.js diff --git a/Examples/UIExplorer/XHRExampleCookies.js b/Examples/UIExplorer/js/XHRExampleCookies.js similarity index 92% rename from Examples/UIExplorer/XHRExampleCookies.js rename to Examples/UIExplorer/js/XHRExampleCookies.js index d07c7af03d7bb9..783239149126ca 100644 --- a/Examples/UIExplorer/XHRExampleCookies.js +++ b/Examples/UIExplorer/js/XHRExampleCookies.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/XHRExampleFetch.js b/Examples/UIExplorer/js/XHRExampleFetch.js similarity index 89% rename from Examples/UIExplorer/XHRExampleFetch.js rename to Examples/UIExplorer/js/XHRExampleFetch.js index c2d7c0076e214b..06a90dac398db2 100644 --- a/Examples/UIExplorer/XHRExampleFetch.js +++ b/Examples/UIExplorer/js/XHRExampleFetch.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/XHRExampleHeaders.js b/Examples/UIExplorer/js/XHRExampleHeaders.js similarity index 90% rename from Examples/UIExplorer/XHRExampleHeaders.js rename to Examples/UIExplorer/js/XHRExampleHeaders.js index ed75ad0ad2cf3c..20ecbd9eaf9f42 100644 --- a/Examples/UIExplorer/XHRExampleHeaders.js +++ b/Examples/UIExplorer/js/XHRExampleHeaders.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/XHRExampleOnTimeOut.js b/Examples/UIExplorer/js/XHRExampleOnTimeOut.js similarity index 89% rename from Examples/UIExplorer/XHRExampleOnTimeOut.js rename to Examples/UIExplorer/js/XHRExampleOnTimeOut.js index 4fbd2aa8205c7d..1c06430918097b 100644 --- a/Examples/UIExplorer/XHRExampleOnTimeOut.js +++ b/Examples/UIExplorer/js/XHRExampleOnTimeOut.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/bunny.png b/Examples/UIExplorer/js/bunny.png similarity index 100% rename from Examples/UIExplorer/bunny.png rename to Examples/UIExplorer/js/bunny.png diff --git a/Examples/UIExplorer/createExamplePage.js b/Examples/UIExplorer/js/createExamplePage.js similarity index 90% rename from Examples/UIExplorer/createExamplePage.js rename to Examples/UIExplorer/js/createExamplePage.js index 72a9bc685f99f1..2a037dbe04d63f 100644 --- a/Examples/UIExplorer/createExamplePage.js +++ b/Examples/UIExplorer/js/createExamplePage.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * diff --git a/Examples/UIExplorer/flux@3x.png b/Examples/UIExplorer/js/flux@3x.png similarity index 100% rename from Examples/UIExplorer/flux@3x.png rename to Examples/UIExplorer/js/flux@3x.png diff --git a/Examples/UIExplorer/hawk.png b/Examples/UIExplorer/js/hawk.png similarity index 100% rename from Examples/UIExplorer/hawk.png rename to Examples/UIExplorer/js/hawk.png diff --git a/Examples/UIExplorer/helloworld.html b/Examples/UIExplorer/js/helloworld.html similarity index 100% rename from Examples/UIExplorer/helloworld.html rename to Examples/UIExplorer/js/helloworld.html diff --git a/Examples/UIExplorer/relay@3x.png b/Examples/UIExplorer/js/relay@3x.png similarity index 100% rename from Examples/UIExplorer/relay@3x.png rename to Examples/UIExplorer/js/relay@3x.png diff --git a/Examples/UIExplorer/slider-left.png b/Examples/UIExplorer/js/slider-left.png similarity index 100% rename from Examples/UIExplorer/slider-left.png rename to Examples/UIExplorer/js/slider-left.png diff --git a/Examples/UIExplorer/slider-left@2x.png b/Examples/UIExplorer/js/slider-left@2x.png similarity index 100% rename from Examples/UIExplorer/slider-left@2x.png rename to Examples/UIExplorer/js/slider-left@2x.png diff --git a/Examples/UIExplorer/slider-right.png b/Examples/UIExplorer/js/slider-right.png similarity index 100% rename from Examples/UIExplorer/slider-right.png rename to Examples/UIExplorer/js/slider-right.png diff --git a/Examples/UIExplorer/slider-right@2x.png b/Examples/UIExplorer/js/slider-right@2x.png similarity index 100% rename from Examples/UIExplorer/slider-right@2x.png rename to Examples/UIExplorer/js/slider-right@2x.png diff --git a/Examples/UIExplorer/slider.png b/Examples/UIExplorer/js/slider.png similarity index 100% rename from Examples/UIExplorer/slider.png rename to Examples/UIExplorer/js/slider.png diff --git a/Examples/UIExplorer/slider@2x.png b/Examples/UIExplorer/js/slider@2x.png similarity index 100% rename from Examples/UIExplorer/slider@2x.png rename to Examples/UIExplorer/js/slider@2x.png diff --git a/Examples/UIExplorer/uie_comment_highlighted@2x.png b/Examples/UIExplorer/js/uie_comment_highlighted@2x.png similarity index 100% rename from Examples/UIExplorer/uie_comment_highlighted@2x.png rename to Examples/UIExplorer/js/uie_comment_highlighted@2x.png diff --git a/Examples/UIExplorer/uie_comment_normal@2x.png b/Examples/UIExplorer/js/uie_comment_normal@2x.png similarity index 100% rename from Examples/UIExplorer/uie_comment_normal@2x.png rename to Examples/UIExplorer/js/uie_comment_normal@2x.png diff --git a/Examples/UIExplorer/uie_thumb_big.png b/Examples/UIExplorer/js/uie_thumb_big.png similarity index 100% rename from Examples/UIExplorer/uie_thumb_big.png rename to Examples/UIExplorer/js/uie_thumb_big.png diff --git a/Examples/UIExplorer/uie_thumb_normal@2x.png b/Examples/UIExplorer/js/uie_thumb_normal@2x.png similarity index 100% rename from Examples/UIExplorer/uie_thumb_normal@2x.png rename to Examples/UIExplorer/js/uie_thumb_normal@2x.png diff --git a/Examples/UIExplorer/uie_thumb_selected@2x.png b/Examples/UIExplorer/js/uie_thumb_selected@2x.png similarity index 100% rename from Examples/UIExplorer/uie_thumb_selected@2x.png rename to Examples/UIExplorer/js/uie_thumb_selected@2x.png diff --git a/Examples/UIExplorer/websocket_test_server.js b/Examples/UIExplorer/js/websocket_test_server.js similarity index 81% rename from Examples/UIExplorer/websocket_test_server.js rename to Examples/UIExplorer/js/websocket_test_server.js index 24240127843d56..42f44b126a77a4 100644 --- a/Examples/UIExplorer/websocket_test_server.js +++ b/Examples/UIExplorer/js/websocket_test_server.js @@ -1,4 +1,11 @@ /** + * 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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * From e632025917a000bf3fb5d296c6712a81435626d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Gregorczyk?= Date: Tue, 12 Jul 2016 08:03:04 -0700 Subject: [PATCH 089/241] Allow apps to provide JSBundleLoader of their choice Reviewed By: tadeuzagallo Differential Revision: D3522798 fbshipit-source-id: 90324e44a02ad78885ff3c2a33ba58d4ee6a021a --- .../facebook/react/ReactInstanceManager.java | 38 ++++++++++++++----- .../react/XReactInstanceManagerImpl.java | 19 ++++------ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 71818459b393a0..ee4a8a2c379b31 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -23,6 +23,7 @@ import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.cxxbridge.JSBundleLoader; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.devsupport.DevSupportManager; import com.facebook.react.devsupport.RedBoxHandler; @@ -184,6 +185,7 @@ public static class Builder { protected final List mPackages = new ArrayList<>(); protected @Nullable String mJSBundleFile; + protected @Nullable JSBundleLoader mJSBundleLoader; protected @Nullable String mJSMainModuleName; protected @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener; protected @Nullable Application mApplication; @@ -225,6 +227,19 @@ public Builder setBundleAssetName(String bundleAssetName) { */ public Builder setJSBundleFile(String jsBundleFile) { mJSBundleFile = jsBundleFile; + mJSBundleLoader = null; + return this; + } + + /** + * Bundle loader to use when setting up JS environment. This supersedes + * prior invcations of {@link setJSBundleFile} and {@link setBundleAssetName}. + * + * Example: {@code JSBundleLoader.createFileLoader(application, bundleFile)} + */ + public Builder setJSBundleLoader(JSBundleLoader jsBundleLoader) { + mJSBundleLoader = jsBundleLoader; + mJSBundleFile = null; return this; } @@ -326,12 +341,20 @@ public Builder setUseOldBridge(boolean enable) { * */ public ReactInstanceManager build() { + Assertions.assertNotNull( + mApplication, + "Application property has not been set with this builder"); + Assertions.assertCondition( - mUseDeveloperSupport || mJSBundleFile != null, + mJSBundleLoader == null || !mUseOldBridge, + "JSBundleLoader can't be used with the old bridge"); + + Assertions.assertCondition( + mUseDeveloperSupport || mJSBundleFile != null || mJSBundleLoader != null, "JS Bundle File has to be provided when dev support is disabled"); Assertions.assertCondition( - mJSMainModuleName != null || mJSBundleFile != null, + mJSMainModuleName != null || mJSBundleFile != null || mJSBundleLoader != null, "Either MainModuleName or JS Bundle File needs to be provided"); if (mUIImplementationProvider == null) { @@ -341,9 +364,7 @@ public ReactInstanceManager build() { if (mUseOldBridge) { return new ReactInstanceManagerImpl( - Assertions.assertNotNull( - mApplication, - "Application property has not been set with this builder"), + mApplication, mCurrentActivity, mDefaultHardwareBackBtnHandler, mJSBundleFile, @@ -358,12 +379,11 @@ public ReactInstanceManager build() { mRedBoxHandler); } else { return new XReactInstanceManagerImpl( - Assertions.assertNotNull( - mApplication, - "Application property has not been set with this builder"), + mApplication, mCurrentActivity, mDefaultHardwareBackBtnHandler, - mJSBundleFile, + (mJSBundleLoader == null && mJSBundleFile != null) ? + JSBundleLoader.createFileLoader(mApplication, mJSBundleFile) : mJSBundleLoader, mJSMainModuleName, mPackages, mUseDeveloperSupport, diff --git a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java index 42911f885d11b5..ee29bc0799d93c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java @@ -109,7 +109,7 @@ private @Nullable ReactContextInitAsyncTask mReactContextInitAsyncTask; /* accessed from any thread */ - private @Nullable String mJSBundleFile; /* path to JS bundle on file system */ + private final @Nullable JSBundleLoader mBundleLoader; /* path to JS bundle on file system */ private final @Nullable String mJSMainModuleName; /* path to JS bundle root on packager server */ private final List mPackages; private final DevSupportManager mDevSupportManager; @@ -275,7 +275,7 @@ public T get() throws Exception { Context applicationContext, @Nullable Activity currentActivity, @Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler, - @Nullable String jsBundleFile, + @Nullable JSBundleLoader bundleLoader, @Nullable String jsMainModuleName, List packages, boolean useDeveloperSupport, @@ -295,7 +295,7 @@ public T get() throws Exception { mApplicationContext = applicationContext; mCurrentActivity = currentActivity; mDefaultBackButtonImpl = defaultHardwareBackBtnHandler; - mJSBundleFile = jsBundleFile; + mBundleLoader = bundleLoader; mJSMainModuleName = jsMainModuleName; mPackages = packages; mUseDeveloperSupport = useDeveloperSupport; @@ -382,7 +382,7 @@ private void recreateReactContextInBackgroundInner() { // If there is a up-to-date bundle downloaded from server, // with remote JS debugging disabled, always use that. onJSBundleLoadedFromServer(); - } else if (mJSBundleFile == null) { + } else if (mBundleLoader == null) { mDevSupportManager.handleReloadJS(); } else { mDevSupportManager.isPackagerRunning( @@ -398,7 +398,7 @@ public void run() { } else { // If dev server is down, disable the remote JS debugging. devSettings.setRemoteJSDebugEnabled(false); - recreateReactContextInBackgroundFromBundleFile(); + recreateReactContextInBackgroundFromBundleLoader(); } } }); @@ -408,16 +408,13 @@ public void run() { return; } - recreateReactContextInBackgroundFromBundleFile(); + recreateReactContextInBackgroundFromBundleLoader(); } - private void recreateReactContextInBackgroundFromBundleFile() { - boolean useLazyBundle = mJSCConfig.getConfigMap().hasKey("useLazyBundle") ? - mJSCConfig.getConfigMap().getBoolean("useLazyBundle") : false; - + private void recreateReactContextInBackgroundFromBundleLoader() { recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()), - JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile, useLazyBundle)); + mBundleLoader); } /** From a665914d1857768e4cfb168ddeaab6f000f5e5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Gregorczyk?= Date: Tue, 12 Jul 2016 08:03:08 -0700 Subject: [PATCH 090/241] Unpack files required by optimized bundle format Reviewed By: tadeuzagallo Differential Revision: D3522855 fbshipit-source-id: 2d14db33ce9b98ea1aeea5a12e292e5926e43796 --- .../react/cxxbridge/JSBundleLoader.java | 15 + .../cxxbridge/UnpackingJSBundleLoader.java | 342 ++++++++++++++++++ .../java/com/facebook/react/cxxbridge/BUCK | 40 ++ .../ContentCheckingUnpackerTest.java | 85 +++++ .../ExistenceCheckingUnpackerTest.java | 78 ++++ .../react/cxxbridge/UnpackerTestBase.java | 95 +++++ .../UnpackingJSBundleLoaderTest.java | 183 ++++++++++ 7 files changed, 838 insertions(+) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java create mode 100644 ReactAndroid/src/test/java/com/facebook/react/cxxbridge/BUCK create mode 100644 ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ContentCheckingUnpackerTest.java create mode 100644 ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ExistenceCheckingUnpackerTest.java create mode 100644 ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackerTestBase.java create mode 100644 ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java index 7b4d6e49976710..6615c75c8aa9c1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java @@ -14,6 +14,8 @@ import com.facebook.react.devsupport.DebugServerException; import com.facebook.react.devsupport.DevServerHelper; +import java.io.File; + /** * A class that stores JS bundle information and allows {@link CatalystInstance} to load a correct * bundle through {@link ReactBridge}. @@ -99,6 +101,19 @@ public String getSourceUrl() { }; } + public static JSBundleLoader createUnpackingBundleLoader( + final Context context, + final String sourceURL, + final String bundleName) { + return UnpackingJSBundleLoader.newBuilder() + .setContext(context) + .setSourceURL(sourceURL) + .setDestinationPath(new File(context.getFilesDir(), "optimized-bundle")) + .checkAndUnpackFile(bundleName + ".meta", "bundle.meta") + .unpackFile(bundleName, "bundle.js") + .build(); + } + public abstract void loadScript(CatalystInstanceImpl instance); public abstract String getSourceUrl(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java new file mode 100644 index 00000000000000..ed65860ff56150 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java @@ -0,0 +1,342 @@ +/** + * 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.cxxbridge; + +import android.content.Context; +import android.content.res.AssetManager; + +import com.facebook.infer.annotation.Assertions; +import com.facebook.soloader.FileLocker; +import com.facebook.soloader.SysUtil; +import com.facebook.systrace.Systrace; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.annotation.Nullable; + +import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; + +/** + * JSBundleLoader capable of unpacking specified files necessary for executing + * JS bundle stored in optimized format. + */ +public class UnpackingJSBundleLoader extends JSBundleLoader { + + /** + * Name of the lock files. Multiple processes can be spawned off the same app + * and we need to guarantee that at most one unpacks files at any time. To + * make that work any process is required to hold file system lock on + * LOCK_FILE when checking whether files should be unpacked and unpacking + * them. + */ + static final String LOCK_FILE = "unpacking-bundle-loader.lock"; + + /** + * Existence of this file indicates that the last unpacking operation finished + * before the app was killed or crashed. File with this name is created in the + * destination directory as the last one. If it is present it means that + * all the files that needed to be fsynced were fsynced and their content is + * what it should be. + */ + static final String DOT_UNPACKED_FILE = ".unpacked"; + + private static final int IO_BUFFER_SIZE = 16 * 1024; + + /** + * Where all the files should go to. + */ + private final File mDirectoryPath; + + private final String mSourceURL; + private final Context mContext; + + /** + * Description of what needs to be unpacked. + */ + private final Unpacker[] mUnpackers; + + /* package */ UnpackingJSBundleLoader(Builder builder) { + mContext = Assertions.assertNotNull(builder.context); + mDirectoryPath = Assertions.assertNotNull(builder.destinationPath); + mSourceURL = Assertions.assertNotNull(builder.sourceURL); + mUnpackers = builder.unpackers.toArray(new Unpacker[builder.unpackers.size()]); + } + + /** + * Checks if any file needs to be extracted again, and if so, clears the destination + * directory and unpacks everything again. + */ + /* package */ void prepare() { + final File lockFilePath = new File(mContext.getFilesDir(), LOCK_FILE); + Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "UnpackingJSBundleLoader.prepare"); + try (FileLocker lock = FileLocker.lock(lockFilePath)) { + prepareLocked(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } finally { + Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); + } + } + + private void prepareLocked() throws IOException { + final File dotFinishedFilePath = new File(mDirectoryPath, DOT_UNPACKED_FILE); + boolean shouldReconstruct = !mDirectoryPath.exists() || !dotFinishedFilePath.exists(); + + byte[] buffer = new byte[IO_BUFFER_SIZE]; + for (int i = 0; i < mUnpackers.length && !shouldReconstruct; ++i) { + shouldReconstruct = mUnpackers[i].shouldReconstructDir(mContext, buffer); + } + + if (!shouldReconstruct) { + return; + } + + boolean succeeded = false; + try { + SysUtil.dumbDeleteRecursive(mDirectoryPath); + if (!mDirectoryPath.mkdirs()) { + throw new IOException("Coult not create the destination directory"); + } + + for (Unpacker unpacker : mUnpackers) { + unpacker.unpack(mContext, buffer); + } + + if (!dotFinishedFilePath.createNewFile()) { + throw new IOException("Could not create .unpacked file"); + } + + // It would be nice to fsync a few directories and files here. The thing is, if we crash and + // lose some data then it should be noticed on the next prepare invocation and the directory + // will be reconstructed. It is only crucial to fsync those files whose content is not + // verified on each start. Everything else is a tradeoff between perf with no crashes + // situation and perf when user experiences crashes. Fortunately Unpackers corresponding + // to files whose content is not checked handle fsyncs themselves. + + succeeded = true; + } finally { + // In case of failure do yourself a favor and remove partially initialized state. + if (!succeeded) { + SysUtil.dumbDeleteRecursive(mDirectoryPath); + } + } + } + + @Override + public void loadScript(CatalystInstanceImpl instance) { + prepare(); + // TODO(12128379): add instance method that would take bundle directory + instance.loadScriptFromFile( + new File(mDirectoryPath, "bundle.js").getPath(), + mSourceURL); + } + + @Override + public String getSourceUrl() { + return mSourceURL; + } + + static void fsync(File path) throws IOException { + try (RandomAccessFile file = new RandomAccessFile(path, "r")) { + file.getFD().sync(); + } + } + + /** + * Reads all the bytes (but no more that maxSize) from given input stream through ioBuffer + * and returns byte array containing all the read bytes. + */ + static byte[] readBytes(InputStream is, byte[] ioBuffer, int maxSize) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copyBytes(baos, is, ioBuffer, maxSize); + return baos.toByteArray(); + } + + /** + * Pumps all the bytes (but no more that maxSize) from given input stream through ioBuffer + * to given output stream and returns number of moved bytes. + */ + static int copyBytes( + OutputStream os, + InputStream is, + byte[] ioBuffer, + int maxSize) throws IOException { + int totalSize = 0; + while (totalSize < maxSize) { + int rc = is.read(ioBuffer, 0, Math.min(maxSize - totalSize, ioBuffer.length)); + if (rc == -1) { + break; + } + os.write(ioBuffer, 0, rc); + totalSize += rc; + } + return totalSize; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private @Nullable Context context; + private @Nullable File destinationPath; + private @Nullable String sourceURL; + private final ArrayList unpackers; + + public Builder() { + this.unpackers = new ArrayList(); + context = null; + destinationPath = null; + sourceURL = null; + } + + public Builder setContext(Context context) { + this.context = context; + return this; + } + + public Builder setDestinationPath(File destinationPath) { + this.destinationPath = destinationPath; + return this; + } + + public Builder setSourceURL(String sourceURL) { + this.sourceURL = sourceURL; + return this; + } + + /** + * Adds a file for unpacking. Content of extracted file is not checked on each + * start against content of the file bundled in apk. + */ + public Builder unpackFile(String nameInApk, String destFileName) { + unpackers.add(new ExistenceCheckingUnpacker(nameInApk, destFileName)); + return this; + } + + /** + * Adds a file for unpacking. Content of extracted file is compared on each + * start with content of the same file bundled in apk. It is usefull for + * detecting bundle/app changes. + */ + public Builder checkAndUnpackFile(String nameInApk, String destFileName) { + unpackers.add(new ContentCheckingUnpacker(nameInApk, destFileName)); + return this; + } + + /** + * Adds arbitrary unpacker. Usefull for injecting mocks. + */ + Builder addUnpacker(Unpacker u) { + unpackers.add(u); + return this; + } + + public UnpackingJSBundleLoader build() { + Assertions.assertNotNull(destinationPath); + for (int i = 0; i < unpackers.size(); ++i) { + unpackers.get(i).setDestinationDirectory(destinationPath); + } + return new UnpackingJSBundleLoader(this); + } + } + + /** + * Abstraction for dealing with unpacking single file from apk. + */ + static abstract class Unpacker { + protected final String mNameInApk; + private final String mFileName; + protected @Nullable File mDestinationFilePath; + + public Unpacker(String nameInApk, String fileName) { + mNameInApk = nameInApk; + mFileName = fileName; + } + + public void setDestinationDirectory(File destinationDirectoryPath) { + mDestinationFilePath = new File(destinationDirectoryPath, mFileName); + } + + public abstract boolean shouldReconstructDir(Context context, byte[] ioBuffer) + throws IOException; + + public void unpack(Context context, byte[] ioBuffer) throws IOException { + AssetManager am = context.getAssets(); + try (InputStream is = am.open(mNameInApk, AssetManager.ACCESS_STREAMING)) { + try (FileOutputStream fileOutputStream = new FileOutputStream( + Assertions.assertNotNull(mDestinationFilePath))) { + copyBytes(fileOutputStream, is, ioBuffer, Integer.MAX_VALUE); + } + } + } + } + + /** + * Deals with unpacking files whose content is not checked on each start and + * need to be fsynced after unpacking. + */ + static class ExistenceCheckingUnpacker extends Unpacker { + public ExistenceCheckingUnpacker(String nameInApk, String fileName) { + super(nameInApk, fileName); + } + + @Override + public boolean shouldReconstructDir(Context context, byte[] ioBuffer) { + return !Assertions.assertNotNull(mDestinationFilePath).exists(); + } + + @Override + public void unpack(Context context, byte[] ioBuffer) throws IOException { + super.unpack(context, ioBuffer); + fsync(Assertions.assertNotNull(mDestinationFilePath)); + } + } + + /** + * Deals with unpacking files whose content is checked on each start and thus + * do not require fsync. + */ + static class ContentCheckingUnpacker extends Unpacker { + public ContentCheckingUnpacker(String nameInApk, String fileName) { + super(nameInApk, fileName); + } + + @Override + public boolean shouldReconstructDir(Context context, byte[] ioBuffer) throws IOException { + if (!Assertions.assertNotNull(mDestinationFilePath).exists()) { + return true; + } + + AssetManager am = context.getAssets(); + final byte[] assetContent; + try (InputStream assetStream = am.open(mNameInApk, AssetManager.ACCESS_STREAMING)) { + assetContent = readBytes(assetStream, ioBuffer, Integer.MAX_VALUE); + } + + final byte[] fileContent; + try (InputStream fileStream = new FileInputStream( + Assertions.assertNotNull(mDestinationFilePath))) { + fileContent = readBytes(fileStream, ioBuffer, assetContent.length + 1); + } + + return !Arrays.equals(assetContent, fileContent); + } + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/BUCK b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/BUCK new file mode 100644 index 00000000000000..22c014b0692ee7 --- /dev/null +++ b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/BUCK @@ -0,0 +1,40 @@ +include_defs('//ReactAndroid/DEFS') + +STANDARD_TEST_SRCS = [ + '*Test.java', +] + +android_library( + name = 'testhelpers', + srcs = glob(['*.java'], excludes = STANDARD_TEST_SRCS), + deps = [ + react_native_dep('third-party/java/junit:junit'), + react_native_dep('third-party/java/mockito:mockito'), + ], + visibility = [ + 'PUBLIC' + ], +) + +robolectric3_test( + name = 'bridge', + # Please change the contact to the oncall of your team + contacts = ['oncall+fbandroid_sheriff@xmail.facebook.com'], + srcs = glob(STANDARD_TEST_SRCS), + deps = [ + ':testhelpers', + react_native_dep('libraries/fbcore/src/test/java/com/facebook/powermock:powermock'), + react_native_dep('libraries/soloader/java/com/facebook/soloader:soloader'), + react_native_dep('third-party/java/junit:junit'), + react_native_dep('third-party/java/mockito:mockito'), + react_native_dep('third-party/java/robolectric3/robolectric:robolectric'), + react_native_target('java/com/facebook/react/cxxbridge:bridge'), + ], + visibility = [ + 'PUBLIC' + ], +) + +project_config( + test_target = ':bridge', +) diff --git a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ContentCheckingUnpackerTest.java b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ContentCheckingUnpackerTest.java new file mode 100644 index 00000000000000..23ba3ca67f8509 --- /dev/null +++ b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ContentCheckingUnpackerTest.java @@ -0,0 +1,85 @@ +/** + * 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.cxxbridge; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.Test; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + +@PrepareForTest({UnpackingJSBundleLoader.class}) +@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" }) +@RunWith(RobolectricTestRunner.class) +public class ContentCheckingUnpackerTest extends UnpackerTestBase { + @Rule + public PowerMockRule rule = new PowerMockRule(); + + private UnpackingJSBundleLoader.ContentCheckingUnpacker mUnpacker; + + @Before + public void setUp() throws IOException { + super.setUp(); + mUnpacker = new UnpackingJSBundleLoader.ContentCheckingUnpacker( + NAME_IN_APK, + DESTINATION_NAME); + mUnpacker.setDestinationDirectory(folder.getRoot()); + } + + @Test + public void testReconstructsIfFileDoesNotExist() throws IOException { + assertTrue(mUnpacker.shouldReconstructDir(mContext, mIOBuffer)); + } + + @Test + public void testReconstructsIfContentDoesNotMatch() throws IOException { + try (FileOutputStream fos = new FileOutputStream(mDestinationPath)) { + fos.write(ASSET_DATA, 0, ASSET_DATA.length - 1); + fos.write((byte) (ASSET_DATA[ASSET_DATA.length - 1] + 1)); + } + assertTrue(mUnpacker.shouldReconstructDir(mContext, mIOBuffer)); + } + + @Test + public void testDoesNotReconstructIfContentMatches() throws IOException { + try (FileOutputStream fos = new FileOutputStream(mDestinationPath)) { + fos.write(ASSET_DATA); + } + assertFalse(mUnpacker.shouldReconstructDir(mContext, mIOBuffer)); + } + + @Test + public void testUnpacksFile() throws IOException { + mUnpacker.unpack(mContext, mIOBuffer); + assertTrue(mDestinationPath.exists()); + try (InputStream is = new FileInputStream(mDestinationPath)) { + byte[] storedData = UnpackingJSBundleLoader.readBytes(is, mIOBuffer, Integer.MAX_VALUE); + assertArrayEquals(ASSET_DATA, storedData); + } + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ExistenceCheckingUnpackerTest.java b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ExistenceCheckingUnpackerTest.java new file mode 100644 index 00000000000000..2608bcab3dc437 --- /dev/null +++ b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/ExistenceCheckingUnpackerTest.java @@ -0,0 +1,78 @@ +/** + * 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.cxxbridge; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.Test; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + +@PrepareForTest({UnpackingJSBundleLoader.class}) +@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" }) +@RunWith(RobolectricTestRunner.class) +public class ExistenceCheckingUnpackerTest extends UnpackerTestBase { + @Rule + public PowerMockRule rule = new PowerMockRule(); + + private UnpackingJSBundleLoader.ExistenceCheckingUnpacker mUnpacker; + + @Before + public void setUp() throws IOException { + super.setUp(); + mUnpacker = new UnpackingJSBundleLoader.ExistenceCheckingUnpacker( + NAME_IN_APK, + DESTINATION_NAME); + mUnpacker.setDestinationDirectory(folder.getRoot()); + } + + @Test + public void testReconstructsIfFileDoesNotExist() { + assertTrue(mUnpacker.shouldReconstructDir(mContext, mIOBuffer)); + } + + @Test + public void testDoesNotReconstructIfFileExists() throws IOException { + mDestinationPath.createNewFile(); + assertFalse(mUnpacker.shouldReconstructDir(mContext, mIOBuffer)); + } + + @Test + public void testUnpacksFile() throws IOException { + mUnpacker.unpack(mContext, mIOBuffer); + assertTrue(mDestinationPath.exists()); + try (InputStream is = new FileInputStream(mDestinationPath)) { + byte[] storedData = UnpackingJSBundleLoader.readBytes(is, mIOBuffer, Integer.MAX_VALUE); + assertArrayEquals(ASSET_DATA, storedData); + } + } + + @Test + public void testFsyncsAfterUnpacking() throws IOException { + mockStatic(UnpackingJSBundleLoader.class); + mUnpacker.unpack(mContext, mIOBuffer); + verifyStatic(times(1)); + UnpackingJSBundleLoader.fsync(mDestinationPath); + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackerTestBase.java b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackerTestBase.java new file mode 100644 index 00000000000000..a2865a612ab868 --- /dev/null +++ b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackerTestBase.java @@ -0,0 +1,95 @@ +/** + * 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.cxxbridge; + +import android.content.Context; +import android.content.res.AssetManager; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class UnpackerTestBase { + static final String NAME_IN_APK = "nameInApk"; + static final String DESTINATION_NAME = "destination"; + static final byte[] ASSET_DATA = new byte[]{(byte) 1, (byte) 101, (byte) 50}; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + File mDestinationPath; + byte[] mIOBuffer; + + Context mContext; + AssetManager mAssetManager; + + public void setUp() throws IOException { + mDestinationPath = new File(folder.getRoot(), DESTINATION_NAME); + mIOBuffer = new byte[16 * 1024]; + + mContext = mock(Context.class); + mAssetManager = mock(AssetManager.class); + + when(mContext.getAssets()).thenReturn(mAssetManager); + when(mAssetManager.open(eq(NAME_IN_APK), anyInt())) + .then(new Answer() { + @Override + public FileInputStream answer(InvocationOnMock invocation) throws Throwable { + final ByteArrayInputStream bais = new ByteArrayInputStream(ASSET_DATA); + final FileInputStream fis = mock(FileInputStream.class); + when(fis.read()) + .then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bais.read(); + } + }); + when(fis.read(any(byte[].class))) + .then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bais.read((byte[]) invocation.getArguments()[0]); + } + }); + when(fis.read(any(byte[].class), any(int.class), any(int.class))) + .then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bais.read( + (byte[]) invocation.getArguments()[0], + (int) invocation.getArguments()[1], + (int) invocation.getArguments()[2]); + } + }); + when(fis.available()).then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bais.available(); + } + }); + return fis; + } + }); + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java new file mode 100644 index 00000000000000..d6a2b9c66f493a --- /dev/null +++ b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java @@ -0,0 +1,183 @@ +/** + * 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.cxxbridge; + +import android.content.Context; + +import com.facebook.soloader.SoLoader; + +import java.io.File; +import java.io.IOException; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.Test; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@RunWith(RobolectricTestRunner.class) +public class UnpackingJSBundleLoaderTest { + static { + SoLoader.setInTestMode(); + } + + private static final String URL = "http://this.is.an.url"; + private static final int MOCK_UNPACKERS_NUM = 2; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private File mDestinationPath; + private File mFilesPath; + + private UnpackingJSBundleLoader.Builder mBuilder; + private Context mContext; + private CatalystInstanceImpl mCatalystInstanceImpl; + private UnpackingJSBundleLoader.Unpacker[] mMockUnpackers; + + @Before + public void setUp() throws IOException { + mDestinationPath = folder.newFolder("destination"); + mFilesPath = folder.newFolder("files"); + + mContext = mock(Context.class); + when(mContext.getFilesDir()).thenReturn(mFilesPath); + + mCatalystInstanceImpl = mock(CatalystInstanceImpl.class); + + mBuilder = UnpackingJSBundleLoader.newBuilder() + .setDestinationPath(mDestinationPath) + .setSourceURL(URL) + .setContext(mContext); + + mMockUnpackers = new UnpackingJSBundleLoader.Unpacker[MOCK_UNPACKERS_NUM]; + for (int i = 0; i < mMockUnpackers.length; ++i) { + mMockUnpackers[i] = mock(UnpackingJSBundleLoader.Unpacker.class); + } + } + + private void addUnpackers() { + for (UnpackingJSBundleLoader.Unpacker unpacker : mMockUnpackers) { + mBuilder.addUnpacker(unpacker); + } + } + + @Test + public void testGetSourceUrl() { + assertEquals(URL, mBuilder.build().getSourceUrl()); + } + + @Test + public void testCreatesDotUnpackedFile() throws IOException { + mBuilder.build().prepare(); + assertTrue(new File(mDestinationPath, UnpackingJSBundleLoader.DOT_UNPACKED_FILE).exists()); + } + + @Test + public void testCreatesLockFile() throws IOException { + mBuilder.build().prepare(); + assertTrue(new File(mFilesPath, UnpackingJSBundleLoader.LOCK_FILE).exists()); + } + + @Test + public void testCallsAppropriateInstanceMethod() throws IOException { + mBuilder.build().loadScript(mCatalystInstanceImpl); + verify(mCatalystInstanceImpl).loadScriptFromFile( + eq(new File(mDestinationPath, "bundle.js").getPath()), + eq(URL)); + verifyNoMoreInteractions(mCatalystInstanceImpl); + } + + @Test + public void testLoadScriptUnpacks() { + mBuilder.build().loadScript(mCatalystInstanceImpl); + assertTrue(new File(mDestinationPath, UnpackingJSBundleLoader.DOT_UNPACKED_FILE).exists()); + } + + @Test + public void testPrepareCallDoesNotRecreateDirIfNotNecessary() throws IOException { + mBuilder.build().prepare(); + + addUnpackers(); + mBuilder.build().prepare(); + for (UnpackingJSBundleLoader.Unpacker unpacker : mMockUnpackers) { + verify(unpacker).setDestinationDirectory(mDestinationPath); + verify(unpacker).shouldReconstructDir( + same(mContext), + any(byte[].class)); + verifyNoMoreInteractions(unpacker); + } + } + + @Test + public void testShouldReconstructDirForcesRecreation() throws IOException { + mBuilder.build().prepare(); + + addUnpackers(); + when(mMockUnpackers[0].shouldReconstructDir( + same(mContext), + any(byte[].class))) + .thenReturn(true); + mBuilder.build().prepare(); + + verify(mMockUnpackers[0]).shouldReconstructDir( + same(mContext), + any(byte[].class)); + for (UnpackingJSBundleLoader.Unpacker unpacker : mMockUnpackers) { + verify(unpacker).setDestinationDirectory(mDestinationPath); + verify(unpacker).unpack( + same(mContext), + any(byte[].class)); + verifyNoMoreInteractions(unpacker); + } + } + + @Test + public void testDirectoryReconstructionRemovesDir() throws IOException { + mBuilder.build().prepare(); + final File aFile = new File(mDestinationPath, "a_file"); + aFile.createNewFile(); + + when(mMockUnpackers[0].shouldReconstructDir( + same(mContext), + any(byte[].class))) + .thenReturn(true); + addUnpackers(); + mBuilder.build().prepare(); + + assertFalse(aFile.exists()); + } + + @Test(expected = RuntimeException.class) + public void testDropsDirectoryOnException() throws IOException { + doThrow(new IOException("An expected IOException")) + .when(mMockUnpackers[0]).unpack( + same(mContext), + any(byte[].class)); + try { + mBuilder.addUnpacker(mMockUnpackers[0]).build().prepare(); + } finally { + assertFalse(mDestinationPath.exists()); + } + } +} From 1331e20db5c3ba890a6b94a369b2554782f8dac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Gregorczyk?= Date: Tue, 12 Jul 2016 08:03:09 -0700 Subject: [PATCH 091/241] add API to CatalystInstanceImpl for executing optimized bundle Reviewed By: tadeuzagallo Differential Revision: D3545345 fbshipit-source-id: 538fec77b816c3fd767e8c2eda81c78971996b17 --- .../react/cxxbridge/CatalystInstanceImpl.java | 3 +- .../react/cxxbridge/JSBundleLoader.java | 9 +- .../cxxbridge/UnpackingJSBundleLoader.java | 14 +- .../jni/xreact/jni/CatalystInstanceImpl.cpp | 132 ++---------------- .../jni/xreact/jni/CatalystInstanceImpl.h | 3 +- .../src/main/jni/xreact/jni/OnLoad.cpp | 8 +- ReactAndroid/src/main/jni/xreact/jni/OnLoad.h | 1 - .../UnpackingJSBundleLoaderTest.java | 7 +- ReactCommon/cxxreact/Android.mk | 1 + ReactCommon/cxxreact/BUCK | 1 + ReactCommon/cxxreact/Executor.cpp | 77 ++++++++++ ReactCommon/cxxreact/Executor.h | 16 +++ ReactCommon/cxxreact/Instance.cpp | 10 ++ ReactCommon/cxxreact/Instance.h | 1 + ReactCommon/cxxreact/JSCExecutor.cpp | 48 ++++--- ReactCommon/cxxreact/JSCExecutor.h | 6 + ReactCommon/cxxreact/NativeToJsBridge.cpp | 14 ++ ReactCommon/cxxreact/NativeToJsBridge.h | 6 + 18 files changed, 199 insertions(+), 158 deletions(-) create mode 100644 ReactCommon/cxxreact/Executor.cpp 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 569ae980ed3476..7bef0a38b70796 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java @@ -161,8 +161,9 @@ private native void initializeBridge(ReactCallback callback, MessageQueueThread moduleQueue, ModuleRegistryHolder registryHolder); - /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean useLazyBundle); + /* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL); /* package */ native void loadScriptFromFile(String fileName, String sourceURL); + /* package */ native void loadScriptFromOptimizedBundle(String path, String sourceURL, int flags); @Override public void runJSBundle() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java index 6615c75c8aa9c1..311b4fa41c3074 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JSBundleLoader.java @@ -30,18 +30,11 @@ public abstract class JSBundleLoader { public static JSBundleLoader createFileLoader( final Context context, final String fileName) { - return createFileLoader(context, fileName, false); - } - - public static JSBundleLoader createFileLoader( - final Context context, - final String fileName, - final boolean useLazyBundle) { return new JSBundleLoader() { @Override public void loadScript(CatalystInstanceImpl instance) { if (fileName.startsWith("assets://")) { - instance.loadScriptFromAssets(context.getAssets(), fileName, useLazyBundle); + instance.loadScriptFromAssets(context.getAssets(), fileName); } else { instance.loadScriptFromFile(fileName, fileName); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java index ed65860ff56150..722b3c59668cf4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoader.java @@ -38,6 +38,12 @@ */ public class UnpackingJSBundleLoader extends JSBundleLoader { + /** + * Flag passed to loadScriptFromOptimizedBundle to let the bridge know that + * the unpacked unpacked js source file. + */ + static final int UNPACKED_JS_SOURCE = (1 << 0); + /** * Name of the lock files. Multiple processes can be spawned off the same app * and we need to guarantee that at most one unpacks files at any time. To @@ -141,10 +147,10 @@ private void prepareLocked() throws IOException { @Override public void loadScript(CatalystInstanceImpl instance) { prepare(); - // TODO(12128379): add instance method that would take bundle directory - instance.loadScriptFromFile( - new File(mDirectoryPath, "bundle.js").getPath(), - mSourceURL); + instance.loadScriptFromOptimizedBundle( + mDirectoryPath.getPath(), + mSourceURL, + UNPACKED_JS_SOURCE); } @Override diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp index 612107b7cc684c..196e333a549fdf 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp @@ -13,8 +13,6 @@ #include #include -#include - #include #include #include @@ -25,7 +23,6 @@ #include "ModuleRegistryHolder.h" #include "NativeArray.h" #include "JNativeRunnable.h" -#include "OnLoad.h" using namespace facebook::jni; @@ -101,9 +98,11 @@ void CatalystInstanceImpl::registerNatives() { makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge), makeNativeMethod("loadScriptFromAssets", - "(Landroid/content/res/AssetManager;Ljava/lang/String;Z)V", + "(Landroid/content/res/AssetManager;Ljava/lang/String;)V", CatalystInstanceImpl::loadScriptFromAssets), makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile), + makeNativeMethod("loadScriptFromOptimizedBundle", + CatalystInstanceImpl::loadScriptFromOptimizedBundle), makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction), makeNativeMethod("callJSCallback", CatalystInstanceImpl::callJSCallback), makeNativeMethod("getMainExecutorToken", CatalystInstanceImpl::getMainExecutorToken), @@ -152,131 +151,20 @@ void CatalystInstanceImpl::initializeBridge( mrh->getModuleRegistry()); } -#ifdef WITH_FBJSCEXTENSIONS -static std::unique_ptr loadScriptFromCache( - AAssetManager* manager, - std::string& sourceURL) { - - // 20-byte sha1 as hex - static const size_t HASH_STR_SIZE = 40; - - // load bundle hash from the metadata file in the APK - auto hash = react::loadScriptFromAssets(manager, sourceURL + ".meta"); - auto cacheDir = getApplicationCacheDir() + "/rn-bundle"; - auto encoding = static_cast(hash->c_str()[20]); - - if (mkdir(cacheDir.c_str(), 0755) == -1 && errno != EEXIST) { - throw std::runtime_error("Can't create cache directory"); - } - - if (encoding != JSBigMmapString::Encoding::Ascii) { - throw std::runtime_error("Can't use mmap fastpath for non-ascii bundles"); - } - - // convert hash to string - char hashStr[HASH_STR_SIZE + 1]; - for (size_t i = 0; i < HASH_STR_SIZE; i += 2) { - snprintf(hashStr + i, 3, "%02hhx", hash->c_str()[i / 2] & 0xFF); - } - - // the name of the cached bundle file should be the hash - std::string cachePath = cacheDir + "/" + hashStr; - FILE *cache = fopen(cachePath.c_str(), "r"); - SCOPE_EXIT { if (cache) fclose(cache); }; - - size_t size = 0; - if (cache == NULL) { - // delete old bundle, if there was one. - std::string metaPath = cacheDir + "/meta"; - if (auto meta = fopen(metaPath.c_str(), "r")) { - char oldBundleHash[HASH_STR_SIZE + 1]; - if (fread(oldBundleHash, HASH_STR_SIZE, 1, meta) == HASH_STR_SIZE) { - remove((cacheDir + "/" + oldBundleHash).c_str()); - remove(metaPath.c_str()); - } - fclose(meta); - } - - // load script from the APK and write to temporary file - auto script = react::loadScriptFromAssets(manager, sourceURL); - auto tmpPath = cachePath + "_"; - cache = fopen(tmpPath.c_str(), "w"); - if (!cache) { - throw std::runtime_error("Can't open cache, errno: " + errno); - } - if (fwrite(script->c_str(), 1, script->size(), cache) != size) { - remove(tmpPath.c_str()); - throw std::runtime_error("Failed to unpack bundle"); - } - - // force data to be written to disk - fsync(fileno(cache)); - fclose(cache); - - // move script to final path - atomic operation - if (rename(tmpPath.c_str(), cachePath.c_str())) { - throw std::runtime_error("Failed to update cache, errno: " + errno); - } - - // store the bundle hash in a metadata file - auto meta = fopen(metaPath.c_str(), "w"); - if (!meta) { - throw std::runtime_error("Failed to open metadata file to store bundle hash"); - } - if (fwrite(hashStr, HASH_STR_SIZE, 1, meta) != HASH_STR_SIZE) { - throw std::runtime_error("Failed to write bundle hash to metadata file"); - } - fsync(fileno(meta)); - fclose(meta); - - // return the final written cache - cache = fopen(cachePath.c_str(), "r"); - if (!cache) { - throw std::runtime_error("Cache has been cleared"); - } - } else { - struct stat fileInfo = {0}; - if (fstat(fileno(cache), &fileInfo)) { - throw std::runtime_error("Failed to get cache stats, errno: " + errno); - } - size = fileInfo.st_size; - } - - return folly::make_unique( - dup(fileno(cache)), - size, - reinterpret_cast(hash->c_str()), - encoding); -} -#endif - void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager, - const std::string& assetURL, - bool useLazyBundle) { + const std::string& assetURL) { const int kAssetsLength = 9; // strlen("assets://"); auto sourceURL = assetURL.substr(kAssetsLength); - auto manager = react::extractAssetManager(assetManager); + auto manager = react::extractAssetManager(assetManager); + auto script = react::loadScriptFromAssets(manager, sourceURL); if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) { - auto script = react::loadScriptFromAssets(manager, sourceURL); instance_->loadUnbundle( folly::make_unique(manager, sourceURL), std::move(script), sourceURL); return; } else { -#ifdef WITH_FBJSCEXTENSIONS - if (useLazyBundle) { - try { - auto script = loadScriptFromCache(manager, sourceURL); - instance_->loadScriptFromString(std::move(script), sourceURL); - return; - } catch (...) { - LOG(WARNING) << "Failed to load bundle as Source Code"; - } - } -#endif - auto script = react::loadScriptFromAssets(manager, sourceURL); instance_->loadScriptFromString(std::move(script), sourceURL); } } @@ -287,6 +175,14 @@ void CatalystInstanceImpl::loadScriptFromFile(jni::alias_ref fileName, sourceURL); } +void CatalystInstanceImpl::loadScriptFromOptimizedBundle(const std::string& bundlePath, + const std::string& sourceURL, + jint flags) { + return instance_->loadScriptFromOptimizedBundle(std::move(bundlePath), + std::move(sourceURL), + flags); +} + void CatalystInstanceImpl::callJSFunction( JExecutorToken* token, std::string module, std::string method, NativeArray* arguments) { // We want to share the C++ code, and on iOS, modules pass module/method diff --git a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h index 64835e6b461d1c..d0221af9783e04 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h @@ -47,8 +47,9 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref jsQueue, jni::alias_ref moduleQueue, ModuleRegistryHolder* mrh); - void loadScriptFromAssets(jobject assetManager, const std::string& assetURL, bool useLazyBundle); + void loadScriptFromAssets(jobject assetManager, const std::string& assetURL); void loadScriptFromFile(jni::alias_ref fileName, const std::string& sourceURL); + void loadScriptFromOptimizedBundle(const std::string& bundlePath, const std::string& sourceURL, jint flags); void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments); void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments); local_ref getMainExecutorToken(); diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp index 67e167b4eaa9c8..952a6b46c4c4ff 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp @@ -51,6 +51,10 @@ static std::string getApplicationDir(const char* methodName) { return getAbsolutePathMethod(dirObj)->toStdString(); } +static std::string getApplicationCacheDir() { + return getApplicationDir("getCacheDir"); +} + static std::string getApplicationPersistentDir() { return getApplicationDir("getFilesDir"); } @@ -158,10 +162,6 @@ class JReactMarker : public JavaClass { } -std::string getApplicationCacheDir() { - return getApplicationDir("getCacheDir"); -} - extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return initialize(vm, [] { // Inject some behavior into react/ diff --git a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h index 713d8b582daf36..5cd3c1749e62bf 100644 --- a/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h +++ b/ReactAndroid/src/main/jni/xreact/jni/OnLoad.h @@ -10,6 +10,5 @@ namespace facebook { namespace react { jmethodID getLogMarkerMethod(); -std::string getApplicationCacheDir(); } // namespace react } // namespace facebook diff --git a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java index d6a2b9c66f493a..016ecb2012b045 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/cxxbridge/UnpackingJSBundleLoaderTest.java @@ -102,9 +102,10 @@ public void testCreatesLockFile() throws IOException { @Test public void testCallsAppropriateInstanceMethod() throws IOException { mBuilder.build().loadScript(mCatalystInstanceImpl); - verify(mCatalystInstanceImpl).loadScriptFromFile( - eq(new File(mDestinationPath, "bundle.js").getPath()), - eq(URL)); + verify(mCatalystInstanceImpl).loadScriptFromOptimizedBundle( + eq(mDestinationPath.getPath()), + eq(URL), + eq(UnpackingJSBundleLoader.UNPACKED_JS_SOURCE)); verifyNoMoreInteractions(mCatalystInstanceImpl); } diff --git a/ReactCommon/cxxreact/Android.mk b/ReactCommon/cxxreact/Android.mk index 985cc035752b11..30cd232dbac745 100644 --- a/ReactCommon/cxxreact/Android.mk +++ b/ReactCommon/cxxreact/Android.mk @@ -5,6 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libreactnativefb LOCAL_SRC_FILES := \ + Executor.cpp \ Instance.cpp \ JSCExecutor.cpp \ JSCHelpers.cpp \ diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index 14a5b1e77487ba..46b19ea4e34c9a 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -109,6 +109,7 @@ react_library( force_static = True, srcs = [ 'CxxMessageQueue.cpp', + 'Executor.cpp', 'Instance.cpp', 'JSCExecutor.cpp', 'JSCHelpers.cpp', diff --git a/ReactCommon/cxxreact/Executor.cpp b/ReactCommon/cxxreact/Executor.cpp new file mode 100644 index 00000000000000..15f7730b407618 --- /dev/null +++ b/ReactCommon/cxxreact/Executor.cpp @@ -0,0 +1,77 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "Executor.h" + +#include +#include +#include +#include +#include + +#include + +namespace facebook { +namespace react { + +void JSExecutor::loadApplicationScript(std::string bundlePath, std::string sourceURL, int flags) { + if ((flags & UNPACKED_JS_SOURCE) == 0) { + throw std::runtime_error("No unpacked js source file"); + } + return loadApplicationScript( + JSBigMmapString::fromOptimizedBundle(bundlePath), + std::move(sourceURL)); +} + +static JSBigMmapString::Encoding encodingFromByte(uint8_t byte) { + switch (byte) { + case 0: + return JSBigMmapString::Encoding::Unknown; + case 1: + return JSBigMmapString::Encoding::Ascii; + case 2: + return JSBigMmapString::Encoding::Utf8; + case 3: + return JSBigMmapString::Encoding::Utf16; + default: + throw std::invalid_argument("Unknown bundle encoding"); + } +} + +std::unique_ptr JSBigMmapString::fromOptimizedBundle( + const std::string& bundlePath) { + uint8_t sha1[20]; + uint8_t encoding; + struct stat fileInfo; + int fd = -1; + SCOPE_FAIL { CHECK(fd == -1 || ::close(fd) == 0); }; + + { + auto metaPath = bundlePath + UNPACKED_META_PATH_SUFFIX; + std::ifstream metaFile; + metaFile.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit); + metaFile.open(metaPath, std::ifstream::in | std::ifstream::binary); + metaFile.read(reinterpret_cast(sha1), sizeof(sha1)); + metaFile.read(reinterpret_cast(&encoding), sizeof(encoding)); + } + + { + auto sourcePath = bundlePath + UNPACKED_JS_SOURCE_PATH_SUFFIX; + fd = ::open(sourcePath.c_str(), O_RDONLY); + if (fd == -1) { + throw std::runtime_error(std::string("could not open js bundle file: ") + ::strerror(errno)); + } + } + + if (::fstat(fd, &fileInfo)) { + throw std::runtime_error(std::string("fstat on js bundle failed: ") + strerror(errno)); + } + + return folly::make_unique( + fd, + fileInfo.st_size, + sha1, + encodingFromByte(encoding)); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/Executor.h b/ReactCommon/cxxreact/Executor.h index 6b99df0b2cc1d0..62b2004c56e525 100644 --- a/ReactCommon/cxxreact/Executor.h +++ b/ReactCommon/cxxreact/Executor.h @@ -16,6 +16,13 @@ namespace facebook { namespace react { +#define UNPACKED_JS_SOURCE_PATH_SUFFIX "/bundle.js" +#define UNPACKED_META_PATH_SUFFIX "/bundle.meta" + +enum { + UNPACKED_JS_SOURCE = (1 << 0), +}; + class JSExecutor; class MessageQueueThread; @@ -190,6 +197,8 @@ class JSBigMmapString : public JSBigString { return m_encoding; } + static std::unique_ptr fromOptimizedBundle(const std::string& bundlePath); + private: int m_fd; size_t m_size; @@ -206,6 +215,11 @@ class JSExecutor { virtual void loadApplicationScript(std::unique_ptr script, std::string sourceURL) = 0; + /** + * Execute an application script optimized bundle in the JS context. + */ + virtual void loadApplicationScript(std::string bundlePath, std::string source, int flags); + /** * Add an application "unbundle" file */ @@ -245,4 +259,6 @@ class JSExecutor { virtual ~JSExecutor() {} }; +std::unique_ptr readJSBundle(const std::string& path); + } } diff --git a/ReactCommon/cxxreact/Instance.cpp b/ReactCommon/cxxreact/Instance.cpp index cbc8e656bc39fe..eb5e31d949848a 100644 --- a/ReactCommon/cxxreact/Instance.cpp +++ b/ReactCommon/cxxreact/Instance.cpp @@ -75,6 +75,16 @@ void Instance::loadScriptFromFile(const std::string& filename, loadScriptFromString(std::move(buf), sourceURL); } +void Instance::loadScriptFromOptimizedBundle(std::string bundlePath, + std::string sourceURL, + int flags) { + SystraceSection s("reactbridge_xplat_loadScriptFromOptimizedBundle", + "bundlePath", bundlePath); + nativeToJsBridge_->loadOptimizedApplicationScript(std::move(bundlePath), + std::move(sourceURL), + flags); +} + void Instance::loadUnbundle(std::unique_ptr unbundle, std::unique_ptr startupScript, std::string startupScriptSourceURL) { diff --git a/ReactCommon/cxxreact/Instance.h b/ReactCommon/cxxreact/Instance.h index fada0a7cfba7af..1e53c81f2380a6 100644 --- a/ReactCommon/cxxreact/Instance.h +++ b/ReactCommon/cxxreact/Instance.h @@ -36,6 +36,7 @@ class Instance { std::shared_ptr moduleRegistry); void loadScriptFromString(std::unique_ptr string, std::string sourceURL); void loadScriptFromFile(const std::string& filename, const std::string& sourceURL); + void loadScriptFromOptimizedBundle(std::string bundlePath, std::string sourceURL, int flags); void loadUnbundle( std::unique_ptr unbundle, std::unique_ptr startupScript, diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp index d26409c2e0bcc7..67b17a666740ef 100644 --- a/ReactCommon/cxxreact/JSCExecutor.cpp +++ b/ReactCommon/cxxreact/JSCExecutor.cpp @@ -254,15 +254,37 @@ void JSCExecutor::terminateOnJSVMThread() { } #ifdef WITH_FBJSCEXTENSIONS -static void loadApplicationSource( - const JSGlobalContextRef context, - const JSBigMmapString* script, - const std::string& sourceURL) { +void JSCExecutor::loadApplicationScript( + std::string bundlePath, + std::string sourceURL, + int flags) { + SystraceSection s("JSCExecutor::loadApplicationScript", + "sourceURL", sourceURL); + + if ((flags & UNPACKED_JS_SOURCE) == 0) { + throw std::runtime_error("Optimized bundle with no unpacked js source"); + } + + auto jsScriptBigString = JSBigMmapString::fromOptimizedBundle(bundlePath); + if (jsScriptBigString->encoding() != JSBigMmapString::Encoding::Ascii) { + throw std::runtime_error("Optimized bundle needs to be ascii encoded"); + } + String jsSourceURL(sourceURL.c_str()); - bool is8bit = script->encoding() == JSBigMmapString::Encoding::Ascii || script->encoding() == JSBigMmapString::Encoding::Utf8; - JSSourceCodeRef sourceCode = JSCreateSourceCode(script->fd(), script->size(), jsSourceURL, script->hash(), is8bit); - evaluateSourceCode(context, sourceCode, jsSourceURL); - JSReleaseSourceCode(sourceCode); + JSSourceCodeRef sourceCode = JSCreateSourceCode( + jsScriptBigString->fd(), + jsScriptBigString->size(), + jsSourceURL, + jsScriptBigString->hash(), + true); + SCOPE_EXIT { JSReleaseSourceCode(sourceCode); }; + + evaluateSourceCode(m_context, sourceCode, jsSourceURL); + + bindBridge(); + + flush(); + ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); } #endif @@ -270,16 +292,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr scrip SystraceSection s("JSCExecutor::loadApplicationScript", "sourceURL", sourceURL); - #ifdef WITH_FBJSCEXTENSIONS - if (auto source = dynamic_cast(script.get())) { - loadApplicationSource(m_context, source, sourceURL); - bindBridge(); - flush(); - ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); - return; - } - #endif - #ifdef WITH_FBSYSTRACE fbsystrace_begin_section( TRACE_TAG_REACT_CXX_BRIDGE, diff --git a/ReactCommon/cxxreact/JSCExecutor.h b/ReactCommon/cxxreact/JSCExecutor.h index b1dfcdb56d523f..b02c7e5b666420 100644 --- a/ReactCommon/cxxreact/JSCExecutor.h +++ b/ReactCommon/cxxreact/JSCExecutor.h @@ -59,6 +59,12 @@ class JSCExecutor : public JSExecutor { virtual void loadApplicationScript( std::unique_ptr script, std::string sourceURL) throw(JSException) override; +#ifdef WITH_FBJSCEXTENSIONS + virtual void loadApplicationScript( + std::string bundlePath, + std::string sourceURL, + int flags) override; +#endif virtual void setJSModulesUnbundle( std::unique_ptr unbundle) override; virtual void callFunction( diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 8da6bb0fce6890..ecddf32e230113 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -124,6 +124,20 @@ void NativeToJsBridge::loadApplicationScript(std::unique_ptr m_mainExecutor->loadApplicationScript(std::move(script), std::move(sourceURL)); } +void NativeToJsBridge::loadOptimizedApplicationScript( + std::string bundlePath, + std::string sourceURL, + int flags) { + runOnExecutorQueue( + m_mainExecutorToken, + [bundlePath=std::move(bundlePath), + sourceURL=std::move(sourceURL), + flags=flags] + (JSExecutor* executor) { + executor->loadApplicationScript(std::move(bundlePath), std::move(sourceURL), flags); + }); +} + void NativeToJsBridge::loadApplicationUnbundle( std::unique_ptr unbundle, std::unique_ptr startupScript, diff --git a/ReactCommon/cxxreact/NativeToJsBridge.h b/ReactCommon/cxxreact/NativeToJsBridge.h index 786c268675b064..e953673788daeb 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.h +++ b/ReactCommon/cxxreact/NativeToJsBridge.h @@ -84,6 +84,12 @@ class NativeToJsBridge { */ void loadApplicationScript(std::unique_ptr script, std::string sourceURL); + /** + * Similar to loading a "bundle", but instead of passing js source this method accepts + * path to a directory containing files prepared for particular JSExecutor. + */ + void loadOptimizedApplicationScript(std::string bundlePath, std::string sourceURL, int flags); + /** * An "unbundle" is a backend that stores and injects JavaScript modules as * individual scripts, rather than bundling all of them into a single scrupt. From 0d3c4f8e5e1f3f0303bcf4dd40dff8e42100da8b Mon Sep 17 00:00:00 2001 From: Konstantin Raev Date: Tue, 12 Jul 2016 08:43:59 -0700 Subject: [PATCH 092/241] Temporarily enable ReactHorizontalScrollViewTestCase to run in SandCastle Reviewed By: emilsjolander Differential Revision: D3548925 fbshipit-source-id: f56a812316470f1ee4b6a53a83b439bf66f5035f --- .../src/androidTest/java/com/facebook/react/tests/BUCK | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 8f16de6cb9958f..f052dc129744b9 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -3,7 +3,6 @@ include_defs('//ReactAndroid/DEFS') # Tests that are too flaky to run on SandCastle # TODO t11057216 stabilise them SANDCASTLE_FLAKY = [ - 'ReactHorizontalScrollViewTestCase.java', 'ReactScrollViewTestCase.java', ] @@ -36,10 +35,10 @@ deps = [ android_library( name = 'tests', srcs = glob(['**/*.java']), + deps = deps, visibility = [ 'PUBLIC', ], - deps = deps, ) android_library( From c33084cc94c44493a39085932dbbeb7678fde342 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Tue, 12 Jul 2016 10:11:42 -0700 Subject: [PATCH 093/241] Read within bounds of `NSData` object Summary: This prevents the `isUnbundle` check to read beyond the end of an `NSData` instance by using `getBytes:length:` instead of accessing the underlying buffer directly. Reviewed By: javache Differential Revision: D3548874 fbshipit-source-id: 7c93c66cc6abb4a2a321888ab394212f4d14a03e --- React/Executors/RCTJSCExecutor.mm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/React/Executors/RCTJSCExecutor.mm b/React/Executors/RCTJSCExecutor.mm index 7359c03687af2f..deab6281e709d3 100644 --- a/React/Executors/RCTJSCExecutor.mm +++ b/React/Executors/RCTJSCExecutor.mm @@ -730,8 +730,9 @@ - (void)executeApplicationScript:(NSData *)script NSError **error) { // The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`. - uint32_t magicNumber = NSSwapLittleIntToHost(*((uint32_t *)script.bytes)); - isRAMBundle = magicNumber == RCTRAMBundleMagicNumber; + uint32_t magicNumber = 0; + [script getBytes:&magicNumber length:sizeof(magicNumber)]; + isRAMBundle = NSSwapLittleIntToHost(magicNumber) == RCTRAMBundleMagicNumber; if (isRAMBundle) { [performanceLogger markStartForTag:RCTPLRAMBundleLoad]; script = loadRAMBundle(sourceURL, error, randomAccessBundle); From cb31d4cb6d2a9b8e4e04c6abb7729c74fb8f86a1 Mon Sep 17 00:00:00 2001 From: Joel Marcey Date: Tue, 12 Jul 2016 12:13:36 -0700 Subject: [PATCH 094/241] Use tabIndex instead of tabindex Summary: There is a little bit of dogscience here, but I believe JSX is wanting `tabIndex` instead of `tabindex`. We have `tabindex` as an attribute in our Algolia search; changing it to `tabIndex` removes the warning. **Before** screenshot 2016-07-12 11 05 47 **After** screenshot 2016-07-12 11 09 47 Closes https://github.com/facebook/react-native/pull/8721 Differential Revision: D3550398 fbshipit-source-id: b7821d1940cd4c7880ee6fb907bcb16b44087750 --- website/core/AlgoliaDocSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/core/AlgoliaDocSearch.js b/website/core/AlgoliaDocSearch.js index 16b60fd707a816..69766e5aa1c6c6 100644 --- a/website/core/AlgoliaDocSearch.js +++ b/website/core/AlgoliaDocSearch.js @@ -13,7 +13,7 @@ var AlgoliaDocSearch = React.createClass({ render: function() { return (
- +
); } From 3a53228127b3e4c2e7424fd7735e28cf852bf658 Mon Sep 17 00:00:00 2001 From: Will Sun Date: Tue, 12 Jul 2016 13:10:11 -0700 Subject: [PATCH 095/241] Prevent race condition on immediate transition Summary: NavigationTransitioner prepares for transition within `componentWillReceiveProps`, using previously-saved state to determine how to properly handle new props. If a transition is to take place, the code saves new info in state, executes the transition, and cleans up scenes within `_onTransitionEnd`. If the transition is a jump-to transition, or otherwise takes very little time, then it is possible for the setState call within `_onTransitionEnd` to use state which hasn't yet been set by the code within `componentWillReceiveProps`, resulting in a failed transition. This fix ensures that the initial setState call is completed before executing the transition. Closes https://github.com/facebook/react-native/pull/8709 Differential Revision: D3550872 fbshipit-source-id: 1364612048025f5f970b44cbfd0c31acc4a60f56 --- .../NavigationTransitioner.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Libraries/NavigationExperimental/NavigationTransitioner.js b/Libraries/NavigationExperimental/NavigationTransitioner.js index d3aabc5a35a5d8..e0eb6cdc8348ec 100644 --- a/Libraries/NavigationExperimental/NavigationTransitioner.js +++ b/Libraries/NavigationExperimental/NavigationTransitioner.js @@ -125,9 +125,6 @@ class NavigationTransitioner extends React.Component { progress, } = nextState; - // update scenes. - this.setState(nextState); - // get the transition spec. const transitionUserSpec = nextProps.configureTransition ? nextProps.configureTransition( @@ -168,12 +165,14 @@ class NavigationTransitioner extends React.Component { ); } - // play the transition. - nextProps.onTransitionStart && nextProps.onTransitionStart( - this._transitionProps, - this._prevTransitionProps, - ); - Animated.parallel(animations).start(this._onTransitionEnd); + // update scenes and play the transition + this.setState(nextState, () => { + nextProps.onTransitionStart && nextProps.onTransitionStart( + this._transitionProps, + this._prevTransitionProps, + ); + Animated.parallel(animations).start(this._onTransitionEnd); + }); } render(): ReactElement { From a74780e5a0146f67471b219c06f7bf395870f0e3 Mon Sep 17 00:00:00 2001 From: Joel Marcey Date: Tue, 12 Jul 2016 13:42:32 -0700 Subject: [PATCH 096/241] Fix favicon location error Summary: When generating the html after running `npm start`, we convert the `.md` files. As part of this, we use the location of the favicon. The location has been wrong for a while. This fixes it to point to the current, correct location. screenshot 2016-07-12 10 44 16 Closes https://github.com/facebook/react-native/pull/8718 Differential Revision: D3551002 fbshipit-source-id: 13c8292a76d3fddcd0a14d2b6cdb8a7cee70b606 --- website/server/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/server/server.js b/website/server/server.js index 79e7fcede047ff..96ca60e9ac09ab 100644 --- a/website/server/server.js +++ b/website/server/server.js @@ -52,7 +52,7 @@ var app = connect() }) .use(reactMiddleware.provide(buildOptions)) .use(connect['static'](FILE_SERVE_ROOT)) - .use(connect.favicon(path.join(FILE_SERVE_ROOT, 'elements', 'favicon', 'favicon.ico'))) + .use(connect.favicon(path.join(FILE_SERVE_ROOT, 'react-native', 'img', 'favicon.png'))) .use(connect.logger()) .use(connect.compress()) .use(connect.errorHandler()); From f317b9f28f61ca6076ecb0ba7f8e14ccb4b9face Mon Sep 17 00:00:00 2001 From: Jesse Luo Date: Tue, 12 Jul 2016 13:54:16 -0700 Subject: [PATCH 097/241] Fix misusage of NSUInteger * in RCTImageStoreManager Summary: I would like to believe it's some black magic code but no it's just a typo. Closes https://github.com/facebook/react-native/pull/8712 Differential Revision: D3550809 fbshipit-source-id: 00a7ba1cbcd36e29af44cdefd5fc1148d11d26e3 --- Libraries/Image/RCTImageStoreManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index 55c1b978525401..c33ee08e428d65 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -23,7 +23,7 @@ @implementation RCTImageStoreManager { NSMutableDictionary *_store; - NSUInteger *_id; + NSUInteger _id; } @synthesize methodQueue = _methodQueue; From 5189c94a6e876ef5274b22c083034a561ef3d5e8 Mon Sep 17 00:00:00 2001 From: Hedger Wang Date: Tue, 12 Jul 2016 17:39:10 -0700 Subject: [PATCH 098/241] Fix Navigator transition. Summary: Fix the bug reported as https://github.com/facebook/react-native/pull/8071 Reviewed By: fkgozali Differential Revision: D3553062 fbshipit-source-id: 286feddc268f51c0d9005a8831640c5e6af4880e --- Libraries/CustomComponents/Navigator/Navigator.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 63b53b03f20fac..d5fbd0674c1b31 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -501,13 +501,13 @@ var Navigator = React.createClass({ }, _transitionTo: function(destIndex, velocity, jumpSpringTo, cb) { - if ( - destIndex === this.state.presentedIndex && - this.state.transitionQueue.length === 0 - ) { + if (this.state.presentedIndex === destIndex) { + cb && cb(); return; } + if (this.state.transitionFromIndex !== null) { + // Navigation is still transitioning, put the `destIndex` into queue. this.state.transitionQueue.push({ destIndex, velocity, From b06b7ae60932d8be08bc792b3c70c32907b871b0 Mon Sep 17 00:00:00 2001 From: Christoph Pojer Date: Tue, 12 Jul 2016 21:40:14 -0700 Subject: [PATCH 099/241] Fix Relay tests, fix React.js whitespace Reviewed By: steveluscher Differential Revision: D3554045 fbshipit-source-id: c55bcecec7ce72def932d2dc37689283f2831bf4 --- Libraries/react-native/React.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Libraries/react-native/React.js b/Libraries/react-native/React.js index 05c1f2eafdc015..4dcb46b769ea1e 100644 --- a/Libraries/react-native/React.js +++ b/Libraries/react-native/React.js @@ -8,5 +8,7 @@ * * @providesModule React */ - 'use strict'; - module.exports = require('react/lib/React'); + +'use strict'; + +module.exports = require('react/lib/React'); From 33a1f28654e24a47d80f6ffc75072d0158085a09 Mon Sep 17 00:00:00 2001 From: sathya Date: Tue, 12 Jul 2016 22:06:23 -0700 Subject: [PATCH 100/241] =?UTF-8?q?Android=20WebView=20=E2=80=9Ctel:?= =?UTF-8?q?=E2=80=9D=20links=20show=20web=20page=20not=20found=20-=20issue?= =?UTF-8?q?=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Submitting PR #6810 - Android WebView “tel:” links show web page not found - issue fixed. 'tel:' link web page not loading: ![screenshot_2016-07-01-19-48-05](https://cloud.githubusercontent.com/assets/11989113/16525364/b3e9f10c-3fc9-11e6-8119-93cdf24d54df.png) After Fixing the issue: ![screenshot_2016-07-01-19-52-00](https://cloud.githubusercontent.com/assets/11989113/16525371/c0d74d92-3fc9-11e6-899b-570a940692f6.png) Closes https://github.com/facebook/react-native/pull/8526 Differential Revision: D3554500 fbshipit-source-id: e8cc1ac4c36ddf0c6b261a29b2e038caddc03e75 --- .../react/views/webview/ReactWebViewManager.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 11350d16e24639..3ed60497b8886a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -41,6 +41,8 @@ import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; +import android.content.Intent; +import android.net.Uri; /** * Manages instances of {@link WebView} @@ -111,6 +113,18 @@ public void onPageStarted(WebView webView, String url, Bitmap favicon) { createWebViewEvent(webView, url))); } + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (url.startsWith("http://") || url.startsWith("https://")) { + return false; + } else { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + view.getContext().startActivity(intent); + return true; + } + } + @Override public void onReceivedError( WebView webView, From 58202b7ba4cbb701fb5a1e6bfeaf6de7035d8707 Mon Sep 17 00:00:00 2001 From: Chace Liang Date: Tue, 12 Jul 2016 22:56:00 -0700 Subject: [PATCH 101/241] expose remote/local flag from native side instead of JS side Reviewed By: hedgerwang Differential Revision: D3536661 fbshipit-source-id: ae613daabe2ae581ebeabd8649b529d5fe6c5493 --- .../PushNotificationIOS.js | 25 ++------------- .../RCTPushNotificationManager.m | 31 +++++-------------- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/Libraries/PushNotificationIOS/PushNotificationIOS.js b/Libraries/PushNotificationIOS/PushNotificationIOS.js index 5089e7ee601b4b..fbdacccf2e891b 100644 --- a/Libraries/PushNotificationIOS/PushNotificationIOS.js +++ b/Libraries/PushNotificationIOS/PushNotificationIOS.js @@ -19,10 +19,6 @@ const PushNotificationEmitter = new NativeEventEmitter(RCTPushNotificationManage const _notifHandlers = new Map(); -//TODO: remove this once all call sites for popInitialNotification() have been removed -let _initialNotification = RCTPushNotificationManager && - RCTPushNotificationManager.initialNotification; - const DEVICE_NOTIF_EVENT = 'remoteNotificationReceived'; const NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered'; const DEVICE_LOCAL_NOTIF_EVENT = 'localNotificationReceived'; @@ -92,7 +88,7 @@ class PushNotificationIOS { * - `soundName` : The sound played when the notification is fired (optional). * - `category` : The category of this notification, required for actionable notifications (optional). * - `userInfo` : An optional object containing additional notification data. - * - `applicationIconBadgeNumber` (optional) : The number to display as the app’s icon badge. The default value of this property is 0, which means that no badge is displayed. + * - `applicationIconBadgeNumber` (optional) : The number to display as the app's icon badge. The default value of this property is 0, which means that no badge is displayed. */ static presentLocalNotification(details: Object) { RCTPushNotificationManager.presentLocalNotification(details); @@ -109,7 +105,7 @@ class PushNotificationIOS { * - `soundName` : The sound played when the notification is fired (optional). * - `category` : The category of this notification, required for actionable notifications (optional). * - `userInfo` : An optional object containing additional notification data. - * - `applicationIconBadgeNumber` (optional) : The number to display as the app’s icon badge. Setting the number to 0 removes the icon badge. + * - `applicationIconBadgeNumber` (optional) : The number to display as the app's icon badge. Setting the number to 0 removes the icon badge. */ static scheduleLocalNotification(details: Object) { RCTPushNotificationManager.scheduleLocalNotification(details); @@ -177,7 +173,6 @@ class PushNotificationIOS { listener = PushNotificationEmitter.addListener( DEVICE_NOTIF_EVENT, (notifData) => { - notifData.remote = true; handler(new PushNotificationIOS(notifData)); } ); @@ -185,7 +180,6 @@ class PushNotificationIOS { listener = PushNotificationEmitter.addListener( DEVICE_LOCAL_NOTIF_EVENT, (notifData) => { - notifData.remote = false; handler(new PushNotificationIOS(notifData)); } ); @@ -289,21 +283,6 @@ class PushNotificationIOS { RCTPushNotificationManager.checkPermissions(callback); } - /** - * DEPRECATED: An initial notification will be available if the app was - * cold-launched from a notification. - * - * The first caller of `popInitialNotification` will get the initial - * notification object, or `null`. Subsequent invocations will return null. - */ - static popInitialNotification() { - console.warn('PushNotificationIOS.popInitialNotification() is deprecated. Use getInitialNotification() instead.'); - var initialNotification = _initialNotification && - new PushNotificationIOS(_initialNotification); - _initialNotification = null; - return initialNotification; - } - /** * If the app launch was triggered by a push notification, * it will give the notification object, otherwise it will give `null` diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index a84f4d959753ff..806eccc5eb53d2 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -71,6 +71,7 @@ @implementation RCTPushNotificationManager formattedLocalNotification[@"category"] = RCTNullIfNil(notification.category); formattedLocalNotification[@"soundName"] = RCTNullIfNil(notification.soundName); formattedLocalNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo)); + formattedLocalNotification[@"remote"] = @NO; return formattedLocalNotification; } @@ -113,25 +114,6 @@ - (void)stopObserving @"remoteNotificationsRegistered"]; } -// TODO: Once all JS call sites for popInitialNotification have -// been removed we can get rid of this -- (NSDictionary *)constantsToExport -{ - NSDictionary *initialNotification = - self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; - - UILocalNotification *initialLocalNotification = - self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; - - if (initialNotification) { - return @{@"initialNotification": [initialNotification copy]}; - } else if (initialLocalNotification) { - return @{@"initialNotification": RCTFormatLocalNotification(initialLocalNotification)}; - } else { - return @{@"initialNotification": (id)kCFNull}; - } -} - + (void)didRegisterUserNotificationSettings:(__unused UIUserNotificationSettings *)notificationSettings { if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) { @@ -176,7 +158,9 @@ - (void)handleLocalNotificationReceived:(NSNotification *)notification - (void)handleRemoteNotificationReceived:(NSNotification *)notification { - [self sendEventWithName:@"remoteNotificationReceived" body:notification.userInfo]; + NSMutableDictionary *userInfo = [notification.userInfo mutableCopy]; + userInfo[@"remote"] = @YES; + [self sendEventWithName:@"remoteNotificationReceived" body:userInfo]; } - (void)handleRemoteNotificationsRegistered:(NSNotification *)notification @@ -329,14 +313,15 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject) { - NSDictionary *initialNotification = - self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; + NSMutableDictionary *initialNotification = + [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] mutableCopy]; UILocalNotification *initialLocalNotification = self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; if (initialNotification) { - resolve([initialNotification copy]); + initialNotification[@"remote"] = @YES; + resolve(initialNotification); } else if (initialLocalNotification) { resolve(RCTFormatLocalNotification(initialLocalNotification)); } else { From afbd2d7f32d84afa138ba25fb771d7d2c95d67e6 Mon Sep 17 00:00:00 2001 From: Jean Regisser Date: Tue, 12 Jul 2016 23:19:00 -0700 Subject: [PATCH 102/241] Added FanVision Bolt to showcase Summary: Hi there, I showed this app to a couple of FB people at the React Europe conf in June (ndfred tadeuzagallo ericvicenti astreet and others), they suggested I should submit it to be part of the showcased apps. It's used in production since the beginning of the year and fully usable at all NASCAR events. The external accessory receives the digital TV signal broadcasted around the track by an antenna on a FanVision truck. People rent the accessory there at our kiosks. It also recharges your smartphone battery. I linked to a demo video so people can actually see what it does without having the required accessory. The app is of course powered by React Native. I've built custom modules to integrate the native code that does all the heavy work (talking to the accessory, demuxing the raw data, decoding the audio and video, processing the additional data for all stats, etc). Let me know what you think. Closes https://github.com/facebook/react-native/pull/8708 Differential Revision: D3553712 Pulled By: lacker fbshipit-source-id: 870a3fea9f3a662e82480ffbfcf0835478f0d4be --- website/src/react-native/showcase.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/src/react-native/showcase.js b/website/src/react-native/showcase.js index f032e23cc38894..671d0c60798be1 100644 --- a/website/src/react-native/showcase.js +++ b/website/src/react-native/showcase.js @@ -55,6 +55,13 @@ var featured = [ infoLink: 'http://makeitopen.com/tutorials/building-the-f8-app/planning/', infoTitle: 'Building the F8 App', }, + { + name: 'FanVision Bolt', + icon: 'http://a4.mzstatic.com/us/r30/Purple18/v4/94/b4/6e/94b46ee5-80e3-ff6e-513d-16da926b03a3/icon175x175.jpeg', + linkAppStore: 'https://itunes.apple.com/us/app/fanvision-bolt/id1081891275', + infoLink: 'https://www.youtube.com/watch?v=oWOcAXyDf0w', + infoTitle: 'FanVision Bolt accessory + app provide live audio/video and stats at NASCAR events', + }, { name: 'Gyroscope', icon: 'https://media.gyrosco.pe/images/magneto/180x180.png', From 68b0ce657e81936d3b910ee9ea2f45d84deced9c Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Tue, 12 Jul 2016 23:24:27 -0700 Subject: [PATCH 103/241] iOS: Provide correct initial value for AppState.currentState Summary: Attempt to fix #7919. Currently, if the app is launched into the background and you read `AppState.currentState` too soon, you will see the value `'active'` instead of `'background'`. This is because the default value of `AppState.currentState` is hardcoded to be `'active'` and it is initialized with the actual value asynchronously. This attempts to fix the bug by having the `RCTAppState` module provide the initial state as a module constant. As noted in #7919, it looks like this fix was already tried and reverted with 0fb3d8de83c4d8b79a8a110e9cd1300b39ab8918. zjj010104, hedgerwang, nicklockwood -- can you explain why? I would very much like to get this bug fixed. Nobody has followed up on the issue I filed so I decided to just go ahead and make a PR with my best guess at a fix. **Test plan (required)** Built a small app as described in the repro steps for #7919 and verified that, when the app is launched into the background, `init currentState: background` is printed. Also verified that `i Closes https://github.com/facebook/react-native/pull/8058 Differential Revision: D3554619 fbshipit-source-id: 5d950b85e335765552bbd3cf6ed91534062e35a1 --- Libraries/AppState/AppState.js | 14 ++++++-------- React/Modules/RCTAppState.m | 5 +++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Libraries/AppState/AppState.js b/Libraries/AppState/AppState.js index 1573a90129c9dd..e8babe4dd61c8d 100644 --- a/Libraries/AppState/AppState.js +++ b/Libraries/AppState/AppState.js @@ -83,12 +83,10 @@ class AppState extends NativeEventEmitter { memoryWarning: new Map(), }; - // TODO: getCurrentAppState callback seems to be called at a really late stage - // after app launch. Trying to get currentState when mounting App component - // will likely to have the initial value here. - // Initialize to 'active' instead of null. - this.currentState = 'active'; - + // TODO: Remove the 'active' fallback after `initialAppState` is exported by + // the Android implementation. + this.currentState = RCTAppState.initialAppState || 'active'; + // TODO: this is a terrible solution - in order to ensure `currentState` prop // is up to date, we have to register an observer that updates it whenever // the state changes, even if nobody cares. We should just deprecate the @@ -99,7 +97,7 @@ class AppState extends NativeEventEmitter { this.currentState = appStateData.app_state; } ); - + // TODO: see above - this request just populates the value of `currentState` // when the module is first initialized. Would be better to get rid of the prop // and expose `getCurrentAppState` method directly. @@ -111,7 +109,7 @@ class AppState extends NativeEventEmitter { ); } - /** + /** * Add a handler to AppState changes by listening to the `change` event type * and providing the handler * diff --git a/React/Modules/RCTAppState.m b/React/Modules/RCTAppState.m index db9716a7eff2b5..1ae390271641a7 100644 --- a/React/Modules/RCTAppState.m +++ b/React/Modules/RCTAppState.m @@ -46,6 +46,11 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } +- (NSDictionary *)constantsToExport +{ + return @{@"initialAppState": RCTCurrentAppBackgroundState()}; +} + #pragma mark - Lifecycle - (NSArray *)supportedEvents From c06a1360a949fd4101a86907f66c0fb72281ce41 Mon Sep 17 00:00:00 2001 From: Antti Moilanen Date: Wed, 13 Jul 2016 00:52:58 -0700 Subject: [PATCH 104/241] Fix broken link to PanResponderExample.js Summary: In recent change in 2f73ca8 all javascript files under UIExplorer were moved to js subfolders but PanResponderExample link wasn't updated accordingly. Closes https://github.com/facebook/react-native/pull/8725 Differential Revision: D3554851 fbshipit-source-id: 798b9a76ecb667512a23a405d0fee0691a9debce --- Libraries/Interaction/PanResponder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Interaction/PanResponder.js b/Libraries/Interaction/PanResponder.js index a5ad0af42b0bd2..713e3840aef6cc 100644 --- a/Libraries/Interaction/PanResponder.js +++ b/Libraries/Interaction/PanResponder.js @@ -116,7 +116,7 @@ const currentCentroidY = TouchHistoryMath.currentCentroidY; * ### Working Example * * To see it in action, try the - * [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/PanResponderExample.js) + * [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/js/PanResponderExample.js) */ const PanResponder = { From 262397d5b164c656de1a01c2e1e26285ae1e7c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E6=A0=91?= Date: Wed, 13 Jul 2016 02:07:56 -0700 Subject: [PATCH 105/241] Fix UIExplorer example AppDelegate.m for pre-bundle comment Summary: Thanks for submitting a pull request! Please provide enough information so that others can review your pull request: (You can skip this if you're fixing a typo or adding an app to the Showcase.) Explain the **motivation** for making this change. What existing problem does the pull request solve? UIExplorer example doc fix. **Test plan (required)** Unnecessary. **Code formatting** Just fix comment, not related to code Fix pre-bundle doc Closes https://github.com/facebook/react-native/pull/8733 Differential Revision: D3554930 fbshipit-source-id: 76ed3da17df840f90987f7468cea7cd65741313f --- Examples/UIExplorer/UIExplorer/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m index 7e1273141446e4..4c6b6b44e0c527 100644 --- a/Examples/UIExplorer/UIExplorer/AppDelegate.m +++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m @@ -74,7 +74,7 @@ - (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` * to your Xcode project folder and run * - * $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?platform=ios' -o main.jsbundle + * $ curl 'http://localhost:8081/Examples/UIExplorer/js/UIExplorerApp.ios.bundle?platform=ios' -o main.jsbundle * * then add the `main.jsbundle` file to your project and uncomment this line: */ From 91ff6868a554c4930fd5fda6ba8044dbd56c8374 Mon Sep 17 00:00:00 2001 From: Mark Oswald Date: Wed, 13 Jul 2016 03:54:38 -0700 Subject: [PATCH 106/241] Use HTTP range requests (responses) to serve mp4 from assets Summary: This PR solves a problem when video assets are used from third-party React Native components (e.g. [react-native-video](https://github.com/brentvatne/react-native-video). The video will not work while the assets are served from the react native packager because the used video component (iOS) relies on HTTP range requests. I added a small fix that allows ranged requests (e.g. mp4) to be served in ranges. To test this: 1. make new react native project 1. add [react-native-video](https://github.com/brentvatne/react-native-video) to xcode project 1. add video component to your project ``` import Video from 'react-native-video'; var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); /* ... /* render() { let source = resolveAssetSource(require('./someVideoFile.mp4')) || {}; return