From d1a071d23ef191afb7d5a13da15f1387e4e48667 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Tue, 8 Mar 2022 16:58:01 -0800 Subject: [PATCH 01/10] integrates 2/27/22 --- .../react-native-win32-tester/overrides.json | 2 +- .../react-native-win32-tester/package.json | 4 +- .../react-native-win32/.flowconfig | 2 + .../react-native-win32/overrides.json | 17 +- .../react-native-win32/package.json | 14 +- .../SegmentedControlIOS.js | 45 - .../Libraries/Types/CoreEventTypes.win32.js | 2 +- .../react-native-win32/src/index.win32.js | 31 +- .../automation-channel/package.json | 2 +- .../tester/overrides.json | 2 +- .../@react-native-windows/tester/package.json | 4 +- .../virtualized-list/.flowconfig | 17 + .../virtualized-list/overrides.json | 4 +- .../virtualized-list/package.json | 4 +- .../@react-native/repo-config/overrides.json | 4 +- .../@react-native/repo-config/package.json | 4 +- packages/@react-native/tester/overrides.json | 2 +- packages/e2e-test-app/package.json | 2 +- packages/integration-test-app/package.json | 2 +- packages/playground/package.json | 2 +- packages/sample-apps/package.json | 2 +- vnext/.flowconfig | 2 + .../jsi/JSCRuntime.cpp | 1480 +++++++++++++++++ .../jsi/jsi/decorator.h | 753 +++++++++ .../TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h | 1321 +++++++++++++++ .../components/rnwcore/ComponentDescriptors.h | 1 - .../components/rnwcore/EventEmitters.cpp | 8 - .../react/components/rnwcore/EventEmitters.h | 11 - .../react/components/rnwcore/Props.cpp | 13 - .../codegen/react/components/rnwcore/Props.h | 16 - .../react/components/rnwcore/ShadowNodes.cpp | 1 - .../react/components/rnwcore/ShadowNodes.h | 10 - vnext/overrides.json | 41 +- vnext/package.json | 14 +- .../SegmentedControlIOS.windows.js | 45 - .../Components/TextInput/TextInput.windows.js | 2 +- vnext/src/Libraries/Text/Text.windows.js | 16 +- .../Libraries/Types/CoreEventTypes.windows.js | 2 +- .../GenerateViewConfigJs.js | 2 +- vnext/src/index.windows.js | 31 +- yarn.lock | 148 +- 41 files changed, 3824 insertions(+), 261 deletions(-) delete mode 100644 packages/@office-iss/react-native-win32/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h delete mode 100644 vnext/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.windows.js diff --git a/packages/@office-iss/react-native-win32-tester/overrides.json b/packages/@office-iss/react-native-win32-tester/overrides.json index 10b0f8885ad..1f1c8b13224 100644 --- a/packages/@office-iss/react-native-win32-tester/overrides.json +++ b/packages/@office-iss/react-native-win32-tester/overrides.json @@ -5,7 +5,7 @@ "excludePatterns": [ "src/js/examples-win32/**" ], - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "patch", diff --git a/packages/@office-iss/react-native-win32-tester/package.json b/packages/@office-iss/react-native-win32-tester/package.json index a509c571bdb..ce5d9f85cdd 100644 --- a/packages/@office-iss/react-native-win32-tester/package.json +++ b/packages/@office-iss/react-native-win32-tester/package.json @@ -16,7 +16,7 @@ "peerDependencies": { "@office-iss/react-native-win32": "^0.0.0-canary.136", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423" + "react-native": "0.0.0-20220227-2009-189c2c895" }, "devDependencies": { "@office-iss/react-native-win32": "^0.0.0-canary.136", @@ -26,7 +26,7 @@ "@types/node": "^14.14.22", "eslint": "^7.32.0", "just-scripts": "^1.3.3", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-platform-override": "^1.6.6", "typescript": "^4.4.4" }, diff --git a/packages/@office-iss/react-native-win32/.flowconfig b/packages/@office-iss/react-native-win32/.flowconfig index 552b6745d80..950b5bd698f 100644 --- a/packages/@office-iss/react-native-win32/.flowconfig +++ b/packages/@office-iss/react-native-win32/.flowconfig @@ -58,6 +58,8 @@ ; Ignore files with flow typing issues #9223 .*/react-native-win32/Libraries/Core/ReactNativeVersionCheck.js +.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$ + [untyped] .*/node_modules/@react-native-community/cli/.*/.* ; Should work out how to do this properly diff --git a/packages/@office-iss/react-native-win32/overrides.json b/packages/@office-iss/react-native-win32/overrides.json index 0811d949474..a70d4c1aa35 100644 --- a/packages/@office-iss/react-native-win32/overrides.json +++ b/packages/@office-iss/react-native-win32/overrides.json @@ -7,19 +7,19 @@ "**/__snapshots__/**", "src/rntypes/**" ], - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "derived", "file": ".flowconfig", "baseFile": ".flowconfig", - "baseHash": "e0fd826d1c731209ca1f4322e950df83877ea95a" + "baseHash": "62cbbd198b5f952592fcffae5e5f88a387a2f190" }, { "type": "derived", "file": "src/index.win32.js", "baseFile": "index.js", - "baseHash": "a33f356abc2db3767c8fee7e4d6bda60384787a1" + "baseHash": "2a0bd511c691be2ac3da45a3c77250aaf55414e1" }, { "type": "patch", @@ -100,13 +100,6 @@ "baseFile": "Libraries/Components/SafeAreaView/SafeAreaView.js", "baseHash": "d13d67c91a9096989776ba7d7b0f8537c46b87a8" }, - { - "type": "copy", - "file": "src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js", - "baseFile": "Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.android.js", - "baseHash": "a7b7561ee80510fb3a45bad5ad18bc9076152144", - "issue": 4378 - }, { "type": "platform", "file": "src/Libraries/Components/Text/Tests/TextWin32Test.tsx" @@ -131,7 +124,7 @@ "type": "derived", "file": "src/Libraries/Components/TextInput/TextInput.win32.tsx", "baseFile": "Libraries/Components/TextInput/TextInput.js", - "baseHash": "c447994d5ac59824f298cf4aec899c174b78e3e2" + "baseHash": "567ebad070a2fb9020ef177ee30defba9f0c54ec" }, { "type": "patch", @@ -443,7 +436,7 @@ "type": "patch", "file": "src/Libraries/Types/CoreEventTypes.win32.js", "baseFile": "Libraries/Types/CoreEventTypes.js", - "baseHash": "b7ed94c49c38a6b3579349e6940bb663328c1f4e", + "baseHash": "12d577ba5b522593fb49d4621d5ad4bba0d37119", "issue": 6240 }, { diff --git a/packages/@office-iss/react-native-win32/package.json b/packages/@office-iss/react-native-win32/package.json index 0d0c80a57b0..c6126e16c46 100644 --- a/packages/@office-iss/react-native-win32/package.json +++ b/packages/@office-iss/react-native-win32/package.json @@ -22,7 +22,7 @@ "dependencies": { "@babel/runtime": "^7.0.0", "@jest/create-cache-key-function": "^27.0.1", - "@react-native-community/cli": "^7.0.1", + "@react-native-community/cli": "^7.0.3", "@react-native-community/cli-platform-android": "^7.0.1", "@react-native-community/cli-platform-ios": "^7.0.1", "@react-native-windows/virtualized-list": "0.0.0-canary.31", @@ -37,16 +37,16 @@ "hermes-engine": "~0.11.0", "invariant": "^2.2.4", "jsc-android": "^250230.2.1", - "metro-react-native-babel-transformer": "0.67.0", - "metro-runtime": "0.67.0", - "metro-source-map": "0.67.0", + "metro-react-native-babel-transformer": "0.68.0", + "metro-runtime": "0.68.0", + "metro-source-map": "0.68.0", "nullthrows": "^1.1.1", "pretty-format": "^26.5.2", "promise": "^8.0.3", "react-clone-referenced-element": "^1.0.1", "react-devtools-core": "^4.23.0", "react-native-codegen": "^0.0.13", - "react-native-gradle-plugin": "^0.0.4", + "react-native-gradle-plugin": "^0.0.5", "react-refresh": "^0.4.0", "react-shallow-renderer": "16.14.1", "regenerator-runtime": "^0.13.2", @@ -72,14 +72,14 @@ "just-scripts": "^1.3.3", "prettier": "^2.4.1", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-platform-override": "^1.6.6", "react-shallow-renderer": "16.14.1", "typescript": "^4.4.4" }, "peerDependencies": { "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423" + "react-native": "0.0.0-20220227-2009-189c2c895" }, "beachball": { "defaultNpmTag": "canary", diff --git a/packages/@office-iss/react-native-win32/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js b/packages/@office-iss/react-native-win32/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js deleted file mode 100644 index afc2f621e78..00000000000 --- a/packages/@office-iss/react-native-win32/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; -import * as React from 'react'; -import StyleSheet from '../../StyleSheet/StyleSheet'; -import Text from '../../Text/Text'; -import View from '../View/View'; - -class DummySegmentedControlIOS extends React.Component { - render() { - return ( - - - SegmentedControlIOS is not supported on this platform! - - - ); - } -} - -const styles = StyleSheet.create({ - dummy: { - width: 120, - height: 50, - backgroundColor: '#ffbcbc', - borderWidth: 1, - borderColor: 'red', - alignItems: 'center', - justifyContent: 'center', - }, - text: { - color: '#333333', - margin: 5, - fontSize: 10, - }, -}); - -module.exports = DummySegmentedControlIOS; diff --git a/packages/@office-iss/react-native-win32/src/Libraries/Types/CoreEventTypes.win32.js b/packages/@office-iss/react-native-win32/src/Libraries/Types/CoreEventTypes.win32.js index 5148f07d48d..2e81584d819 100644 --- a/packages/@office-iss/react-native-win32/src/Libraries/Types/CoreEventTypes.win32.js +++ b/packages/@office-iss/react-native-win32/src/Libraries/Types/CoreEventTypes.win32.js @@ -11,7 +11,7 @@ import * as React from 'react'; import type {HostComponent} from '../Renderer/shims/ReactNativeTypes'; -export type SyntheticEvent = $ReadOnly<{| +export type SyntheticEvent<+T> = $ReadOnly<{| bubbles: ?boolean, cancelable: ?boolean, currentTarget: number | React.ElementRef>, diff --git a/packages/@office-iss/react-native-win32/src/index.win32.js b/packages/@office-iss/react-native-win32/src/index.win32.js index b1516f8668b..9f629d94ea3 100644 --- a/packages/@office-iss/react-native-win32/src/index.win32.js +++ b/packages/@office-iss/react-native-win32/src/index.win32.js @@ -30,7 +30,6 @@ import typeof RefreshControl from './Libraries/Components/RefreshControl/Refresh import typeof SafeAreaView from './Libraries/Components/SafeAreaView/SafeAreaView'; import typeof ScrollView from './Libraries/Components/ScrollView/ScrollView'; import typeof SectionList from './Libraries/Lists/SectionList'; -import typeof SegmentedControlIOS from './Libraries/Components/SegmentedControlIOS/SegmentedControlIOS'; import typeof Slider from './Libraries/Components/Slider/Slider'; import typeof StatusBar from './Libraries/Components/StatusBar/StatusBar'; import typeof Switch from './Libraries/Components/Switch/Switch'; @@ -197,16 +196,6 @@ module.exports = { get SectionList(): SectionList { return require('./Libraries/Lists/SectionList').default; }, - // $FlowFixMe[value-as-type] - get SegmentedControlIOS(): SegmentedControlIOS { - warnOnce( - 'segmented-control-ios-moved', - 'SegmentedControlIOS has been extracted from react-native core and will be removed in a future release. ' + - "It can now be installed and imported from '@react-native-segmented-control/segmented-control' instead of 'react-native'. " + - 'See https://github.com/react-native-segmented-control/segmented-control', - ); - return require('./Libraries/Components/SegmentedControlIOS/SegmentedControlIOS'); - }, get Slider(): Slider { warnOnce( 'slider-moved', @@ -685,8 +674,8 @@ if (__DEV__) { invariant( false, 'ViewPagerAndroid has been removed from React Native. ' + - "It can now be installed and imported from '@react-native-community/viewpager' instead of 'react-native'. " + - 'See https://github.com/callstack/react-native-viewpager', + "It can now be installed and imported from 'react-native-pager-view' instead of 'react-native'. " + + 'See https://github.com/callstack/react-native-pager-view', ); }, }); @@ -707,6 +696,22 @@ if (__DEV__) { }, }); + /* $FlowFixMe[prop-missing] This is intentional: Flow will error when + * attempting to access SegmentedControlIOS. */ + /* $FlowFixMe[invalid-export] This is intentional: Flow will error when + * attempting to access SegmentedControlIOS. */ + Object.defineProperty(module.exports, 'SegmentedControlIOS', { + configurable: true, + get() { + invariant( + false, + 'SegmentedControlIOS has been removed from React Native. ' + + "It can now be installed and imported from '@react-native-community/segmented-checkbox' instead of 'react-native'." + + 'See https://github.com/react-native-segmented-control/segmented-control', + ); + }, + }); + /* $FlowFixMe[prop-missing] This is intentional: Flow will error when * attempting to access StatusBarIOS. */ /* $FlowFixMe[invalid-export] This is intentional: Flow will error when diff --git a/packages/@react-native-windows/automation-channel/package.json b/packages/@react-native-windows/automation-channel/package.json index 44960f6d6c3..5712799ced7 100644 --- a/packages/@react-native-windows/automation-channel/package.json +++ b/packages/@react-native-windows/automation-channel/package.json @@ -29,7 +29,7 @@ "just-scripts": "^1.3.2", "prettier": "^2.4.1", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-windows": "^0.0.0-canary.466", "typescript": "^4.4.4" }, diff --git a/packages/@react-native-windows/tester/overrides.json b/packages/@react-native-windows/tester/overrides.json index 3eee8dd4d95..c7e28099f7f 100644 --- a/packages/@react-native-windows/tester/overrides.json +++ b/packages/@react-native-windows/tester/overrides.json @@ -5,7 +5,7 @@ "excludePatterns": [ "src/js/examples-win/**" ], - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "patch", diff --git a/packages/@react-native-windows/tester/package.json b/packages/@react-native-windows/tester/package.json index 2bc3ff5017b..a8fd3d12cca 100644 --- a/packages/@react-native-windows/tester/package.json +++ b/packages/@react-native-windows/tester/package.json @@ -16,7 +16,7 @@ "peerDependencies": { "@react-native-picker/picker": "2.2.0", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-windows": "^0.0.0-canary.466", "react-native-xaml": "^0.0.50" }, @@ -27,7 +27,7 @@ "@types/node": "^14.14.22", "eslint": "^7.32.0", "just-scripts": "^1.3.3", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-platform-override": "^1.6.6", "react-native-windows": "^0.0.0-canary.466", "typescript": "^4.4.4" diff --git a/packages/@react-native-windows/virtualized-list/.flowconfig b/packages/@react-native-windows/virtualized-list/.flowconfig index b5d4d52c6e4..dc7a1bc3972 100644 --- a/packages/@react-native-windows/virtualized-list/.flowconfig +++ b/packages/@react-native-windows/virtualized-list/.flowconfig @@ -5,6 +5,23 @@ .*/node_modules/react-native-windows/Microsoft.ReactNative.Managed.CodeGen.UnitTests/.* .*/node_modules/react-native-windows/Microsoft.ReactNative.Managed.UnitTests/.* +; Ignore templates for 'react-native init' +/template/.* + +; Ignore the Dangerfile +/bots/dangerfile.js + +; Ignore "BUCK" generated dirs +/\.buckd/ + +; Flow doesn't support platforms +.*/Libraries/Utilities/LoadingView.js + +.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$ + +[untyped] +.*/node_modules/@react-native-community/cli/.*/.* + [libs] ../../../node_modules/react-native/interface.js ../../../node_modules/react-native/flow/ diff --git a/packages/@react-native-windows/virtualized-list/overrides.json b/packages/@react-native-windows/virtualized-list/overrides.json index d83872a1779..8b963477a68 100644 --- a/packages/@react-native-windows/virtualized-list/overrides.json +++ b/packages/@react-native-windows/virtualized-list/overrides.json @@ -3,13 +3,13 @@ ".flowconfig", "src/**" ], - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "derived", "file": ".flowconfig", "baseFile": ".flowconfig", - "baseHash": "e0fd826d1c731209ca1f4322e950df83877ea95a" + "baseHash": "62cbbd198b5f952592fcffae5e5f88a387a2f190" }, { "type": "copy", diff --git a/packages/@react-native-windows/virtualized-list/package.json b/packages/@react-native-windows/virtualized-list/package.json index a7b73cd1035..47593fdc453 100644 --- a/packages/@react-native-windows/virtualized-list/package.json +++ b/packages/@react-native-windows/virtualized-list/package.json @@ -25,12 +25,12 @@ "just-scripts": "^1.3.3", "prettier": "^2.4.1", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-platform-override": "^1.6.6" }, "peerDependencies": { "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423" + "react-native": "0.0.0-20220227-2009-189c2c895" }, "beachball": { "defaultNpmTag": "canary", diff --git a/packages/@react-native/repo-config/overrides.json b/packages/@react-native/repo-config/overrides.json index 13873d87ae8..72d289e142c 100644 --- a/packages/@react-native/repo-config/overrides.json +++ b/packages/@react-native/repo-config/overrides.json @@ -1,11 +1,11 @@ { - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "copy", "file": "package.json", "baseFile": "repo-config/package.json", - "baseHash": "8b5f3ea2a9fc8b90c7c51de7b08c1336a9d1c7b4" + "baseHash": "e3b88e4a659e95b76939b54abda030da0ca7f3e3" } ] } \ No newline at end of file diff --git a/packages/@react-native/repo-config/package.json b/packages/@react-native/repo-config/package.json index 388f8bf6e6a..2972f7e612f 100644 --- a/packages/@react-native/repo-config/package.json +++ b/packages/@react-native/repo-config/package.json @@ -18,7 +18,7 @@ "babel-eslint": "^10.1.0", "clang-format": "^1.2.4", "connect": "^3.6.5", - "coveralls": "^3.0.2", + "coveralls": "^3.1.1", "eslint": "^7.32.0", "eslint-config-fb-strict": "^26.0.0", "eslint-config-fbjs": "^3.1.1", @@ -38,7 +38,7 @@ "jest": "^26.6.3", "jest-junit": "^10.0.0", "jscodeshift": "^0.13.1", - "metro-babel-register": "0.67.0", + "metro-babel-register": "0.68.0", "mkdirp": "^0.5.1", "prettier": "^2.4.1", "react": "17.0.2", diff --git a/packages/@react-native/tester/overrides.json b/packages/@react-native/tester/overrides.json index e993f187d2c..61bff9c6d95 100644 --- a/packages/@react-native/tester/overrides.json +++ b/packages/@react-native/tester/overrides.json @@ -1,5 +1,5 @@ { - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "copy", diff --git a/packages/e2e-test-app/package.json b/packages/e2e-test-app/package.json index ae4e2b362a6..0092dd7b4c5 100644 --- a/packages/e2e-test-app/package.json +++ b/packages/e2e-test-app/package.json @@ -16,7 +16,7 @@ "@react-native-windows/automation-channel": "^0.1.40", "@react-native-windows/tester": "0.0.1", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-windows": "^0.0.0-canary.466", "react-native-xaml": "^0.0.50" }, diff --git a/packages/integration-test-app/package.json b/packages/integration-test-app/package.json index c9289830776..545aa6bab55 100644 --- a/packages/integration-test-app/package.json +++ b/packages/integration-test-app/package.json @@ -14,7 +14,7 @@ "@react-native-windows/fs": "^1.0.2", "chai": "^4.2.0", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-windows": "^0.0.0-canary.466" }, "devDependencies": { diff --git a/packages/playground/package.json b/packages/playground/package.json index 7d1bc9a83e3..a12266e4ea2 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -13,7 +13,7 @@ "@react-native-picker/picker": "2.2.0", "@react-native-windows/tester": "0.0.1", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-windows": "^0.0.0-canary.466" }, "devDependencies": { diff --git a/packages/sample-apps/package.json b/packages/sample-apps/package.json index c4e5fabd48a..15bed0b694d 100644 --- a/packages/sample-apps/package.json +++ b/packages/sample-apps/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-windows": "^0.0.0-canary.466" }, "devDependencies": { diff --git a/vnext/.flowconfig b/vnext/.flowconfig index 74d99d8938e..cbca5116211 100644 --- a/vnext/.flowconfig +++ b/vnext/.flowconfig @@ -58,6 +58,8 @@ ; Ignore the src folder - flow files are combined with ones from react-native into the root Libraries folder .*/vnext/src/.* +.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$ + [untyped] .*/node_modules/@react-native-community/cli/.*/.* diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp new file mode 100644 index 00000000000..58804af1739 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp @@ -0,0 +1,1480 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "JSCRuntime.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook { +namespace jsc { + +namespace detail { +class ArgsConverter; +} // namespace detail + +class JSCRuntime; + +struct Lock { + void lock(const jsc::JSCRuntime &) const {} + void unlock(const jsc::JSCRuntime &) const {} +}; + +class JSCRuntime : public jsi::Runtime { + public: + // Creates new context in new context group + JSCRuntime(); + // Retains ctx + JSCRuntime(JSGlobalContextRef ctx); + ~JSCRuntime(); + + std::shared_ptr prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) override; + + jsi::Value evaluatePreparedJavaScript( + const std::shared_ptr &js) override; + + jsi::Value evaluateJavaScript( + const std::shared_ptr &buffer, + const std::string &sourceURL) override; + + bool drainMicrotasks(int maxMicrotasksHint = -1) override; + + jsi::Object global() override; + + std::string description() override; + + bool isInspectable() override; + + void setDescription(const std::string &desc); + + // Please don't use the following two functions, only exposed for + // integration efforts. + JSGlobalContextRef getContext() { + return ctx_; + } + + // JSValueRef->JSValue (needs make.*Value so it must be member function) + jsi::Value createValue(JSValueRef value) const; + + // Value->JSValueRef (similar to above) + JSValueRef valueRef(const jsi::Value &value); + + protected: + friend class detail::ArgsConverter; + class JSCSymbolValue final : public PointerValue { +#ifndef NDEBUG + JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym, + std::atomic &counter); +#else + JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym); +#endif + void invalidate() override; + + JSGlobalContextRef ctx_; + const std::atomic &ctxInvalid_; + // There is no C type in the JSC API to represent Symbol, so this stored + // a JSValueRef which contains the Symbol. + JSValueRef sym_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + class JSCStringValue final : public PointerValue { +#ifndef NDEBUG + JSCStringValue(JSStringRef str, std::atomic &counter); +#else + JSCStringValue(JSStringRef str); +#endif + void invalidate() override; + + JSStringRef str_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + class JSCObjectValue final : public PointerValue { + JSCObjectValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSObjectRef obj +#ifndef NDEBUG + , + std::atomic &counter +#endif + ); + + void invalidate() override; + + JSGlobalContextRef ctx_; + const std::atomic &ctxInvalid_; + JSObjectRef obj_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + PointerValue *cloneSymbol(const Runtime::PointerValue *pv) override; + PointerValue *cloneString(const Runtime::PointerValue *pv) override; + PointerValue *cloneObject(const Runtime::PointerValue *pv) override; + PointerValue *clonePropNameID(const Runtime::PointerValue *pv) override; + + jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) + override; + jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) + override; + jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override; + std::string utf8(const jsi::PropNameID &) override; + bool compare(const jsi::PropNameID &, const jsi::PropNameID &) override; + + std::string symbolToString(const jsi::Symbol &) override; + + jsi::String createStringFromAscii(const char *str, size_t length) override; + jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; + std::string utf8(const jsi::String &) override; + + jsi::Object createObject() override; + jsi::Object createObject(std::shared_ptr ho) override; + virtual std::shared_ptr getHostObject( + const jsi::Object &) override; + jsi::HostFunctionType &getHostFunction(const jsi::Function &) override; + + jsi::Value getProperty(const jsi::Object &, const jsi::String &name) override; + jsi::Value getProperty(const jsi::Object &, const jsi::PropNameID &name) + override; + bool hasProperty(const jsi::Object &, const jsi::String &name) override; + bool hasProperty(const jsi::Object &, const jsi::PropNameID &name) override; + void setPropertyValue( + jsi::Object &, + const jsi::String &name, + const jsi::Value &value) override; + void setPropertyValue( + jsi::Object &, + const jsi::PropNameID &name, + const jsi::Value &value) override; + bool isArray(const jsi::Object &) const override; + bool isArrayBuffer(const jsi::Object &) const override; + bool isFunction(const jsi::Object &) const override; + bool isHostObject(const jsi::Object &) const override; + bool isHostFunction(const jsi::Function &) const override; + jsi::Array getPropertyNames(const jsi::Object &) override; + + // TODO: revisit this implementation + jsi::WeakObject createWeakObject(const jsi::Object &) override; + jsi::Value lockWeakObject(jsi::WeakObject &) override; + + jsi::Array createArray(size_t length) override; + size_t size(const jsi::Array &) override; + size_t size(const jsi::ArrayBuffer &) override; + uint8_t *data(const jsi::ArrayBuffer &) override; + jsi::Value getValueAtIndex(const jsi::Array &, size_t i) override; + void setValueAtIndexImpl(jsi::Array &, size_t i, const jsi::Value &value) + override; + + jsi::Function createFunctionFromHostFunction( + const jsi::PropNameID &name, + unsigned int paramCount, + jsi::HostFunctionType func) override; + jsi::Value call( + const jsi::Function &, + const jsi::Value &jsThis, + const jsi::Value *args, + size_t count) override; + jsi::Value callAsConstructor( + const jsi::Function &, + const jsi::Value *args, + size_t count) override; + + bool strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const override; + bool strictEquals(const jsi::String &a, const jsi::String &b) const override; + bool strictEquals(const jsi::Object &a, const jsi::Object &b) const override; + bool instanceOf(const jsi::Object &o, const jsi::Function &f) override; + + private: + // Basically convenience casts + static JSValueRef symbolRef(const jsi::Symbol &str); + static JSStringRef stringRef(const jsi::String &str); + static JSStringRef stringRef(const jsi::PropNameID &sym); + static JSObjectRef objectRef(const jsi::Object &obj); + +#ifdef RN_FABRIC_ENABLED + static JSObjectRef objectRef(const jsi::WeakObject &obj); +#endif + + // Factory methods for creating String/Object + jsi::Symbol createSymbol(JSValueRef symbolRef) const; + jsi::String createString(JSStringRef stringRef) const; + jsi::PropNameID createPropNameID(JSStringRef stringRef); + jsi::Object createObject(JSObjectRef objectRef) const; + + // Used by factory methods and clone methods + jsi::Runtime::PointerValue *makeSymbolValue(JSValueRef sym) const; + jsi::Runtime::PointerValue *makeStringValue(JSStringRef str) const; + jsi::Runtime::PointerValue *makeObjectValue(JSObjectRef obj) const; + + void checkException(JSValueRef exc); + void checkException(JSValueRef res, JSValueRef exc); + void checkException(JSValueRef exc, const char *msg); + void checkException(JSValueRef res, JSValueRef exc, const char *msg); + + JSGlobalContextRef ctx_; + std::atomic ctxInvalid_; + std::string desc_; +#ifndef NDEBUG + mutable std::atomic objectCounter_; + mutable std::atomic symbolCounter_; + mutable std::atomic stringCounter_; +#endif +}; + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__builtin_expect) || defined(__GNUC__) +#define JSC_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) +#define JSC_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) +#else +#define JSC_LIKELY(EXPR) (EXPR) +#define JSC_UNLIKELY(EXPR) (EXPR) +#endif + +#define JSC_ASSERT(x) \ + do { \ + if (JSC_UNLIKELY(!!(x))) { \ + abort(); \ + } \ + } while (0) + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +// This takes care of watch and tvos (due to backwards compatibility in +// Availability.h +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0 +#define _JSC_FAST_IS_ARRAY +#endif +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 +#define _JSC_NO_ARRAY_BUFFERS +#endif +#endif +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11 +// Only one of these should be set for a build. If somehow that's not +// true, this will be a compile-time error and it can be resolved when +// we understand why. +#define _JSC_FAST_IS_ARRAY +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12 +#define _JSC_NO_ARRAY_BUFFERS +#endif +#endif + +// JSStringRef utilities +namespace { +std::string JSStringToSTLString(JSStringRef str) { + // Small string optimization: Avoid one heap allocation for strings that fit + // in stackBuffer.size() bytes of UTF-8 (including the null terminator). + std::array stackBuffer; + std::unique_ptr heapBuffer; + char *buffer; + // NOTE: By definition, maxBytes >= 1 since the null terminator is included. + size_t maxBytes = JSStringGetMaximumUTF8CStringSize(str); + if (maxBytes <= stackBuffer.size()) { + buffer = stackBuffer.data(); + } else { + heapBuffer = std::make_unique(maxBytes); + buffer = heapBuffer.get(); + } + size_t actualBytes = JSStringGetUTF8CString(str, buffer, maxBytes); + if (!actualBytes) { + // Happens if maxBytes == 0 (never the case here) or if str contains + // invalid UTF-16 data, since JSStringGetUTF8CString attempts a strict + // conversion. + // When converting an invalid string, JSStringGetUTF8CString writes a null + // terminator before returning. So we can reliably treat our buffer as a C + // string and return the truncated data to our caller. This is slightly + // slower than if we knew the length (like below) but better than crashing. + // TODO(T62295565): Perform a non-strict, best effort conversion of the + // full string instead, like we did before the JSI migration. + return std::string(buffer); + } + return std::string(buffer, actualBytes - 1); +} + +JSStringRef getLengthString() { + static JSStringRef length = JSStringCreateWithUTF8CString("length"); + return length; +} + +JSStringRef getNameString() { + static JSStringRef name = JSStringCreateWithUTF8CString("name"); + return name; +} + +JSStringRef getFunctionString() { + static JSStringRef func = JSStringCreateWithUTF8CString("Function"); + return func; +} + +#if !defined(_JSC_FAST_IS_ARRAY) +JSStringRef getArrayString() { + static JSStringRef array = JSStringCreateWithUTF8CString("Array"); + return array; +} + +JSStringRef getIsArrayString() { + static JSStringRef isArray = JSStringCreateWithUTF8CString("isArray"); + return isArray; +} +#endif +} // namespace + +// std::string utility +namespace { +std::string to_string(void *value) { + std::ostringstream ss; + ss << value; + return ss.str(); +} +} // namespace + +JSCRuntime::JSCRuntime() + : JSCRuntime(JSGlobalContextCreateInGroup(nullptr, nullptr)) { + JSGlobalContextRelease(ctx_); +} + +JSCRuntime::JSCRuntime(JSGlobalContextRef ctx) + : ctx_(JSGlobalContextRetain(ctx)), + ctxInvalid_(false) +#ifndef NDEBUG + , + objectCounter_(0), + stringCounter_(0) +#endif +{ +} + +JSCRuntime::~JSCRuntime() { + // On shutting down and cleaning up: when JSC is actually torn down, + // it calls JSC::Heap::lastChanceToFinalize internally which + // finalizes anything left over. But at this point, + // JSValueUnprotect() can no longer be called. We use an + // atomic to avoid unsafe unprotects happening after shutdown + // has started. + ctxInvalid_ = true; + JSGlobalContextRelease(ctx_); +#ifndef NDEBUG + assert( + objectCounter_ == 0 && "JSCRuntime destroyed with a dangling API object"); + assert( + stringCounter_ == 0 && "JSCRuntime destroyed with a dangling API string"); +#endif +} + +std::shared_ptr JSCRuntime::prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) { + return std::make_shared( + buffer, std::move(sourceURL)); +} + +jsi::Value JSCRuntime::evaluatePreparedJavaScript( + const std::shared_ptr &js) { + assert( + dynamic_cast(js.get()) && + "preparedJavaScript must be a SourceJavaScriptPreparation"); + auto sourceJs = + std::static_pointer_cast(js); + return evaluateJavaScript(sourceJs, sourceJs->sourceURL()); +} + +jsi::Value JSCRuntime::evaluateJavaScript( + const std::shared_ptr &buffer, + const std::string &sourceURL) { + std::string tmp( + reinterpret_cast(buffer->data()), buffer->size()); + JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str()); + JSStringRef sourceURLRef = nullptr; + if (!sourceURL.empty()) { + sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str()); + } + JSValueRef exc = nullptr; + JSValueRef res = + JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc); + JSStringRelease(sourceRef); + if (sourceURLRef) { + JSStringRelease(sourceURLRef); + } + checkException(res, exc); + return createValue(res); +} + +bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) { + return true; +} + +jsi::Object JSCRuntime::global() { + return createObject(JSContextGetGlobalObject(ctx_)); +} + +std::string JSCRuntime::description() { + if (desc_.empty()) { + desc_ = std::string(""; + } + return desc_; +} + +bool JSCRuntime::isInspectable() { + return false; +} + +namespace { + +bool smellsLikeES6Symbol(JSGlobalContextRef ctx, JSValueRef ref) { + // Since iOS 13, JSValueGetType will return kJSTypeSymbol + // Before: Empirically, an es6 Symbol is not an object, but its type is + // object. This makes no sense, but we'll run with it. + // https://github.com/WebKit/webkit/blob/master/Source/JavaScriptCore/API/JSValueRef.cpp#L79-L82 + + JSType type = JSValueGetType(ctx, ref); + + if (type == /* kJSTypeSymbol */ 6) { + return true; + } + + return (!JSValueIsObject(ctx, ref) && type == kJSTypeObject); +} + +} // namespace + +JSCRuntime::JSCSymbolValue::JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym +#ifndef NDEBUG + , + std::atomic &counter +#endif + ) + : ctx_(ctx), + ctxInvalid_(ctxInvalid), + sym_(sym) +#ifndef NDEBUG + , + counter_(counter) +#endif +{ + assert(smellsLikeES6Symbol(ctx_, sym_)); + JSValueProtect(ctx_, sym_); +#ifndef NDEBUG + counter_ += 1; +#endif +} + +void JSCRuntime::JSCSymbolValue::invalidate() { +#ifndef NDEBUG + counter_ -= 1; +#endif + + if (!ctxInvalid_) { + JSValueUnprotect(ctx_, sym_); + } + delete this; +} + +#ifndef NDEBUG +JSCRuntime::JSCStringValue::JSCStringValue( + JSStringRef str, + std::atomic &counter) + : str_(JSStringRetain(str)), counter_(counter) { + // Since std::atomic returns a copy instead of a reference when calling + // operator+= we must do this explicitly in the constructor + counter_ += 1; +} +#else +JSCRuntime::JSCStringValue::JSCStringValue(JSStringRef str) + : str_(JSStringRetain(str)) {} +#endif + +void JSCRuntime::JSCStringValue::invalidate() { + // These JSC{String,Object}Value objects are implicitly owned by the + // {String,Object} objects, thus when a String/Object is destructed + // the JSC{String,Object}Value should be released. +#ifndef NDEBUG + counter_ -= 1; +#endif + JSStringRelease(str_); + // Angery reaccs only + delete this; +} + +JSCRuntime::JSCObjectValue::JSCObjectValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSObjectRef obj +#ifndef NDEBUG + , + std::atomic &counter +#endif + ) + : ctx_(ctx), + ctxInvalid_(ctxInvalid), + obj_(obj) +#ifndef NDEBUG + , + counter_(counter) +#endif +{ + JSValueProtect(ctx_, obj_); +#ifndef NDEBUG + counter_ += 1; +#endif +} + +void JSCRuntime::JSCObjectValue::invalidate() { +#ifndef NDEBUG + counter_ -= 1; +#endif + // When shutting down the VM, if there is a HostObject which + // contains or otherwise owns a jsi::Object, then the final GC will + // finalize the HostObject, leading to a call to invalidate(). But + // at that point, making calls to JSValueUnprotect will crash. + // It is up to the application to make sure that any other calls to + // invalidate() happen before VM destruction; see the comment on + // jsi::Runtime. + // + // Another potential concern here is that in the non-shutdown case, + // if a HostObject is GCd, JSValueUnprotect will be called from the + // JSC finalizer. The documentation warns against this: "You must + // not call any function that may cause a garbage collection or an + // allocation of a garbage collected object from within a + // JSObjectFinalizeCallback. This includes all functions that have a + // JSContextRef parameter." However, an audit of the source code for + // JSValueUnprotect in late 2018 shows that it cannot cause + // allocation or a GC, and further, this code has not changed in + // about two years. In the future, we may choose to reintroduce the + // mechanism previously used here which uses a separate thread for + // JSValueUnprotect, in order to conform to the documented API, but + // use the "unsafe" synchronous version on iOS 11 and earlier. + + if (!ctxInvalid_) { + JSValueUnprotect(ctx_, obj_); + } + delete this; +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneSymbol( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCSymbolValue *symbol = static_cast(pv); + return makeSymbolValue(symbol->sym_); +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneString( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCStringValue *string = static_cast(pv); + return makeStringValue(string->str_); +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneObject( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCObjectValue *object = static_cast(pv); + assert( + object->ctx_ == ctx_ && + "Don't try to clone an object backed by a different Runtime"); + return makeObjectValue(object->obj_); +} + +jsi::Runtime::PointerValue *JSCRuntime::clonePropNameID( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCStringValue *string = static_cast(pv); + return makeStringValue(string->str_); +} + +jsi::PropNameID JSCRuntime::createPropNameIDFromAscii( + const char *str, + size_t length) { + // For system JSC this must is identical to a string + std::string tmp(str, length); + JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str()); + auto res = createPropNameID(strRef); + JSStringRelease(strRef); + return res; +} + +jsi::PropNameID JSCRuntime::createPropNameIDFromUtf8( + const uint8_t *utf8, + size_t length) { + std::string tmp(reinterpret_cast(utf8), length); + JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str()); + auto res = createPropNameID(strRef); + JSStringRelease(strRef); + return res; +} + +jsi::PropNameID JSCRuntime::createPropNameIDFromString(const jsi::String &str) { + return createPropNameID(stringRef(str)); +} + +std::string JSCRuntime::utf8(const jsi::PropNameID &sym) { + return JSStringToSTLString(stringRef(sym)); +} + +bool JSCRuntime::compare(const jsi::PropNameID &a, const jsi::PropNameID &b) { + return JSStringIsEqual(stringRef(a), stringRef(b)); +} + +std::string JSCRuntime::symbolToString(const jsi::Symbol &sym) { + return jsi::Value(*this, sym).toString(*this).utf8(*this); +} + +jsi::String JSCRuntime::createStringFromAscii(const char *str, size_t length) { + // Yes we end up double casting for semantic reasons (UTF8 contains ASCII, + // not the other way around) + return this->createStringFromUtf8( + reinterpret_cast(str), length); +} + +jsi::String JSCRuntime::createStringFromUtf8( + const uint8_t *str, + size_t length) { + std::string tmp(reinterpret_cast(str), length); + JSStringRef stringRef = JSStringCreateWithUTF8CString(tmp.c_str()); + auto result = createString(stringRef); + JSStringRelease(stringRef); + return result; +} + +std::string JSCRuntime::utf8(const jsi::String &str) { + return JSStringToSTLString(stringRef(str)); +} + +jsi::Object JSCRuntime::createObject() { + return createObject(static_cast(nullptr)); +} + +// HostObject details +namespace detail { +struct HostObjectProxyBase { + HostObjectProxyBase( + JSCRuntime &rt, + const std::shared_ptr &sho) + : runtime(rt), hostObject(sho) {} + + JSCRuntime &runtime; + std::shared_ptr hostObject; +}; +} // namespace detail + +namespace { +std::once_flag hostObjectClassOnceFlag; +JSClassRef hostObjectClass{}; +} // namespace + +jsi::Object JSCRuntime::createObject(std::shared_ptr ho) { + struct HostObjectProxy : public detail::HostObjectProxyBase { + static JSValueRef getProperty( + JSContextRef ctx, + JSObjectRef object, + JSStringRef propName, + JSValueRef *exception) { + auto proxy = static_cast(JSObjectGetPrivate(object)); + auto &rt = proxy->runtime; + jsi::PropNameID sym = rt.createPropNameID(propName); + jsi::Value ret; + try { + ret = proxy->hostObject->get(rt, sym); + } catch (const jsi::JSError &error) { + *exception = rt.valueRef(error.value()); + return JSValueMakeUndefined(ctx); + } catch (const std::exception &ex) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::get(propName:") + + JSStringToSTLString(propName) + std::string("): ") + + ex.what()); + *exception = rt.valueRef(excValue); + return JSValueMakeUndefined(ctx); + } catch (...) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::get(propName:") + + JSStringToSTLString(propName) + + std::string("): ")); + *exception = rt.valueRef(excValue); + return JSValueMakeUndefined(ctx); + } + return rt.valueRef(ret); + } + +#define JSC_UNUSED(x) (void)(x); + + static bool setProperty( + JSContextRef ctx, + JSObjectRef object, + JSStringRef propName, + JSValueRef value, + JSValueRef *exception) { + JSC_UNUSED(ctx); + auto proxy = static_cast(JSObjectGetPrivate(object)); + auto &rt = proxy->runtime; + jsi::PropNameID sym = rt.createPropNameID(propName); + try { + proxy->hostObject->set(rt, sym, rt.createValue(value)); + } catch (const jsi::JSError &error) { + *exception = rt.valueRef(error.value()); + return false; + } catch (const std::exception &ex) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::set(propName:") + + JSStringToSTLString(propName) + std::string("): ") + + ex.what()); + *exception = rt.valueRef(excValue); + return false; + } catch (...) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::set(propName:") + + JSStringToSTLString(propName) + + std::string("): ")); + *exception = rt.valueRef(excValue); + return false; + } + return true; + } + + // JSC does not provide means to communicate errors from this callback, + // so the error handling strategy is very brutal - we'll just crash + // due to noexcept. + static void getPropertyNames( + JSContextRef ctx, + JSObjectRef object, + JSPropertyNameAccumulatorRef propertyNames) noexcept { + JSC_UNUSED(ctx); + auto proxy = static_cast(JSObjectGetPrivate(object)); + auto &rt = proxy->runtime; + auto names = proxy->hostObject->getPropertyNames(rt); + for (auto &name : names) { + JSPropertyNameAccumulatorAddName(propertyNames, stringRef(name)); + } + } + +#undef JSC_UNUSED + + static void finalize(JSObjectRef obj) { + auto hostObject = static_cast(JSObjectGetPrivate(obj)); + JSObjectSetPrivate(obj, nullptr); + delete hostObject; + } + + using HostObjectProxyBase::HostObjectProxyBase; + }; + + std::call_once(hostObjectClassOnceFlag, []() { + JSClassDefinition hostObjectClassDef = kJSClassDefinitionEmpty; + hostObjectClassDef.version = 0; + hostObjectClassDef.attributes = kJSClassAttributeNoAutomaticPrototype; + hostObjectClassDef.finalize = HostObjectProxy::finalize; + hostObjectClassDef.getProperty = HostObjectProxy::getProperty; + hostObjectClassDef.setProperty = HostObjectProxy::setProperty; + hostObjectClassDef.getPropertyNames = HostObjectProxy::getPropertyNames; + hostObjectClass = JSClassCreate(&hostObjectClassDef); + }); + + JSObjectRef obj = + JSObjectMake(ctx_, hostObjectClass, new HostObjectProxy(*this, ho)); + return createObject(obj); +} + +std::shared_ptr JSCRuntime::getHostObject( + const jsi::Object &obj) { + // We are guaranteed at this point to have isHostObject(obj) == true + // so the private data should be HostObjectMetadata + JSObjectRef object = objectRef(obj); + auto metadata = + static_cast(JSObjectGetPrivate(object)); + assert(metadata); + return metadata->hostObject; +} + +jsi::Value JSCRuntime::getProperty( + const jsi::Object &obj, + const jsi::String &name) { + JSObjectRef objRef = objectRef(obj); + JSValueRef exc = nullptr; + JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc); + checkException(exc); + return createValue(res); +} + +jsi::Value JSCRuntime::getProperty( + const jsi::Object &obj, + const jsi::PropNameID &name) { + JSObjectRef objRef = objectRef(obj); + JSValueRef exc = nullptr; + JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc); + checkException(exc); + return createValue(res); +} + +bool JSCRuntime::hasProperty(const jsi::Object &obj, const jsi::String &name) { + JSObjectRef objRef = objectRef(obj); + return JSObjectHasProperty(ctx_, objRef, stringRef(name)); +} + +bool JSCRuntime::hasProperty( + const jsi::Object &obj, + const jsi::PropNameID &name) { + JSObjectRef objRef = objectRef(obj); + return JSObjectHasProperty(ctx_, objRef, stringRef(name)); +} + +void JSCRuntime::setPropertyValue( + jsi::Object &object, + const jsi::PropNameID &name, + const jsi::Value &value) { + JSValueRef exc = nullptr; + JSObjectSetProperty( + ctx_, + objectRef(object), + stringRef(name), + valueRef(value), + kJSPropertyAttributeNone, + &exc); + checkException(exc); +} + +void JSCRuntime::setPropertyValue( + jsi::Object &object, + const jsi::String &name, + const jsi::Value &value) { + JSValueRef exc = nullptr; + JSObjectSetProperty( + ctx_, + objectRef(object), + stringRef(name), + valueRef(value), + kJSPropertyAttributeNone, + &exc); + checkException(exc); +} + +bool JSCRuntime::isArray(const jsi::Object &obj) const { +#if !defined(_JSC_FAST_IS_ARRAY) + JSObjectRef global = JSContextGetGlobalObject(ctx_); + JSStringRef arrayString = getArrayString(); + JSValueRef exc = nullptr; + JSValueRef arrayCtorValue = + JSObjectGetProperty(ctx_, global, arrayString, &exc); + JSC_ASSERT(exc); + JSObjectRef arrayCtor = JSValueToObject(ctx_, arrayCtorValue, &exc); + JSC_ASSERT(exc); + JSStringRef isArrayString = getIsArrayString(); + JSValueRef isArrayValue = + JSObjectGetProperty(ctx_, arrayCtor, isArrayString, &exc); + JSC_ASSERT(exc); + JSObjectRef isArray = JSValueToObject(ctx_, isArrayValue, &exc); + JSC_ASSERT(exc); + JSValueRef arg = objectRef(obj); + JSValueRef result = + JSObjectCallAsFunction(ctx_, isArray, nullptr, 1, &arg, &exc); + JSC_ASSERT(exc); + return JSValueToBoolean(ctx_, result); +#else + return JSValueIsArray(ctx_, objectRef(obj)); +#endif +} + +bool JSCRuntime::isArrayBuffer(const jsi::Object &obj) const { +#if defined(_JSC_NO_ARRAY_BUFFERS) + throw std::runtime_error("Unsupported"); +#else + auto typedArrayType = JSValueGetTypedArrayType(ctx_, objectRef(obj), nullptr); + return typedArrayType == kJSTypedArrayTypeArrayBuffer; +#endif +} + +uint8_t *JSCRuntime::data(const jsi::ArrayBuffer &obj) { +#if defined(_JSC_NO_ARRAY_BUFFERS) + throw std::runtime_error("Unsupported"); +#else + return static_cast( + JSObjectGetArrayBufferBytesPtr(ctx_, objectRef(obj), nullptr)); +#endif +} + +size_t JSCRuntime::size(const jsi::ArrayBuffer &obj) { +#if defined(_JSC_NO_ARRAY_BUFFERS) + throw std::runtime_error("Unsupported"); +#else + return JSObjectGetArrayBufferByteLength(ctx_, objectRef(obj), nullptr); +#endif +} + +bool JSCRuntime::isFunction(const jsi::Object &obj) const { + return JSObjectIsFunction(ctx_, objectRef(obj)); +} + +bool JSCRuntime::isHostObject(const jsi::Object &obj) const { + auto cls = hostObjectClass; + return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls); +} + +// Very expensive +jsi::Array JSCRuntime::getPropertyNames(const jsi::Object &obj) { + JSPropertyNameArrayRef names = + JSObjectCopyPropertyNames(ctx_, objectRef(obj)); + size_t len = JSPropertyNameArrayGetCount(names); + // Would be better if we could create an array with explicit elements + auto result = createArray(len); + for (size_t i = 0; i < len; i++) { + JSStringRef str = JSPropertyNameArrayGetNameAtIndex(names, i); + result.setValueAtIndex(*this, i, createString(str)); + } + JSPropertyNameArrayRelease(names); + return result; +} + +jsi::WeakObject JSCRuntime::createWeakObject(const jsi::Object &obj) { +#ifdef RN_FABRIC_ENABLED + // TODO: revisit this implementation + JSObjectRef objRef = objectRef(obj); + return make(makeObjectValue(objRef)); +#else + throw std::logic_error("Not implemented"); +#endif +} + +jsi::Value JSCRuntime::lockWeakObject(jsi::WeakObject &obj) { +#ifdef RN_FABRIC_ENABLED + // TODO: revisit this implementation + JSObjectRef objRef = objectRef(obj); + return jsi::Value(createObject(objRef)); +#else + throw std::logic_error("Not implemented"); +#endif +} + +jsi::Array JSCRuntime::createArray(size_t length) { + JSValueRef exc = nullptr; + JSObjectRef obj = JSObjectMakeArray(ctx_, 0, nullptr, &exc); + checkException(obj, exc); + JSObjectSetProperty( + ctx_, + obj, + getLengthString(), + JSValueMakeNumber(ctx_, static_cast(length)), + 0, + &exc); + checkException(exc); + return createObject(obj).getArray(*this); +} + +size_t JSCRuntime::size(const jsi::Array &arr) { + return static_cast( + getProperty(arr, createPropNameID(getLengthString())).getNumber()); +} + +jsi::Value JSCRuntime::getValueAtIndex(const jsi::Array &arr, size_t i) { + JSValueRef exc = nullptr; + auto res = JSObjectGetPropertyAtIndex(ctx_, objectRef(arr), (int)i, &exc); + checkException(exc); + return createValue(res); +} + +void JSCRuntime::setValueAtIndexImpl( + jsi::Array &arr, + size_t i, + const jsi::Value &value) { + JSValueRef exc = nullptr; + JSObjectSetPropertyAtIndex( + ctx_, objectRef(arr), (int)i, valueRef(value), &exc); + checkException(exc); +} + +namespace { +std::once_flag hostFunctionClassOnceFlag; +JSClassRef hostFunctionClass{}; + +class HostFunctionProxy { + public: + HostFunctionProxy(jsi::HostFunctionType hostFunction) + : hostFunction_(hostFunction) {} + + jsi::HostFunctionType &getHostFunction() { + return hostFunction_; + } + + protected: + jsi::HostFunctionType hostFunction_; +}; +} // namespace + +jsi::Function JSCRuntime::createFunctionFromHostFunction( + const jsi::PropNameID &name, + unsigned int paramCount, + jsi::HostFunctionType func) { + class HostFunctionMetadata : public HostFunctionProxy { + public: + static void initialize(JSContextRef ctx, JSObjectRef object) { + // We need to set up the prototype chain properly here. In theory we + // could set func.prototype.prototype = Function.prototype to get the + // same result. Not sure which approach is better. + HostFunctionMetadata *metadata = + static_cast(JSObjectGetPrivate(object)); + + JSValueRef exc = nullptr; + JSObjectSetProperty( + ctx, + object, + getLengthString(), + JSValueMakeNumber(ctx, metadata->argCount), + kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | + kJSPropertyAttributeDontDelete, + &exc); + if (exc) { + // Silently fail to set length + exc = nullptr; + } + + JSStringRef name = nullptr; + std::swap(metadata->name, name); + JSObjectSetProperty( + ctx, + object, + getNameString(), + JSValueMakeString(ctx, name), + kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | + kJSPropertyAttributeDontDelete, + &exc); + JSStringRelease(name); + if (exc) { + // Silently fail to set name + exc = nullptr; + } + + JSObjectRef global = JSContextGetGlobalObject(ctx); + JSValueRef value = + JSObjectGetProperty(ctx, global, getFunctionString(), &exc); + // If we don't have Function then something bad is going on. + if (JSC_UNLIKELY(exc)) { + abort(); + } + JSObjectRef funcCtor = JSValueToObject(ctx, value, &exc); + if (!funcCtor) { + // We can't do anything if Function is not an object + return; + } + JSValueRef funcProto = JSObjectGetPrototype(ctx, funcCtor); + JSObjectSetPrototype(ctx, object, funcProto); + } + + static JSValueRef makeError(JSCRuntime &rt, const std::string &desc) { + jsi::Value value = + rt.global().getPropertyAsFunction(rt, "Error").call(rt, desc); + return rt.valueRef(value); + } + + static JSValueRef call( + JSContextRef ctx, + JSObjectRef function, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef *exception) { + HostFunctionMetadata *metadata = + static_cast(JSObjectGetPrivate(function)); + JSCRuntime &rt = *(metadata->runtime); + const unsigned maxStackArgCount = 8; + jsi::Value stackArgs[maxStackArgCount]; + std::unique_ptr heapArgs; + jsi::Value *args; + if (argumentCount > maxStackArgCount) { + heapArgs = std::make_unique(argumentCount); + for (size_t i = 0; i < argumentCount; i++) { + heapArgs[i] = rt.createValue(arguments[i]); + } + args = heapArgs.get(); + } else { + for (size_t i = 0; i < argumentCount; i++) { + stackArgs[i] = rt.createValue(arguments[i]); + } + args = stackArgs; + } + JSValueRef res; + jsi::Value thisVal(rt.createObject(thisObject)); + try { + res = rt.valueRef( + metadata->hostFunction_(rt, thisVal, args, argumentCount)); + } catch (const jsi::JSError &error) { + *exception = rt.valueRef(error.value()); + res = JSValueMakeUndefined(ctx); + } catch (const std::exception &ex) { + std::string exceptionString("Exception in HostFunction: "); + exceptionString += ex.what(); + *exception = makeError(rt, exceptionString); + res = JSValueMakeUndefined(ctx); + } catch (...) { + std::string exceptionString("Exception in HostFunction: "); + *exception = makeError(rt, exceptionString); + res = JSValueMakeUndefined(ctx); + } + return res; + } + + static void finalize(JSObjectRef object) { + HostFunctionMetadata *metadata = + static_cast(JSObjectGetPrivate(object)); + JSObjectSetPrivate(object, nullptr); + delete metadata; + } + + HostFunctionMetadata( + JSCRuntime *rt, + jsi::HostFunctionType hf, + unsigned ac, + JSStringRef n) + : HostFunctionProxy(hf), + runtime(rt), + argCount(ac), + name(JSStringRetain(n)) {} + + JSCRuntime *runtime; + unsigned argCount; + JSStringRef name; + }; + + std::call_once(hostFunctionClassOnceFlag, []() { + JSClassDefinition functionClass = kJSClassDefinitionEmpty; + functionClass.version = 0; + functionClass.attributes = kJSClassAttributeNoAutomaticPrototype; + functionClass.initialize = HostFunctionMetadata::initialize; + functionClass.finalize = HostFunctionMetadata::finalize; + functionClass.callAsFunction = HostFunctionMetadata::call; + + hostFunctionClass = JSClassCreate(&functionClass); + }); + + JSObjectRef funcRef = JSObjectMake( + ctx_, + hostFunctionClass, + new HostFunctionMetadata(this, func, paramCount, stringRef(name))); + return createObject(funcRef).getFunction(*this); +} + +namespace detail { + +class ArgsConverter { + public: + ArgsConverter(JSCRuntime &rt, const jsi::Value *args, size_t count) { + JSValueRef *destination = inline_; + if (count > maxStackArgs) { + outOfLine_ = std::make_unique(count); + destination = outOfLine_.get(); + } + + for (size_t i = 0; i < count; ++i) { + destination[i] = rt.valueRef(args[i]); + } + } + + operator JSValueRef *() { + return outOfLine_ ? outOfLine_.get() : inline_; + } + + private: + constexpr static unsigned maxStackArgs = 8; + JSValueRef inline_[maxStackArgs]; + std::unique_ptr outOfLine_; +}; +} // namespace detail + +bool JSCRuntime::isHostFunction(const jsi::Function &obj) const { + auto cls = hostFunctionClass; + return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls); +} + +jsi::HostFunctionType &JSCRuntime::getHostFunction(const jsi::Function &obj) { + // We know that isHostFunction(obj) is true here, so its safe to proceed + auto proxy = + static_cast(JSObjectGetPrivate(objectRef(obj))); + return proxy->getHostFunction(); +} + +jsi::Value JSCRuntime::call( + const jsi::Function &f, + const jsi::Value &jsThis, + const jsi::Value *args, + size_t count) { + JSValueRef exc = nullptr; + auto res = JSObjectCallAsFunction( + ctx_, + objectRef(f), + jsThis.isUndefined() ? nullptr : objectRef(jsThis.getObject(*this)), + count, + detail::ArgsConverter(*this, args, count), + &exc); + checkException(exc); + return createValue(res); +} + +jsi::Value JSCRuntime::callAsConstructor( + const jsi::Function &f, + const jsi::Value *args, + size_t count) { + JSValueRef exc = nullptr; + auto res = JSObjectCallAsConstructor( + ctx_, + objectRef(f), + count, + detail::ArgsConverter(*this, args, count), + &exc); + checkException(exc); + return createValue(res); +} + +bool JSCRuntime::strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) + const { + JSValueRef exc = nullptr; + bool ret = JSValueIsEqual(ctx_, symbolRef(a), symbolRef(b), &exc); + const_cast(this)->checkException(exc); + return ret; +} + +bool JSCRuntime::strictEquals(const jsi::String &a, const jsi::String &b) + const { + return JSStringIsEqual(stringRef(a), stringRef(b)); +} + +bool JSCRuntime::strictEquals(const jsi::Object &a, const jsi::Object &b) + const { + return objectRef(a) == objectRef(b); +} + +bool JSCRuntime::instanceOf(const jsi::Object &o, const jsi::Function &f) { + JSValueRef exc = nullptr; + bool res = + JSValueIsInstanceOfConstructor(ctx_, objectRef(o), objectRef(f), &exc); + checkException(exc); + return res; +} + +jsi::Runtime::PointerValue *JSCRuntime::makeSymbolValue( + JSValueRef symbolRef) const { +#ifndef NDEBUG + return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef, symbolCounter_); +#else + return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef); +#endif +} + +namespace { +JSStringRef getEmptyString() { + static JSStringRef empty = JSStringCreateWithUTF8CString(""); + return empty; +} +} // namespace + +jsi::Runtime::PointerValue *JSCRuntime::makeStringValue( + JSStringRef stringRef) const { + if (!stringRef) { + stringRef = getEmptyString(); + } +#ifndef NDEBUG + return new JSCStringValue(stringRef, stringCounter_); +#else + return new JSCStringValue(stringRef); +#endif +} + +jsi::Symbol JSCRuntime::createSymbol(JSValueRef sym) const { + return make(makeSymbolValue(sym)); +} + +jsi::String JSCRuntime::createString(JSStringRef str) const { + return make(makeStringValue(str)); +} + +jsi::PropNameID JSCRuntime::createPropNameID(JSStringRef str) { + return make(makeStringValue(str)); +} + +jsi::Runtime::PointerValue *JSCRuntime::makeObjectValue( + JSObjectRef objectRef) const { + if (!objectRef) { + objectRef = JSObjectMake(ctx_, nullptr, nullptr); + } +#ifndef NDEBUG + return new JSCObjectValue(ctx_, ctxInvalid_, objectRef, objectCounter_); +#else + return new JSCObjectValue(ctx_, ctxInvalid_, objectRef); +#endif +} + +jsi::Object JSCRuntime::createObject(JSObjectRef obj) const { + return make(makeObjectValue(obj)); +} + +jsi::Value JSCRuntime::createValue(JSValueRef value) const { + JSType type = JSValueGetType(ctx_, value); + + switch (type) { + case kJSTypeNumber: + return jsi::Value(JSValueToNumber(ctx_, value, nullptr)); + case kJSTypeBoolean: + return jsi::Value(JSValueToBoolean(ctx_, value)); + case kJSTypeNull: + return jsi::Value(nullptr); + case kJSTypeUndefined: + return jsi::Value(); + case kJSTypeString: { + JSStringRef str = JSValueToStringCopy(ctx_, value, nullptr); + auto result = jsi::Value(createString(str)); + JSStringRelease(str); + return result; + } + case kJSTypeObject: { + JSObjectRef objRef = JSValueToObject(ctx_, value, nullptr); + return jsi::Value(createObject(objRef)); + } + // TODO: Uncomment this when all supported JSC versions have this symbol + // case kJSTypeSymbol: + default: { + if (smellsLikeES6Symbol(ctx_, value)) { + return jsi::Value(createSymbol(value)); + } else { + // WHAT ARE YOU + abort(); + } + } + } +} + +JSValueRef JSCRuntime::valueRef(const jsi::Value &value) { + // I would rather switch on value.kind_ + if (value.isUndefined()) { + return JSValueMakeUndefined(ctx_); + } else if (value.isNull()) { + return JSValueMakeNull(ctx_); + } else if (value.isBool()) { + return JSValueMakeBoolean(ctx_, value.getBool()); + } else if (value.isNumber()) { + return JSValueMakeNumber(ctx_, value.getNumber()); + } else if (value.isSymbol()) { + return symbolRef(value.getSymbol(*this)); + } else if (value.isString()) { + return JSValueMakeString(ctx_, stringRef(value.getString(*this))); + } else if (value.isObject()) { + return objectRef(value.getObject(*this)); + } else { + // What are you? + abort(); + } +} + +JSValueRef JSCRuntime::symbolRef(const jsi::Symbol &sym) { + return static_cast(getPointerValue(sym))->sym_; +} + +JSStringRef JSCRuntime::stringRef(const jsi::String &str) { + return static_cast(getPointerValue(str))->str_; +} + +JSStringRef JSCRuntime::stringRef(const jsi::PropNameID &sym) { + return static_cast(getPointerValue(sym))->str_; +} + +JSObjectRef JSCRuntime::objectRef(const jsi::Object &obj) { + return static_cast(getPointerValue(obj))->obj_; +} + +#ifdef RN_FABRIC_ENABLED +JSObjectRef JSCRuntime::objectRef(const jsi::WeakObject &obj) { + // TODO: revisit this implementation + return static_cast(getPointerValue(obj))->obj_; +} +#endif + +void JSCRuntime::checkException(JSValueRef exc) { + if (JSC_UNLIKELY(exc)) { + throw jsi::JSError(*this, createValue(exc)); + } +} + +void JSCRuntime::checkException(JSValueRef res, JSValueRef exc) { + if (JSC_UNLIKELY(!res)) { + throw jsi::JSError(*this, createValue(exc)); + } +} + +void JSCRuntime::checkException(JSValueRef exc, const char *msg) { + if (JSC_UNLIKELY(exc)) { + throw jsi::JSError(std::string(msg), *this, createValue(exc)); + } +} + +void JSCRuntime::checkException( + JSValueRef res, + JSValueRef exc, + const char *msg) { + if (JSC_UNLIKELY(!res)) { + throw jsi::JSError(std::string(msg), *this, createValue(exc)); + } +} + +std::unique_ptr makeJSCRuntime() { + return std::make_unique(); +} + +} // namespace jsc +} // namespace facebook diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h new file mode 100644 index 00000000000..a3619544459 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h @@ -0,0 +1,753 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include + +// This file contains objects to help API users create their own +// runtime adapters, i.e. if you want to compose runtimes to add your +// own behavior. + +namespace facebook { +namespace jsi { + +// Use this to wrap host functions. It will pass the member runtime as +// the first arg to the callback. The first argument to the ctor +// should be the decorated runtime, not the plain one. +class DecoratedHostFunction { + public: + DecoratedHostFunction(Runtime& drt, HostFunctionType plainHF) + : drt_(drt), plainHF_(std::move(plainHF)) {} + + Runtime& decoratedRuntime() { + return drt_; + } + + Value + operator()(Runtime&, const Value& thisVal, const Value* args, size_t count) { + return plainHF_(decoratedRuntime(), thisVal, args, count); + } + + private: + template + friend class RuntimeDecorator; + + Runtime& drt_; + HostFunctionType plainHF_; +}; + +// From the perspective of the caller, a plain HostObject is passed to +// the decorated Runtime, and the HostObject methods expect to get +// passed that Runtime. But the plain Runtime will pass itself to its +// callback, so we need a helper here which curries the decorated +// Runtime, and calls the plain HostObject with it. +// +// If the concrete RuntimeDecorator derives DecoratedHostObject, it +// should call the base class get() and set() to invoke the plain +// HostObject functionality. The Runtime& it passes does not matter, +// as it is not used. +class DecoratedHostObject : public HostObject { + public: + DecoratedHostObject(Runtime& drt, std::shared_ptr plainHO) + : drt_(drt), plainHO_(plainHO) {} + + // The derived class methods can call this to get a reference to the + // decorated runtime, since the rt passed to the callback will be + // the plain runtime. + Runtime& decoratedRuntime() { + return drt_; + } + + Value get(Runtime&, const PropNameID& name) override { + return plainHO_->get(decoratedRuntime(), name); + } + + void set(Runtime&, const PropNameID& name, const Value& value) override { + plainHO_->set(decoratedRuntime(), name, value); + } + + std::vector getPropertyNames(Runtime&) override { + return plainHO_->getPropertyNames(decoratedRuntime()); + } + + private: + template + friend class RuntimeDecorator; + + Runtime& drt_; + std::shared_ptr plainHO_; +}; + +/// C++ variant on a standard Decorator pattern, using template +/// parameters. The \c Plain template parameter type is the +/// undecorated Runtime type. You can usually use \c Runtime here, +/// but if you know the concrete type ahead of time and it's final, +/// the compiler can devirtualize calls to the decorated +/// implementation. The \c Base template parameter type will be used +/// as the base class of the decorated type. Here, too, you can +/// usually use \c Runtime, but if you want the decorated type to +/// implement a derived class of Runtime, you can specify that here. +/// For an example, see threadsafe.h. +template +class RuntimeDecorator : public Base, private jsi::Instrumentation { + public: + Plain& plain() { + static_assert( + std::is_base_of::value, + "RuntimeDecorator's Plain type must derive from jsi::Runtime"); + static_assert( + std::is_base_of::value, + "RuntimeDecorator's Base type must derive from jsi::Runtime"); + return plain_; + } + const Plain& plain() const { + return plain_; + } + + Value evaluateJavaScript( + const std::shared_ptr& buffer, + const std::string& sourceURL) override { + return plain().evaluateJavaScript(buffer, sourceURL); + } + std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) override { + return plain().prepareJavaScript(buffer, std::move(sourceURL)); + } + Value evaluatePreparedJavaScript( + const std::shared_ptr& js) override { + return plain().evaluatePreparedJavaScript(js); + } + bool drainMicrotasks(int maxMicrotasksHint) override { + return plain().drainMicrotasks(maxMicrotasksHint); + } + Object global() override { + return plain().global(); + } + std::string description() override { + return plain().description(); + }; + bool isInspectable() override { + return plain().isInspectable(); + }; + Instrumentation& instrumentation() override { + return *this; + } + + protected: + // plain is generally going to be a reference to an object managed + // by a derived class. We cache it here so this class can be + // concrete, and avoid making virtual calls to find the plain + // Runtime. Note that the ctor and dtor do not access through the + // reference, so passing a reference to an object before its + // lifetime has started is ok. + RuntimeDecorator(Plain& plain) : plain_(plain) {} + + Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { + return plain_.cloneSymbol(pv); + }; + Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { + return plain_.cloneString(pv); + }; + Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { + return plain_.cloneObject(pv); + }; + Runtime::PointerValue* clonePropNameID( + const Runtime::PointerValue* pv) override { + return plain_.clonePropNameID(pv); + }; + + PropNameID createPropNameIDFromAscii(const char* str, size_t length) + override { + return plain_.createPropNameIDFromAscii(str, length); + }; + PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) + override { + return plain_.createPropNameIDFromUtf8(utf8, length); + }; + PropNameID createPropNameIDFromString(const String& str) override { + return plain_.createPropNameIDFromString(str); + }; + std::string utf8(const PropNameID& id) override { + return plain_.utf8(id); + }; + bool compare(const PropNameID& a, const PropNameID& b) override { + return plain_.compare(a, b); + }; + + std::string symbolToString(const Symbol& sym) override { + return plain_.symbolToString(sym); + } + + String createStringFromAscii(const char* str, size_t length) override { + return plain_.createStringFromAscii(str, length); + }; + String createStringFromUtf8(const uint8_t* utf8, size_t length) override { + return plain_.createStringFromUtf8(utf8, length); + }; + std::string utf8(const String& s) override { + return plain_.utf8(s); + } + + Object createObject() override { + return plain_.createObject(); + }; + + Object createObject(std::shared_ptr ho) override { + return plain_.createObject( + std::make_shared(*this, std::move(ho))); + }; + std::shared_ptr getHostObject(const jsi::Object& o) override { + std::shared_ptr dho = plain_.getHostObject(o); + return static_cast(*dho).plainHO_; + }; + HostFunctionType& getHostFunction(const jsi::Function& f) override { + HostFunctionType& dhf = plain_.getHostFunction(f); + // This will fail if a cpp file including this header is not compiled + // with RTTI. + return dhf.target()->plainHF_; + }; + + Value getProperty(const Object& o, const PropNameID& name) override { + return plain_.getProperty(o, name); + }; + Value getProperty(const Object& o, const String& name) override { + return plain_.getProperty(o, name); + }; + bool hasProperty(const Object& o, const PropNameID& name) override { + return plain_.hasProperty(o, name); + }; + bool hasProperty(const Object& o, const String& name) override { + return plain_.hasProperty(o, name); + }; + void setPropertyValue(Object& o, const PropNameID& name, const Value& value) + override { + plain_.setPropertyValue(o, name, value); + }; + void setPropertyValue(Object& o, const String& name, const Value& value) + override { + plain_.setPropertyValue(o, name, value); + }; + + bool isArray(const Object& o) const override { + return plain_.isArray(o); + }; + bool isArrayBuffer(const Object& o) const override { + return plain_.isArrayBuffer(o); + }; + bool isFunction(const Object& o) const override { + return plain_.isFunction(o); + }; + bool isHostObject(const jsi::Object& o) const override { + return plain_.isHostObject(o); + }; + bool isHostFunction(const jsi::Function& f) const override { + return plain_.isHostFunction(f); + }; + Array getPropertyNames(const Object& o) override { + return plain_.getPropertyNames(o); + }; + + WeakObject createWeakObject(const Object& o) override { + return plain_.createWeakObject(o); + }; + Value lockWeakObject(WeakObject& wo) override { + return plain_.lockWeakObject(wo); + }; + + Array createArray(size_t length) override { + return plain_.createArray(length); + }; + size_t size(const Array& a) override { + return plain_.size(a); + }; + size_t size(const ArrayBuffer& ab) override { + return plain_.size(ab); + }; + uint8_t* data(const ArrayBuffer& ab) override { + return plain_.data(ab); + }; + Value getValueAtIndex(const Array& a, size_t i) override { + return plain_.getValueAtIndex(a, i); + }; + void setValueAtIndexImpl(Array& a, size_t i, const Value& value) override { + plain_.setValueAtIndexImpl(a, i, value); + }; + + Function createFunctionFromHostFunction( + const PropNameID& name, + unsigned int paramCount, + HostFunctionType func) override { + return plain_.createFunctionFromHostFunction( + name, paramCount, DecoratedHostFunction(*this, std::move(func))); + }; + Value call( + const Function& f, + const Value& jsThis, + const Value* args, + size_t count) override { + return plain_.call(f, jsThis, args, count); + }; + Value callAsConstructor(const Function& f, const Value* args, size_t count) + override { + return plain_.callAsConstructor(f, args, count); + }; + + // Private data for managing scopes. + Runtime::ScopeState* pushScope() override { + return plain_.pushScope(); + } + void popScope(Runtime::ScopeState* ss) override { + plain_.popScope(ss); + } + + bool strictEquals(const Symbol& a, const Symbol& b) const override { + return plain_.strictEquals(a, b); + }; + bool strictEquals(const String& a, const String& b) const override { + return plain_.strictEquals(a, b); + }; + bool strictEquals(const Object& a, const Object& b) const override { + return plain_.strictEquals(a, b); + }; + + bool instanceOf(const Object& o, const Function& f) override { + return plain_.instanceOf(o, f); + }; + + // jsi::Instrumentation methods + + std::string getRecordedGCStats() override { + return plain().instrumentation().getRecordedGCStats(); + } + + std::unordered_map getHeapInfo( + bool includeExpensive) override { + return plain().instrumentation().getHeapInfo(includeExpensive); + } + + void collectGarbage(std::string cause) override { + plain().instrumentation().collectGarbage(std::move(cause)); + } + + void startTrackingHeapObjectStackTraces( + std::function)> callback) override { + plain().instrumentation().startTrackingHeapObjectStackTraces( + std::move(callback)); + } + + void stopTrackingHeapObjectStackTraces() override { + plain().instrumentation().stopTrackingHeapObjectStackTraces(); + } + + void startHeapSampling(size_t samplingInterval) override { + plain().instrumentation().startHeapSampling(samplingInterval); + } + + void stopHeapSampling(std::ostream& os) override { + plain().instrumentation().stopHeapSampling(os); + } + + void createSnapshotToFile(const std::string& path) override { + plain().instrumentation().createSnapshotToFile(path); + } + + void createSnapshotToStream(std::ostream& os) override { + plain().instrumentation().createSnapshotToStream(os); + } + + std::string flushAndDisableBridgeTrafficTrace() override { + return const_cast(plain()) + .instrumentation() + .flushAndDisableBridgeTrafficTrace(); + } + + void writeBasicBlockProfileTraceToFile( + const std::string& fileName) const override { + const_cast(plain()) + .instrumentation() + .writeBasicBlockProfileTraceToFile(fileName); + } + + /// Dump external profiler symbols to the given file name. + void dumpProfilerSymbolsToFile(const std::string& fileName) const override { + const_cast(plain()).instrumentation().dumpProfilerSymbolsToFile( + fileName); + } + + private: + Plain& plain_; +}; + +namespace detail { + +// This metaprogramming allows the With type's methods to be +// optional. + +template +struct BeforeCaller { + static void before(T&) {} +}; + +template +struct AfterCaller { + static void after(T&) {} +}; + +// decltype((void)&...) is either SFINAE, or void. +// So, if SFINAE does not happen for T, then this specialization exists +// for BeforeCaller, and always applies. If not, only the +// default above exists, and that is used instead. +template +struct BeforeCaller { + static void before(T& t) { + t.before(); + } +}; + +template +struct AfterCaller { + static void after(T& t) { + t.after(); + } +}; + +// It's possible to use multiple decorators by nesting +// WithRuntimeDecorator<...>, but this specialization allows use of +// std::tuple of decorator classes instead. See testlib.cpp for an +// example. +template +struct BeforeCaller> { + static void before(std::tuple& tuple) { + all_before<0, T...>(tuple); + } + + private: + template + static void all_before(std::tuple& tuple) { + detail::BeforeCaller::before(std::get(tuple)); + all_before(tuple); + } + + template + static void all_before(std::tuple&) {} +}; + +template +struct AfterCaller> { + static void after(std::tuple& tuple) { + all_after<0, T...>(tuple); + } + + private: + template + static void all_after(std::tuple& tuple) { + all_after(tuple); + detail::AfterCaller::after(std::get(tuple)); + } + + template + static void all_after(std::tuple&) {} +}; + +} // namespace detail + +// A decorator which implements an around idiom. A With instance is +// RAII constructed before each call to the undecorated class; the +// ctor is passed a single argument of type WithArg&. Plain and Base +// are used as in the base class. +template +class WithRuntimeDecorator : public RuntimeDecorator { + public: + using RD = RuntimeDecorator; + + // The reference arguments to the ctor are stored, but not used by + // the ctor, and there is no ctor, so they can be passed members of + // the derived class. + WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {} + + Value evaluateJavaScript( + const std::shared_ptr& buffer, + const std::string& sourceURL) override { + Around around{with_}; + return RD::evaluateJavaScript(buffer, sourceURL); + } + std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) override { + Around around{with_}; + return RD::prepareJavaScript(buffer, std::move(sourceURL)); + } + Value evaluatePreparedJavaScript( + const std::shared_ptr& js) override { + Around around{with_}; + return RD::evaluatePreparedJavaScript(js); + } + bool drainMicrotasks(int maxMicrotasksHint) override { + Around around{with_}; + return RD::drainMicrotasks(maxMicrotasksHint); + } + Object global() override { + Around around{with_}; + return RD::global(); + } + std::string description() override { + Around around{with_}; + return RD::description(); + }; + bool isInspectable() override { + Around around{with_}; + return RD::isInspectable(); + }; + + // The jsi:: prefix is necessary because MSVC compiler complains C2247: + // Instrumentation is not accessible because RuntimeDecorator uses private + // to inherit from Instrumentation. + // TODO(T40821815) Consider removing this workaround when updating MSVC + jsi::Instrumentation& instrumentation() override { + Around around{with_}; + return RD::instrumentation(); + } + + protected: + Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::cloneSymbol(pv); + }; + Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::cloneString(pv); + }; + Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::cloneObject(pv); + }; + Runtime::PointerValue* clonePropNameID( + const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::clonePropNameID(pv); + }; + + PropNameID createPropNameIDFromAscii(const char* str, size_t length) + override { + Around around{with_}; + return RD::createPropNameIDFromAscii(str, length); + }; + PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) + override { + Around around{with_}; + return RD::createPropNameIDFromUtf8(utf8, length); + }; + PropNameID createPropNameIDFromString(const String& str) override { + Around around{with_}; + return RD::createPropNameIDFromString(str); + }; + std::string utf8(const PropNameID& id) override { + Around around{with_}; + return RD::utf8(id); + }; + bool compare(const PropNameID& a, const PropNameID& b) override { + Around around{with_}; + return RD::compare(a, b); + }; + + std::string symbolToString(const Symbol& sym) override { + Around around{with_}; + return RD::symbolToString(sym); + }; + + String createStringFromAscii(const char* str, size_t length) override { + Around around{with_}; + return RD::createStringFromAscii(str, length); + }; + String createStringFromUtf8(const uint8_t* utf8, size_t length) override { + Around around{with_}; + return RD::createStringFromUtf8(utf8, length); + }; + std::string utf8(const String& s) override { + Around around{with_}; + return RD::utf8(s); + } + + Object createObject() override { + Around around{with_}; + return RD::createObject(); + }; + Object createObject(std::shared_ptr ho) override { + Around around{with_}; + return RD::createObject(std::move(ho)); + }; + std::shared_ptr getHostObject(const jsi::Object& o) override { + Around around{with_}; + return RD::getHostObject(o); + }; + HostFunctionType& getHostFunction(const jsi::Function& f) override { + Around around{with_}; + return RD::getHostFunction(f); + }; + + Value getProperty(const Object& o, const PropNameID& name) override { + Around around{with_}; + return RD::getProperty(o, name); + }; + Value getProperty(const Object& o, const String& name) override { + Around around{with_}; + return RD::getProperty(o, name); + }; + bool hasProperty(const Object& o, const PropNameID& name) override { + Around around{with_}; + return RD::hasProperty(o, name); + }; + bool hasProperty(const Object& o, const String& name) override { + Around around{with_}; + return RD::hasProperty(o, name); + }; + void setPropertyValue(Object& o, const PropNameID& name, const Value& value) + override { + Around around{with_}; + RD::setPropertyValue(o, name, value); + }; + void setPropertyValue(Object& o, const String& name, const Value& value) + override { + Around around{with_}; + RD::setPropertyValue(o, name, value); + }; + + bool isArray(const Object& o) const override { + Around around{with_}; + return RD::isArray(o); + }; + bool isArrayBuffer(const Object& o) const override { + Around around{with_}; + return RD::isArrayBuffer(o); + }; + bool isFunction(const Object& o) const override { + Around around{with_}; + return RD::isFunction(o); + }; + bool isHostObject(const jsi::Object& o) const override { + Around around{with_}; + return RD::isHostObject(o); + }; + bool isHostFunction(const jsi::Function& f) const override { + Around around{with_}; + return RD::isHostFunction(f); + }; + Array getPropertyNames(const Object& o) override { + Around around{with_}; + return RD::getPropertyNames(o); + }; + + WeakObject createWeakObject(const Object& o) override { + Around around{with_}; + return RD::createWeakObject(o); + }; + Value lockWeakObject(WeakObject& wo) override { + Around around{with_}; + return RD::lockWeakObject(wo); + }; + + Array createArray(size_t length) override { + Around around{with_}; + return RD::createArray(length); + }; + size_t size(const Array& a) override { + Around around{with_}; + return RD::size(a); + }; + size_t size(const ArrayBuffer& ab) override { + Around around{with_}; + return RD::size(ab); + }; + uint8_t* data(const ArrayBuffer& ab) override { + Around around{with_}; + return RD::data(ab); + }; + Value getValueAtIndex(const Array& a, size_t i) override { + Around around{with_}; + return RD::getValueAtIndex(a, i); + }; + void setValueAtIndexImpl(Array& a, size_t i, const Value& value) override { + Around around{with_}; + RD::setValueAtIndexImpl(a, i, value); + }; + + Function createFunctionFromHostFunction( + const PropNameID& name, + unsigned int paramCount, + HostFunctionType func) override { + Around around{with_}; + return RD::createFunctionFromHostFunction( + name, paramCount, std::move(func)); + }; + Value call( + const Function& f, + const Value& jsThis, + const Value* args, + size_t count) override { + Around around{with_}; + return RD::call(f, jsThis, args, count); + }; + Value callAsConstructor(const Function& f, const Value* args, size_t count) + override { + Around around{with_}; + return RD::callAsConstructor(f, args, count); + }; + + // Private data for managing scopes. + Runtime::ScopeState* pushScope() override { + Around around{with_}; + return RD::pushScope(); + } + void popScope(Runtime::ScopeState* ss) override { + Around around{with_}; + RD::popScope(ss); + } + + bool strictEquals(const Symbol& a, const Symbol& b) const override { + Around around{with_}; + return RD::strictEquals(a, b); + }; + bool strictEquals(const String& a, const String& b) const override { + Around around{with_}; + return RD::strictEquals(a, b); + }; + bool strictEquals(const Object& a, const Object& b) const override { + Around around{with_}; + return RD::strictEquals(a, b); + }; + + bool instanceOf(const Object& o, const Function& f) override { + Around around{with_}; + return RD::instanceOf(o, f); + }; + + private: + // Wrap an RAII type around With& to guarantee after always happens. + struct Around { + Around(With& with) : with_(with) { + detail::BeforeCaller::before(with_); + } + ~Around() { + detail::AfterCaller::after(with_); + } + + With& with_; + }; + + With& with_; +}; + +} // namespace jsi +} // namespace facebook diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h new file mode 100644 index 00000000000..e4764ce2f12 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h @@ -0,0 +1,1321 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#ifndef JSI_EXPORT +#ifdef _MSC_VER +#ifdef CREATE_SHARED_LIBRARY +#define JSI_EXPORT __declspec(dllexport) +#else +#define JSI_EXPORT +#endif // CREATE_SHARED_LIBRARY +#else // _MSC_VER +#define JSI_EXPORT __attribute__((visibility("default"))) +#endif // _MSC_VER +#endif // !defined(JSI_EXPORT) + +class FBJSRuntime; +namespace facebook { +namespace jsi { + +class JSI_EXPORT Buffer { + public: + virtual ~Buffer(); + virtual size_t size() const = 0; + virtual const uint8_t* data() const = 0; +}; + +class JSI_EXPORT StringBuffer : public Buffer { + public: + StringBuffer(std::string s) : s_(std::move(s)) {} + size_t size() const override { + return s_.size(); + } + const uint8_t* data() const override { + return reinterpret_cast(s_.data()); + } + + private: + std::string s_; +}; + +/// PreparedJavaScript is a base class representing JavaScript which is in a +/// form optimized for execution, in a runtime-specific way. Construct one via +/// jsi::Runtime::prepareJavaScript(). +/// ** This is an experimental API that is subject to change. ** +class JSI_EXPORT PreparedJavaScript { + protected: + PreparedJavaScript() = default; + + public: + virtual ~PreparedJavaScript() = 0; +}; + +class Runtime; +class Pointer; +class PropNameID; +class Symbol; +class String; +class Object; +class WeakObject; +class Array; +class ArrayBuffer; +class Function; +class Value; +class Instrumentation; +class Scope; +class JSIException; +class JSError; + +/// A function which has this type can be registered as a function +/// callable from JavaScript using Function::createFromHostFunction(). +/// When the function is called, args will point to the arguments, and +/// count will indicate how many arguments are passed. The function +/// can return a Value to the caller, or throw an exception. If a C++ +/// exception is thrown, a JS Error will be created and thrown into +/// JS; if the C++ exception extends std::exception, the Error's +/// message will be whatever what() returns. Note that it is undefined whether +/// HostFunctions may or may not be called in strict mode; that is `thisVal` +/// can be any value - it will not necessarily be coerced to an object or +/// or set to the global object. +using HostFunctionType = std::function< + Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>; + +/// An object which implements this interface can be registered as an +/// Object with the JS runtime. +class JSI_EXPORT HostObject { + public: + // The C++ object's dtor will be called when the GC finalizes this + // object. (This may be as late as when the Runtime is shut down.) + // You have no control over which thread it is called on. This will + // be called from inside the GC, so it is unsafe to do any VM + // operations which require a Runtime&. Derived classes' dtors + // should also avoid doing anything expensive. Calling the dtor on + // a jsi object is explicitly ok. If you want to do JS operations, + // or any nontrivial work, you should add it to a work queue, and + // manage it externally. + virtual ~HostObject(); + + // When JS wants a property with a given name from the HostObject, + // it will call this method. If it throws an exception, the call + // will throw a JS \c Error object. By default this returns undefined. + // \return the value for the property. + virtual Value get(Runtime&, const PropNameID& name); + + // When JS wants to set a property with a given name on the HostObject, + // it will call this method. If it throws an exception, the call will + // throw a JS \c Error object. By default this throws a type error exception + // mimicking the behavior of a frozen object in strict mode. + virtual void set(Runtime&, const PropNameID& name, const Value& value); + + // When JS wants a list of property names for the HostObject, it will + // call this method. If it throws an exception, the call will throw a + // JS \c Error object. The default implementation returns empty vector. + virtual std::vector getPropertyNames(Runtime& rt); +}; + +/// Represents a JS runtime. Movable, but not copyable. Note that +/// this object may not be thread-aware, but cannot be used safely from +/// multiple threads at once. The application is responsible for +/// ensuring that it is used safely. This could mean using the +/// Runtime from a single thread, using a mutex, doing all work on a +/// serial queue, etc. This restriction applies to the methods of +/// this class, and any method in the API which take a Runtime& as an +/// argument. Destructors (all but ~Scope), operators, or other methods +/// which do not take Runtime& as an argument are safe to call from any +/// thread, but it is still forbidden to make write operations on a single +/// instance of any class from more than one thread. In addition, to +/// make shutdown safe, destruction of objects associated with the Runtime +/// must be destroyed before the Runtime is destroyed, or from the +/// destructor of a managed HostObject or HostFunction. Informally, this +/// means that the main source of unsafe behavior is to hold a jsi object +/// in a non-Runtime-managed object, and not clean it up before the Runtime +/// is shut down. If your lifecycle is such that avoiding this is hard, +/// you will probably need to do use your own locks. +class JSI_EXPORT Runtime { + public: + virtual ~Runtime(); + + /// Evaluates the given JavaScript \c buffer. \c sourceURL is used + /// to annotate the stack trace if there is an exception. The + /// contents may be utf8-encoded JS source code, or binary bytecode + /// whose format is specific to the implementation. If the input + /// format is unknown, or evaluation causes an error, a JSIException + /// will be thrown. + /// Note this function should ONLY be used when there isn't another means + /// through the JSI API. For example, it will be much slower to use this to + /// call a global function than using the JSI APIs to read the function + /// property from the global object and then calling it explicitly. + virtual Value evaluateJavaScript( + const std::shared_ptr& buffer, + const std::string& sourceURL) = 0; + + /// Prepares to evaluate the given JavaScript \c buffer by processing it into + /// a form optimized for execution. This may include pre-parsing, compiling, + /// etc. If the input is invalid (for example, cannot be parsed), a + /// JSIException will be thrown. The resulting object is tied to the + /// particular concrete type of Runtime from which it was created. It may be + /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete + /// type. + /// The PreparedJavaScript object may be passed to multiple VM instances, so + /// they can all share and benefit from the prepared script. + /// As with evaluateJavaScript(), using JavaScript code should be avoided + /// when the JSI API is sufficient. + virtual std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) = 0; + + /// Evaluates a PreparedJavaScript. If evaluation causes an error, a + /// JSIException will be thrown. + /// As with evaluateJavaScript(), using JavaScript code should be avoided + /// when the JSI API is sufficient. + virtual Value evaluatePreparedJavaScript( + const std::shared_ptr& js) = 0; + + /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue. + /// + /// \param maxMicrotasksHint a hint to tell an implementation that it should + /// make a best effort not execute more than the given number. It's default + /// to -1 for infinity (unbounded execution). + /// \return true if the queue is drained or false if there is more work to do. + /// + /// When there were exceptions thrown from the execution of microtasks, + /// implementations shall discard the exceptional jobs. An implementation may + /// \throw a \c JSError object to signal the hosts to handle. In that case, an + /// implementation may or may not suspend the draining. + /// + /// Hosts may call this function again to resume the draining if it was + /// suspended due to either exceptions or the \p maxMicrotasksHint bound. + /// E.g. a host may repetitively invoke this function until the queue is + /// drained to implement the "microtask checkpoint" defined in WHATWG HTML + /// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint. + /// + /// Note that error propagation is only a concern if a host needs to implement + /// `queueMicrotask`, a recent API that allows enqueueing arbitrary functions + /// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are + /// handled internally to VMs and are never propagated to hosts. + /// + /// This API offers some queue management to hosts at its best effort due to + /// different behaviors and limitations imposed by different VMs and APIs. By + /// the time this is written, An implementation may swallow exceptions (JSC), + /// may not pause (V8), and may not support bounded executions. + virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; + + /// \return the global object + virtual Object global() = 0; + + /// \return a short printable description of the instance. It should + /// at least include some human-readable indication of the runtime + /// implementation. This should only be used by logging, debugging, + /// and other developer-facing callers. + virtual std::string description() = 0; + + /// \return whether or not the underlying runtime supports debugging via the + /// Chrome remote debugging protocol. + /// + /// NOTE: the API for determining whether a runtime is debuggable and + /// registering a runtime with the debugger is still in flux, so please don't + /// use this API unless you know what you're doing. + virtual bool isInspectable() = 0; + + /// \return an interface to extract metrics from this \c Runtime. The default + /// implementation of this function returns an \c Instrumentation instance + /// which returns no metrics. + virtual Instrumentation& instrumentation(); + + protected: + friend class Pointer; + friend class PropNameID; + friend class Symbol; + friend class String; + friend class Object; + friend class WeakObject; + friend class Array; + friend class ArrayBuffer; + friend class Function; + friend class Value; + friend class Scope; + friend class JSError; + + // Potential optimization: avoid the cloneFoo() virtual dispatch, + // and instead just fix the number of fields, and copy them, since + // in practice they are trivially copyable. Sufficient use of + // rvalue arguments/methods would also reduce the number of clones. + + struct PointerValue { + virtual void invalidate() = 0; + + protected: + virtual ~PointerValue() = default; + }; + + virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0; + + virtual PropNameID createPropNameIDFromAscii( + const char* str, + size_t length) = 0; + virtual PropNameID createPropNameIDFromUtf8( + const uint8_t* utf8, + size_t length) = 0; + virtual PropNameID createPropNameIDFromString(const String& str) = 0; + virtual std::string utf8(const PropNameID&) = 0; + virtual bool compare(const PropNameID&, const PropNameID&) = 0; + + virtual std::string symbolToString(const Symbol&) = 0; + + virtual String createStringFromAscii(const char* str, size_t length) = 0; + virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; + virtual std::string utf8(const String&) = 0; + + // \return a \c Value created from a utf8-encoded JSON string. The default + // implementation creates a \c String and invokes JSON.parse. + virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length); + + virtual Object createObject() = 0; + virtual Object createObject(std::shared_ptr ho) = 0; + virtual std::shared_ptr getHostObject(const jsi::Object&) = 0; + virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0; + + virtual Value getProperty(const Object&, const PropNameID& name) = 0; + virtual Value getProperty(const Object&, const String& name) = 0; + virtual bool hasProperty(const Object&, const PropNameID& name) = 0; + virtual bool hasProperty(const Object&, const String& name) = 0; + virtual void + setPropertyValue(Object&, const PropNameID& name, const Value& value) = 0; + virtual void + setPropertyValue(Object&, const String& name, const Value& value) = 0; + + virtual bool isArray(const Object&) const = 0; + virtual bool isArrayBuffer(const Object&) const = 0; + virtual bool isFunction(const Object&) const = 0; + virtual bool isHostObject(const jsi::Object&) const = 0; + virtual bool isHostFunction(const jsi::Function&) const = 0; + virtual Array getPropertyNames(const Object&) = 0; + + virtual WeakObject createWeakObject(const Object&) = 0; + virtual Value lockWeakObject(WeakObject&) = 0; + + virtual Array createArray(size_t length) = 0; + virtual size_t size(const Array&) = 0; + virtual size_t size(const ArrayBuffer&) = 0; + virtual uint8_t* data(const ArrayBuffer&) = 0; + virtual Value getValueAtIndex(const Array&, size_t i) = 0; + virtual void setValueAtIndexImpl(Array&, size_t i, const Value& value) = 0; + + virtual Function createFunctionFromHostFunction( + const PropNameID& name, + unsigned int paramCount, + HostFunctionType func) = 0; + virtual Value call( + const Function&, + const Value& jsThis, + const Value* args, + size_t count) = 0; + virtual Value + callAsConstructor(const Function&, const Value* args, size_t count) = 0; + + // Private data for managing scopes. + struct ScopeState; + virtual ScopeState* pushScope(); + virtual void popScope(ScopeState*); + + virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0; + virtual bool strictEquals(const String& a, const String& b) const = 0; + virtual bool strictEquals(const Object& a, const Object& b) const = 0; + + virtual bool instanceOf(const Object& o, const Function& f) = 0; + + // These exist so derived classes can access the private parts of + // Value, Symbol, String, and Object, which are all friends of Runtime. + template + static T make(PointerValue* pv); + static PointerValue* getPointerValue(Pointer& pointer); + static const PointerValue* getPointerValue(const Pointer& pointer); + static const PointerValue* getPointerValue(const Value& value); + + friend class ::FBJSRuntime; + template + friend class RuntimeDecorator; +}; + +// Base class for pointer-storing types. +class JSI_EXPORT Pointer { + protected: + explicit Pointer(Pointer&& other) : ptr_(other.ptr_) { + other.ptr_ = nullptr; + } + + ~Pointer() { + if (ptr_) { + ptr_->invalidate(); + } + } + + Pointer& operator=(Pointer&& other); + + friend class Runtime; + friend class Value; + + explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {} + + typename Runtime::PointerValue* ptr_; +}; + +/// Represents something that can be a JS property key. Movable, not copyable. +class JSI_EXPORT PropNameID : public Pointer { + public: + using Pointer::Pointer; + + PropNameID(Runtime& runtime, const PropNameID& other) + : Pointer(runtime.clonePropNameID(other.ptr_)) {} + + PropNameID(PropNameID&& other) = default; + PropNameID& operator=(PropNameID&& other) = default; + + /// Create a JS property name id from ascii values. The data is + /// copied. + static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) { + return runtime.createPropNameIDFromAscii(str, length); + } + + /// Create a property name id from a nul-terminated C ascii name. The data is + /// copied. + static PropNameID forAscii(Runtime& runtime, const char* str) { + return forAscii(runtime, str, strlen(str)); + } + + /// Create a PropNameID from a C++ string. The string is copied. + static PropNameID forAscii(Runtime& runtime, const std::string& str) { + return forAscii(runtime, str.c_str(), str.size()); + } + + /// Create a PropNameID from utf8 values. The data is copied. + /// Results are undefined if \p utf8 contains invalid code points. + static PropNameID + forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + return runtime.createPropNameIDFromUtf8(utf8, length); + } + + /// Create a PropNameID from utf8-encoded octets stored in a + /// std::string. The string data is transformed and copied. + /// Results are undefined if \p utf8 contains invalid code points. + static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) { + return runtime.createPropNameIDFromUtf8( + reinterpret_cast(utf8.data()), utf8.size()); + } + + /// Create a PropNameID from a JS string. + static PropNameID forString(Runtime& runtime, const jsi::String& str) { + return runtime.createPropNameIDFromString(str); + } + + // Creates a vector of PropNameIDs constructed from given arguments. + template + static std::vector names(Runtime& runtime, Args&&... args); + + // Creates a vector of given PropNameIDs. + template + static std::vector names(PropNameID(&&propertyNames)[N]); + + /// Copies the data in a PropNameID as utf8 into a C++ string. + std::string utf8(Runtime& runtime) const { + return runtime.utf8(*this); + } + + static bool compare( + Runtime& runtime, + const jsi::PropNameID& a, + const jsi::PropNameID& b) { + return runtime.compare(a, b); + } + + friend class Runtime; + friend class Value; +}; + +/// Represents a JS Symbol (es6). Movable, not copyable. +/// TODO T40778724: this is a limited implementation sufficient for +/// the debugger not to crash when a Symbol is a property in an Object +/// or element in an array. Complete support for creating will come +/// later. +class JSI_EXPORT Symbol : public Pointer { + public: + using Pointer::Pointer; + + Symbol(Symbol&& other) = default; + Symbol& operator=(Symbol&& other) = default; + + /// \return whether a and b refer to the same symbol. + static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) { + return runtime.strictEquals(a, b); + } + + /// Converts a Symbol into a C++ string as JS .toString would. The output + /// will look like \c Symbol(description) . + std::string toString(Runtime& runtime) const { + return runtime.symbolToString(*this); + } + + friend class Runtime; + friend class Value; +}; + +/// Represents a JS String. Movable, not copyable. +class JSI_EXPORT String : public Pointer { + public: + using Pointer::Pointer; + + String(String&& other) = default; + String& operator=(String&& other) = default; + + /// Create a JS string from ascii values. The string data is + /// copied. + static String + createFromAscii(Runtime& runtime, const char* str, size_t length) { + return runtime.createStringFromAscii(str, length); + } + + /// Create a JS string from a nul-terminated C ascii string. The + /// string data is copied. + static String createFromAscii(Runtime& runtime, const char* str) { + return createFromAscii(runtime, str, strlen(str)); + } + + /// Create a JS string from a C++ string. The string data is + /// copied. + static String createFromAscii(Runtime& runtime, const std::string& str) { + return createFromAscii(runtime, str.c_str(), str.size()); + } + + /// Create a JS string from utf8-encoded octets. The string data is + /// transformed and copied. Results are undefined if \p utf8 contains invalid + /// code points. + static String + createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + return runtime.createStringFromUtf8(utf8, length); + } + + /// Create a JS string from utf8-encoded octets stored in a + /// std::string. The string data is transformed and copied. Results are + /// undefined if \p utf8 contains invalid code points. + static String createFromUtf8(Runtime& runtime, const std::string& utf8) { + return runtime.createStringFromUtf8( + reinterpret_cast(utf8.data()), utf8.length()); + } + + /// \return whether a and b contain the same characters. + static bool strictEquals(Runtime& runtime, const String& a, const String& b) { + return runtime.strictEquals(a, b); + } + + /// Copies the data in a JS string as utf8 into a C++ string. + std::string utf8(Runtime& runtime) const { + return runtime.utf8(*this); + } + + friend class Runtime; + friend class Value; +}; + +class Array; +class Function; + +/// Represents a JS Object. Movable, not copyable. +class JSI_EXPORT Object : public Pointer { + public: + using Pointer::Pointer; + + Object(Object&& other) = default; + Object& operator=(Object&& other) = default; + + /// Creates a new Object instance, like '{}' in JS. + Object(Runtime& runtime) : Object(runtime.createObject()) {} + + static Object createFromHostObject( + Runtime& runtime, + std::shared_ptr ho) { + return runtime.createObject(ho); + } + + /// \return whether this and \c obj are the same JSObject or not. + static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) { + return runtime.strictEquals(a, b); + } + + /// \return the result of `this instanceOf ctor` in JS. + bool instanceOf(Runtime& rt, const Function& ctor) { + return rt.instanceOf(*this, ctor); + } + + /// \return the property of the object with the given ascii name. + /// If the name isn't a property on the object, returns the + /// undefined value. + Value getProperty(Runtime& runtime, const char* name) const; + + /// \return the property of the object with the String name. + /// If the name isn't a property on the object, returns the + /// undefined value. + Value getProperty(Runtime& runtime, const String& name) const; + + /// \return the property of the object with the given JS PropNameID + /// name. If the name isn't a property on the object, returns the + /// undefined value. + Value getProperty(Runtime& runtime, const PropNameID& name) const; + + /// \return true if and only if the object has a property with the + /// given ascii name. + bool hasProperty(Runtime& runtime, const char* name) const; + + /// \return true if and only if the object has a property with the + /// given String name. + bool hasProperty(Runtime& runtime, const String& name) const; + + /// \return true if and only if the object has a property with the + /// given PropNameID name. + bool hasProperty(Runtime& runtime, const PropNameID& name) const; + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. + template + void setProperty(Runtime& runtime, const char* name, T&& value); + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. + template + void setProperty(Runtime& runtime, const String& name, T&& value); + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. + template + void setProperty(Runtime& runtime, const PropNameID& name, T&& value); + + /// \return true iff JS \c Array.isArray() would return \c true. If + /// so, then \c getArray() will succeed. + bool isArray(Runtime& runtime) const { + return runtime.isArray(*this); + } + + /// \return true iff the Object is an ArrayBuffer. If so, then \c + /// getArrayBuffer() will succeed. + bool isArrayBuffer(Runtime& runtime) const { + return runtime.isArrayBuffer(*this); + } + + /// \return true iff the Object is callable. If so, then \c + /// getFunction will succeed. + bool isFunction(Runtime& runtime) const { + return runtime.isFunction(*this); + } + + /// \return true iff the Object was initialized with \c createFromHostObject + /// and the HostObject passed is of type \c T. If returns \c true then + /// \c getHostObject will succeed. + template + bool isHostObject(Runtime& runtime) const; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will assert. + Array getArray(Runtime& runtime) const&; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will assert. + Array getArray(Runtime& runtime) &&; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will throw + /// JSIException. + Array asArray(Runtime& runtime) const&; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will throw + /// JSIException. + Array asArray(Runtime& runtime) &&; + + /// \return an ArrayBuffer instance which refers to the same underlying + /// object. If \c isArrayBuffer() would return false, this will assert. + ArrayBuffer getArrayBuffer(Runtime& runtime) const&; + + /// \return an ArrayBuffer instance which refers to the same underlying + /// object. If \c isArrayBuffer() would return false, this will assert. + ArrayBuffer getArrayBuffer(Runtime& runtime) &&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will assert. + Function getFunction(Runtime& runtime) const&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will assert. + Function getFunction(Runtime& runtime) &&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will throw + /// JSIException. + Function asFunction(Runtime& runtime) const&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will throw + /// JSIException. + Function asFunction(Runtime& runtime) &&; + + /// \return a shared_ptr which refers to the same underlying + /// \c HostObject that was used to create this object. If \c isHostObject + /// is false, this will assert. Note that this does a type check and will + /// assert if the underlying HostObject isn't of type \c T + template + std::shared_ptr getHostObject(Runtime& runtime) const; + + /// \return a shared_ptr which refers to the same underlying + /// \c HostObject that was used to create this object. If \c isHostObject + /// is false, this will throw. + template + std::shared_ptr asHostObject(Runtime& runtime) const; + + /// \return same as \c getProperty(name).asObject(), except with + /// a better exception message. + Object getPropertyAsObject(Runtime& runtime, const char* name) const; + + /// \return similar to \c + /// getProperty(name).getObject().getFunction(), except it will + /// throw JSIException instead of asserting if the property is + /// not an object, or the object is not callable. + Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + + /// \return an Array consisting of all enumerable property names in + /// the object and its prototype chain. All values in the return + /// will be isString(). (This is probably not optimal, but it + /// works. I only need it in one place.) + Array getPropertyNames(Runtime& runtime) const; + + protected: + void + setPropertyValue(Runtime& runtime, const String& name, const Value& value) { + return runtime.setPropertyValue(*this, name, value); + } + + void setPropertyValue( + Runtime& runtime, + const PropNameID& name, + const Value& value) { + return runtime.setPropertyValue(*this, name, value); + } + + friend class Runtime; + friend class Value; +}; + +/// Represents a weak reference to a JS Object. If the only reference +/// to an Object are these, the object is eligible for GC. Method +/// names are inspired by C++ weak_ptr. Movable, not copyable. +class JSI_EXPORT WeakObject : public Pointer { + public: + using Pointer::Pointer; + + WeakObject(WeakObject&& other) = default; + WeakObject& operator=(WeakObject&& other) = default; + + /// Create a WeakObject from an Object. + WeakObject(Runtime& runtime, const Object& o) + : WeakObject(runtime.createWeakObject(o)) {} + + /// \return a Value representing the underlying Object if it is still valid; + /// otherwise returns \c undefined. Note that this method has nothing to do + /// with threads or concurrency. The name is based on std::weak_ptr::lock() + /// which serves a similar purpose. + Value lock(Runtime& runtime); + + friend class Runtime; +}; + +/// Represents a JS Object which can be efficiently used as an array +/// with integral indices. +class JSI_EXPORT Array : public Object { + public: + Array(Array&&) = default; + /// Creates a new Array instance, with \c length undefined elements. + Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {} + + Array& operator=(Array&&) = default; + + /// \return the size of the Array, according to its length property. + /// (C++ naming convention) + size_t size(Runtime& runtime) const { + return runtime.size(*this); + } + + /// \return the size of the Array, according to its length property. + /// (JS naming convention) + size_t length(Runtime& runtime) const { + return size(runtime); + } + + /// \return the property of the array at index \c i. If there is no + /// such property, returns the undefined value. If \c i is out of + /// range [ 0..\c length ] throws a JSIException. + Value getValueAtIndex(Runtime& runtime, size_t i) const; + + /// Sets the property of the array at index \c i. The argument + /// value behaves as with Object::setProperty(). If \c i is out of + /// range [ 0..\c length ] throws a JSIException. + template + void setValueAtIndex(Runtime& runtime, size_t i, T&& value); + + /// There is no current API for changing the size of an array once + /// created. We'll probably need that eventually. + + /// Creates a new Array instance from provided values + template + static Array createWithElements(Runtime&, Args&&... args); + + /// Creates a new Array instance from initializer list. + static Array createWithElements( + Runtime& runtime, + std::initializer_list elements); + + private: + friend class Object; + friend class Value; + + void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) { + return runtime.setValueAtIndexImpl(*this, i, value); + } + + Array(Runtime::PointerValue* value) : Object(value) {} +}; + +/// Represents a JSArrayBuffer +class JSI_EXPORT ArrayBuffer : public Object { + public: + ArrayBuffer(ArrayBuffer&&) = default; + ArrayBuffer& operator=(ArrayBuffer&&) = default; + + /// \return the size of the ArrayBuffer, according to its byteLength property. + /// (C++ naming convention) + size_t size(Runtime& runtime) const { + return runtime.size(*this); + } + + size_t length(Runtime& runtime) const { + return runtime.size(*this); + } + + uint8_t* data(Runtime& runtime) { + return runtime.data(*this); + } + + private: + friend class Object; + friend class Value; + + ArrayBuffer(Runtime::PointerValue* value) : Object(value) {} +}; + +/// Represents a JS Object which is guaranteed to be Callable. +class JSI_EXPORT Function : public Object { + public: + Function(Function&&) = default; + Function& operator=(Function&&) = default; + + /// Create a function which, when invoked, calls C++ code. If the + /// function throws an exception, a JS Error will be created and + /// thrown. + /// \param name the name property for the function. + /// \param paramCount the length property for the function, which + /// may not be the number of arguments the function is passed. + static Function createFromHostFunction( + Runtime& runtime, + const jsi::PropNameID& name, + unsigned int paramCount, + jsi::HostFunctionType func); + + /// Calls the function with \c count \c args. The \c this value of the JS + /// function will not be set by the C++ caller, similar to calling + /// Function.prototype.apply(undefined, args) in JS. + /// \b Note: as with Function.prototype.apply, \c this may not always be + /// \c undefined in the function itself. If the function is non-strict, + /// \c this will be set to the global object. + Value call(Runtime& runtime, const Value* args, size_t count) const; + + /// Calls the function with a \c std::initializer_list of Value + /// arguments. The \c this value of the JS function will not be set by the + /// C++ caller, similar to calling Function.prototype.apply(undefined, args) + /// in JS. + /// \b Note: as with Function.prototype.apply, \c this may not always be + /// \c undefined in the function itself. If the function is non-strict, + /// \c this will be set to the global object. + Value call(Runtime& runtime, std::initializer_list args) const; + + /// Calls the function with any number of arguments similarly to + /// Object::setProperty(). The \c this value of the JS function will not be + /// set by the C++ caller, similar to calling + /// Function.prototype.call(undefined, ...args) in JS. + /// \b Note: as with Function.prototype.call, \c this may not always be + /// \c undefined in the function itself. If the function is non-strict, + /// \c this will be set to the global object. + template + Value call(Runtime& runtime, Args&&... args) const; + + /// Calls the function with \c count \c args and \c jsThis value passed + /// as the \c this value. + Value callWithThis( + Runtime& Runtime, + const Object& jsThis, + const Value* args, + size_t count) const; + + /// Calls the function with a \c std::initializer_list of Value + /// arguments and \c jsThis passed as the \c this value. + Value callWithThis( + Runtime& runtime, + const Object& jsThis, + std::initializer_list args) const; + + /// Calls the function with any number of arguments similarly to + /// Object::setProperty(), and with \c jsThis passed as the \c this value. + template + Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args) + const; + + /// Calls the function as a constructor with \c count \c args. Equivalent + /// to calling `new Func` where `Func` is the js function reqresented by + /// this. + Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) + const; + + /// Same as above `callAsConstructor`, except use an initializer_list to + /// supply the arguments. + Value callAsConstructor(Runtime& runtime, std::initializer_list args) + const; + + /// Same as above `callAsConstructor`, but automatically converts/wraps + /// any argument with a jsi Value. + template + Value callAsConstructor(Runtime& runtime, Args&&... args) const; + + /// Returns whether this was created with Function::createFromHostFunction. + /// If true then you can use getHostFunction to get the underlying + /// HostFunctionType. + bool isHostFunction(Runtime& runtime) const { + return runtime.isHostFunction(*this); + } + + /// Returns the underlying HostFunctionType iff isHostFunction returns true + /// and asserts otherwise. You can use this to use std::function<>::target + /// to get the object that was passed to create the HostFunctionType. + /// + /// Note: The reference returned is borrowed from the JS object underlying + /// \c this, and thus only lasts as long as the object underlying + /// \c this does. + HostFunctionType& getHostFunction(Runtime& runtime) const { + assert(isHostFunction(runtime)); + return runtime.getHostFunction(*this); + } + + private: + friend class Object; + friend class Value; + + Function(Runtime::PointerValue* value) : Object(value) {} +}; + +/// Represents any JS Value (undefined, null, boolean, number, symbol, +/// string, or object). Movable, or explicitly copyable (has no copy +/// ctor). +class JSI_EXPORT Value { + public: + /// Default ctor creates an \c undefined JS value. + Value() : Value(UndefinedKind) {} + + /// Creates a \c null JS value. + /* implicit */ Value(std::nullptr_t) : kind_(NullKind) {} + + /// Creates a boolean JS value. + /* implicit */ Value(bool b) : Value(BooleanKind) { + data_.boolean = b; + } + + /// Creates a number JS value. + /* implicit */ Value(double d) : Value(NumberKind) { + data_.number = d; + } + + /// Creates a number JS value. + /* implicit */ Value(int i) : Value(NumberKind) { + data_.number = i; + } + + /// Moves a Symbol, String, or Object rvalue into a new JS value. + template + /* implicit */ Value(T&& other) : Value(kindOf(other)) { + static_assert( + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value, + "Value cannot be implicitly move-constructed from this type"); + new (&data_.pointer) T(std::move(other)); + } + + /// Value("foo") will treat foo as a bool. This makes doing that a + /// compile error. + template + Value(const char*) { + static_assert( + !std::is_same::value, + "Value cannot be constructed directly from const char*"); + } + + Value(Value&& value); + + /// Copies a Symbol lvalue into a new JS value. + Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) { + new (&data_.pointer) String(runtime.cloneSymbol(sym.ptr_)); + } + + /// Copies a String lvalue into a new JS value. + Value(Runtime& runtime, const String& str) : Value(StringKind) { + new (&data_.pointer) String(runtime.cloneString(str.ptr_)); + } + + /// Copies a Object lvalue into a new JS value. + Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) { + new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_)); + } + + /// Creates a JS value from another Value lvalue. + Value(Runtime& runtime, const Value& value); + + /// Value(rt, "foo") will treat foo as a bool. This makes doing + /// that a compile error. + template + Value(Runtime&, const char*) { + static_assert( + !std::is_same::value, + "Value cannot be constructed directly from const char*"); + } + + ~Value(); + // \return the undefined \c Value. + static Value undefined() { + return Value(); + } + + // \return the null \c Value. + static Value null() { + return Value(nullptr); + } + + // \return a \c Value created from a utf8-encoded JSON string. + static Value + createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length) { + return runtime.createValueFromJsonUtf8(json, length); + } + + /// \return according to the Strict Equality Comparison algorithm, see: + /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison + static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); + + Value& operator=(Value&& other) { + this->~Value(); + new (this) Value(std::move(other)); + return *this; + } + + bool isUndefined() const { + return kind_ == UndefinedKind; + } + + bool isNull() const { + return kind_ == NullKind; + } + + bool isBool() const { + return kind_ == BooleanKind; + } + + bool isNumber() const { + return kind_ == NumberKind; + } + + bool isString() const { + return kind_ == StringKind; + } + + bool isSymbol() const { + return kind_ == SymbolKind; + } + + bool isObject() const { + return kind_ == ObjectKind; + } + + /// \return the boolean value, or asserts if not a boolean. + bool getBool() const { + assert(isBool()); + return data_.boolean; + } + + /// \return the number value, or asserts if not a number. + double getNumber() const { + assert(isNumber()); + return data_.number; + } + + /// \return the number value, or throws JSIException if not a + /// number. + double asNumber() const; + + /// \return the Symbol value, or asserts if not a symbol. + Symbol getSymbol(Runtime& runtime) const& { + assert(isSymbol()); + return Symbol(runtime.cloneSymbol(data_.pointer.ptr_)); + } + + /// \return the Symbol value, or asserts if not a symbol. + /// Can be used on rvalue references to avoid cloning more symbols. + Symbol getSymbol(Runtime&) && { + assert(isSymbol()); + auto ptr = data_.pointer.ptr_; + data_.pointer.ptr_ = nullptr; + return static_cast(ptr); + } + + /// \return the Symbol value, or throws JSIException if not a + /// symbol + Symbol asSymbol(Runtime& runtime) const&; + Symbol asSymbol(Runtime& runtime) &&; + + /// \return the String value, or asserts if not a string. + String getString(Runtime& runtime) const& { + assert(isString()); + return String(runtime.cloneString(data_.pointer.ptr_)); + } + + /// \return the String value, or asserts if not a string. + /// Can be used on rvalue references to avoid cloning more strings. + String getString(Runtime&) && { + assert(isString()); + auto ptr = data_.pointer.ptr_; + data_.pointer.ptr_ = nullptr; + return static_cast(ptr); + } + + /// \return the String value, or throws JSIException if not a + /// string. + String asString(Runtime& runtime) const&; + String asString(Runtime& runtime) &&; + + /// \return the Object value, or asserts if not an object. + Object getObject(Runtime& runtime) const& { + assert(isObject()); + return Object(runtime.cloneObject(data_.pointer.ptr_)); + } + + /// \return the Object value, or asserts if not an object. + /// Can be used on rvalue references to avoid cloning more objects. + Object getObject(Runtime&) && { + assert(isObject()); + auto ptr = data_.pointer.ptr_; + data_.pointer.ptr_ = nullptr; + return static_cast(ptr); + } + + /// \return the Object value, or throws JSIException if not an + /// object. + Object asObject(Runtime& runtime) const&; + Object asObject(Runtime& runtime) &&; + + // \return a String like JS .toString() would do. + String toString(Runtime& runtime) const; + + private: + friend class Runtime; + + enum ValueKind { + UndefinedKind, + NullKind, + BooleanKind, + NumberKind, + SymbolKind, + StringKind, + ObjectKind, + PointerKind = SymbolKind, + }; + + union Data { + // Value's ctor and dtor will manage the lifecycle of the contained Data. + Data() { + static_assert( + sizeof(Data) == sizeof(uint64_t), + "Value data should fit in a 64-bit register"); + } + ~Data() {} + + // scalars + bool boolean; + double number; + // pointers + Pointer pointer; // Symbol, String, Object, Array, Function + }; + + Value(ValueKind kind) : kind_(kind) {} + + constexpr static ValueKind kindOf(const Symbol&) { + return SymbolKind; + } + constexpr static ValueKind kindOf(const String&) { + return StringKind; + } + constexpr static ValueKind kindOf(const Object&) { + return ObjectKind; + } + + ValueKind kind_; + Data data_; + + // In the future: Value becomes NaN-boxed. See T40538354. +}; + +/// Not movable and not copyable RAII marker advising the underlying +/// JavaScript VM to track resources allocated since creation until +/// destruction so that they can be recycled eagerly when the Scope +/// goes out of scope instead of floating in the air until the next +/// garbage collection or any other delayed release occurs. +/// +/// This API should be treated only as advice, implementations can +/// choose to ignore the fact that Scopes are created or destroyed. +/// +/// This class is an exception to the rule allowing destructors to be +/// called without proper synchronization (see Runtime documentation). +/// The whole point of this class is to enable all sorts of clean ups +/// when the destructor is called and this proper synchronization is +/// required at that time. +/// +/// Instances of this class are intended to be created as automatic stack +/// variables in which case destructor calls don't require any additional +/// locking, provided that the lock (if any) is managed with RAII helpers. +class JSI_EXPORT Scope { + public: + explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {} + ~Scope() { + rt_.popScope(prv_); + }; + + Scope(const Scope&) = delete; + Scope(Scope&&) = delete; + + Scope& operator=(const Scope&) = delete; + Scope& operator=(Scope&&) = delete; + + template + static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) { + Scope s(rt); + return f(); + } + + private: + Runtime& rt_; + Runtime::ScopeState* prv_; +}; + +/// Base class for jsi exceptions +class JSI_EXPORT JSIException : public std::exception { + protected: + JSIException(){}; + JSIException(std::string what) : what_(std::move(what)){}; + + public: + virtual const char* what() const noexcept override { + return what_.c_str(); + } + + virtual ~JSIException(); + + protected: + std::string what_; +}; + +/// This exception will be thrown by API functions on errors not related to +/// JavaScript execution. +class JSI_EXPORT JSINativeException : public JSIException { + public: + JSINativeException(std::string what) : JSIException(std::move(what)) {} + + virtual ~JSINativeException(); +}; + +/// This exception will be thrown by API functions whenever a JS +/// operation causes an exception as described by the spec, or as +/// otherwise described. +class JSI_EXPORT JSError : public JSIException { + public: + /// Creates a JSError referring to provided \c value + JSError(Runtime& r, Value&& value); + + /// Creates a JSError referring to new \c Error instance capturing current + /// JavaScript stack. The error message property is set to given \c message. + JSError(Runtime& rt, std::string message); + + /// Creates a JSError referring to new \c Error instance capturing current + /// JavaScript stack. The error message property is set to given \c message. + JSError(Runtime& rt, const char* message) + : JSError(rt, std::string(message)){}; + + /// Creates a JSError referring to a JavaScript Object having message and + /// stack properties set to provided values. + JSError(Runtime& rt, std::string message, std::string stack); + + /// Creates a JSError referring to provided value and what string + /// set to provided message. This argument order is a bit weird, + /// but necessary to avoid ambiguity with the above. + JSError(std::string what, Runtime& rt, Value&& value); + + virtual ~JSError(); + + const std::string& getStack() const { + return stack_; + } + + const std::string& getMessage() const { + return message_; + } + + const jsi::Value& value() const { + assert(value_); + return *value_; + } + + private: + // This initializes the value_ member and does some other + // validation, so it must be called by every branch through the + // constructors. + void setValue(Runtime& rt, Value&& value); + + // This needs to be on the heap, because throw requires the object + // be copyable, and Value is not. + std::shared_ptr value_; + std::string message_; + std::string stack_; +}; + +} // namespace jsi +} // namespace facebook + +#include \ No newline at end of file diff --git a/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h b/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h index 4c0e1061226..41484b1f421 100644 --- a/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h +++ b/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h @@ -24,7 +24,6 @@ using RCTProgressViewComponentDescriptor = ConcreteComponentDescriptor; using PullToRefreshViewComponentDescriptor = ConcreteComponentDescriptor; using AndroidHorizontalScrollContentViewComponentDescriptor = ConcreteComponentDescriptor; -using RCTSegmentedControlComponentDescriptor = ConcreteComponentDescriptor; using SwitchComponentDescriptor = ConcreteComponentDescriptor; using UnimplementedNativeViewComponentDescriptor = ConcreteComponentDescriptor; diff --git a/vnext/codegen/react/components/rnwcore/EventEmitters.cpp b/vnext/codegen/react/components/rnwcore/EventEmitters.cpp index b3fdac3bc86..8f0cce7e57d 100644 --- a/vnext/codegen/react/components/rnwcore/EventEmitters.cpp +++ b/vnext/codegen/react/components/rnwcore/EventEmitters.cpp @@ -96,14 +96,6 @@ void PullToRefreshViewEventEmitter::onRefresh(OnRefresh event) const { } -void RCTSegmentedControlEventEmitter::onChange(OnChange event) const { - dispatchEvent("change", [event=std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "value", event.value); -payload.setProperty(runtime, "selectedSegmentIndex", event.selectedSegmentIndex); - return payload; - }); -} void SliderEventEmitter::onValueChange(OnValueChange event) const { dispatchEvent("valueChange", [event=std::move(event)](jsi::Runtime &runtime) { auto payload = jsi::Object(runtime); diff --git a/vnext/codegen/react/components/rnwcore/EventEmitters.h b/vnext/codegen/react/components/rnwcore/EventEmitters.h index 5ffd7dc5bc5..563ae8e3df6 100644 --- a/vnext/codegen/react/components/rnwcore/EventEmitters.h +++ b/vnext/codegen/react/components/rnwcore/EventEmitters.h @@ -159,17 +159,6 @@ class AndroidHorizontalScrollContentViewEventEmitter : public ViewEventEmitter { -}; -class RCTSegmentedControlEventEmitter : public ViewEventEmitter { - public: - using ViewEventEmitter::ViewEventEmitter; - - struct OnChange { - int value; - int selectedSegmentIndex; - }; - - void onChange(OnChange value) const; }; class SliderEventEmitter : public ViewEventEmitter { public: diff --git a/vnext/codegen/react/components/rnwcore/Props.cpp b/vnext/codegen/react/components/rnwcore/Props.cpp index f19c4b06e58..ad62084fae0 100644 --- a/vnext/codegen/react/components/rnwcore/Props.cpp +++ b/vnext/codegen/react/components/rnwcore/Props.cpp @@ -137,19 +137,6 @@ AndroidHorizontalScrollContentViewProps::AndroidHorizontalScrollContentViewProps removeClippedSubviews(convertRawProp(context, rawProps, "removeClippedSubviews", sourceProps.removeClippedSubviews, {false})) {} -RCTSegmentedControlProps::RCTSegmentedControlProps( - const PropsParserContext &context, - const RCTSegmentedControlProps &sourceProps, - const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), - - values(convertRawProp(context, rawProps, "values", sourceProps.values, {})), - selectedIndex(convertRawProp(context, rawProps, "selectedIndex", sourceProps.selectedIndex, {0})), - enabled(convertRawProp(context, rawProps, "enabled", sourceProps.enabled, {true})), - tintColor(convertRawProp(context, rawProps, "tintColor", sourceProps.tintColor, {})), - textColor(convertRawProp(context, rawProps, "textColor", sourceProps.textColor, {})), - backgroundColor(convertRawProp(context, rawProps, "backgroundColor", sourceProps.backgroundColor, {})), - momentary(convertRawProp(context, rawProps, "momentary", sourceProps.momentary, {false})) - {} SliderProps::SliderProps( const PropsParserContext &context, const SliderProps &sourceProps, diff --git a/vnext/codegen/react/components/rnwcore/Props.h b/vnext/codegen/react/components/rnwcore/Props.h index 719027b17da..e7962bb1ab3 100644 --- a/vnext/codegen/react/components/rnwcore/Props.h +++ b/vnext/codegen/react/components/rnwcore/Props.h @@ -478,22 +478,6 @@ class AndroidHorizontalScrollContentViewProps final : public ViewProps { bool removeClippedSubviews{false}; }; -class RCTSegmentedControlProps final : public ViewProps { - public: - RCTSegmentedControlProps() = default; - RCTSegmentedControlProps(const PropsParserContext& context, const RCTSegmentedControlProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - std::vector values{}; - int selectedIndex{0}; - bool enabled{true}; - SharedColor tintColor{}; - SharedColor textColor{}; - SharedColor backgroundColor{}; - bool momentary{false}; -}; - class SliderProps final : public ViewProps { public: SliderProps() = default; diff --git a/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp b/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp index f20ef3efa65..4820fc7a2fd 100644 --- a/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp +++ b/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp @@ -21,7 +21,6 @@ extern const char RCTProgressViewComponentName[] = "RCTProgressView"; extern const char AndroidSwipeRefreshLayoutComponentName[] = "AndroidSwipeRefreshLayout"; extern const char PullToRefreshViewComponentName[] = "PullToRefreshView"; extern const char AndroidHorizontalScrollContentViewComponentName[] = "AndroidHorizontalScrollContentView"; -extern const char RCTSegmentedControlComponentName[] = "RCTSegmentedControl"; extern const char SwitchComponentName[] = "Switch"; extern const char UnimplementedNativeViewComponentName[] = "UnimplementedNativeView"; diff --git a/vnext/codegen/react/components/rnwcore/ShadowNodes.h b/vnext/codegen/react/components/rnwcore/ShadowNodes.h index 139db359f1f..c27eb0951ed 100644 --- a/vnext/codegen/react/components/rnwcore/ShadowNodes.h +++ b/vnext/codegen/react/components/rnwcore/ShadowNodes.h @@ -93,16 +93,6 @@ using AndroidHorizontalScrollContentViewShadowNode = ConcreteViewShadowNode< AndroidHorizontalScrollContentViewComponentName, AndroidHorizontalScrollContentViewProps>; -extern const char RCTSegmentedControlComponentName[]; - -/* - * `ShadowNode` for component. - */ -using RCTSegmentedControlShadowNode = ConcreteViewShadowNode< - RCTSegmentedControlComponentName, - RCTSegmentedControlProps, -RCTSegmentedControlEventEmitter>; - extern const char SwitchComponentName[]; /* diff --git a/vnext/overrides.json b/vnext/overrides.json index 952ea4f74fd..a926e2acbc7 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -8,13 +8,13 @@ "**/__snapshots__/**", "src/rntypes/**" ], - "baseVersion": "0.0.0-20220213-2008-7cece3423", + "baseVersion": "0.0.0-20220227-2009-189c2c895", "overrides": [ { "type": "derived", "file": ".flowconfig", "baseFile": ".flowconfig", - "baseHash": "e0fd826d1c731209ca1f4322e950df83877ea95a" + "baseHash": "62cbbd198b5f952592fcffae5e5f88a387a2f190" }, { "type": "patch", @@ -37,6 +37,27 @@ "baseHash": "010970b09aebda9348dfed9dd7b8fefb365bcc9b", "issue": 7821 }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp", + "baseFile": "ReactCommon/jsi/JSCRuntime.cpp", + "baseHash": "da73ed017a05d0fdc4302bd0794a343dc5ac935a", + "issue": 111 + }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h", + "baseFile": "ReactCommon/jsi/jsi/decorator.h", + "baseHash": "c93e4d10173716218503599610eb48f40bd2a47c", + "issue": 111 + }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h", + "baseFile": "ReactCommon/jsi/jsi/jsi.h", + "baseHash": "6ef3f675e8af4060ebd43ba75136d58932b02eb4", + "issue": 111 + }, { "type": "patch", "file": "ReactCommon/Yoga.cpp", @@ -55,7 +76,7 @@ "type": "patch", "file": "src/babel-plugin-codegen/GenerateViewConfigJs.js", "baseFile": "packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js", - "baseHash": "0795a999921d9e51c8f01a75befc4002bd6226e0", + "baseHash": "900dd159210db7a612ba30e1ebba3799f428425c", "issue": 5430 }, { @@ -76,7 +97,7 @@ "type": "derived", "file": "src/index.windows.js", "baseFile": "index.js", - "baseHash": "a33f356abc2db3767c8fee7e4d6bda60384787a1" + "baseHash": "2a0bd511c691be2ac3da45a3c77250aaf55414e1" }, { "type": "platform", @@ -226,17 +247,11 @@ "baseFile": "Libraries/Components/SafeAreaView/SafeAreaView.js", "baseHash": "d13d67c91a9096989776ba7d7b0f8537c46b87a8" }, - { - "type": "copy", - "file": "src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.windows.js", - "baseFile": "Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.android.js", - "baseHash": "a7b7561ee80510fb3a45bad5ad18bc9076152144" - }, { "type": "patch", "file": "src/Libraries/Components/TextInput/TextInput.windows.js", "baseFile": "Libraries/Components/TextInput/TextInput.js", - "baseHash": "c447994d5ac59824f298cf4aec899c174b78e3e2" + "baseHash": "567ebad070a2fb9020ef177ee30defba9f0c54ec" }, { "type": "patch", @@ -421,13 +436,13 @@ "type": "derived", "file": "src/Libraries/Text/Text.windows.js", "baseFile": "Libraries/Text/Text.js", - "baseHash": "7135a3805562187fe999ce7ffdbbb418edd1025f" + "baseHash": "2585a673f2e11cb101f113c67eb2c379be3850c9" }, { "type": "patch", "file": "src/Libraries/Types/CoreEventTypes.windows.js", "baseFile": "Libraries/Types/CoreEventTypes.js", - "baseHash": "b7ed94c49c38a6b3579349e6940bb663328c1f4e" + "baseHash": "12d577ba5b522593fb49d4621d5ad4bba0d37119" }, { "type": "copy", diff --git a/vnext/package.json b/vnext/package.json index 663eff74133..4f3a3adf5c3 100644 --- a/vnext/package.json +++ b/vnext/package.json @@ -23,7 +23,7 @@ "dependencies": { "@babel/runtime": "^7.0.0", "@jest/create-cache-key-function": "^27.0.1", - "@react-native-community/cli": "^7.0.1", + "@react-native-community/cli": "^7.0.3", "@react-native-community/cli-platform-android": "^7.0.1", "@react-native-community/cli-platform-ios": "^7.0.1", "@react-native-windows/cli": "0.0.0-canary.118", @@ -38,15 +38,15 @@ "hermes-engine": "~0.11.0", "invariant": "^2.2.4", "jsc-android": "^250230.2.1", - "metro-react-native-babel-transformer": "0.67.0", - "metro-runtime": "0.67.0", - "metro-source-map": "0.67.0", + "metro-react-native-babel-transformer": "0.68.0", + "metro-runtime": "0.68.0", + "metro-source-map": "0.68.0", "nullthrows": "^1.1.1", "pretty-format": "^26.5.2", "promise": "^8.0.3", "react-devtools-core": "^4.23.0", "react-native-codegen": "^0.0.13", - "react-native-gradle-plugin": "^0.0.4", + "react-native-gradle-plugin": "^0.0.5", "react-refresh": "^0.4.0", "react-shallow-renderer": "16.14.1", "regenerator-runtime": "^0.13.2", @@ -74,7 +74,7 @@ "metro-config": "^0.67.0", "prettier": "^2.4.1", "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423", + "react-native": "0.0.0-20220227-2009-189c2c895", "react-native-platform-override": "^1.6.6", "react-refresh": "^0.4.0", "react-shallow-renderer": "16.14.1", @@ -82,7 +82,7 @@ }, "peerDependencies": { "react": "17.0.2", - "react-native": "0.0.0-20220213-2008-7cece3423" + "react-native": "0.0.0-20220227-2009-189c2c895" }, "beachball": { "defaultNpmTag": "canary", diff --git a/vnext/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.windows.js b/vnext/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.windows.js deleted file mode 100644 index afc2f621e78..00000000000 --- a/vnext/src/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.windows.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; -import * as React from 'react'; -import StyleSheet from '../../StyleSheet/StyleSheet'; -import Text from '../../Text/Text'; -import View from '../View/View'; - -class DummySegmentedControlIOS extends React.Component { - render() { - return ( - - - SegmentedControlIOS is not supported on this platform! - - - ); - } -} - -const styles = StyleSheet.create({ - dummy: { - width: 120, - height: 50, - backgroundColor: '#ffbcbc', - borderWidth: 1, - borderColor: 'red', - alignItems: 'center', - justifyContent: 'center', - }, - text: { - color: '#333333', - margin: 5, - fontSize: 10, - }, -}); - -module.exports = DummySegmentedControlIOS; diff --git a/vnext/src/Libraries/Components/TextInput/TextInput.windows.js b/vnext/src/Libraries/Components/TextInput/TextInput.windows.js index b2bd9bf0868..960fb64778b 100644 --- a/vnext/src/Libraries/Components/TextInput/TextInput.windows.js +++ b/vnext/src/Libraries/Components/TextInput/TextInput.windows.js @@ -1464,7 +1464,7 @@ ExportedForwardRef.State = { blurTextInput: TextInputState.blurTextInput, }; -type TextInputComponentStatics = $ReadOnly<{| +export type TextInputComponentStatics = $ReadOnly<{| State: $ReadOnly<{| currentlyFocusedInput: typeof TextInputState.currentlyFocusedInput, currentlyFocusedField: typeof TextInputState.currentlyFocusedField, diff --git a/vnext/src/Libraries/Text/Text.windows.js b/vnext/src/Libraries/Text/Text.windows.js index 21c9c7a67c1..93a619c77bc 100644 --- a/vnext/src/Libraries/Text/Text.windows.js +++ b/vnext/src/Libraries/Text/Text.windows.js @@ -52,11 +52,20 @@ const Text: React.AbstractComponent< const [isHighlighted, setHighlighted] = useState(false); + const _disabled = + restProps.disabled != null + ? restProps.disabled + : props.accessibilityState?.disabled; + const _accessibilityState = + _disabled !== props.accessibilityState?.disabled + ? {...props.accessibilityState, disabled: _disabled} + : props.accessibilityState; + const isPressable = (onPress != null || onLongPress != null || onStartShouldSetResponder != null) && - restProps.disabled !== true; + _disabled !== true; const initialized = useLazyInitialization(isPressable); const config = useMemo( @@ -167,6 +176,11 @@ const Text: React.AbstractComponent< = $ReadOnly<{| +export type SyntheticEvent<+T> = $ReadOnly<{| bubbles: ?boolean, cancelable: ?boolean, currentTarget: number | React.ElementRef>, diff --git a/vnext/src/babel-plugin-codegen/GenerateViewConfigJs.js b/vnext/src/babel-plugin-codegen/GenerateViewConfigJs.js index 49b3ef28491..47830bcbac6 100644 --- a/vnext/src/babel-plugin-codegen/GenerateViewConfigJs.js +++ b/vnext/src/babel-plugin-codegen/GenerateViewConfigJs.js @@ -138,7 +138,7 @@ const DeprecatedComponentNameCheckTemplate = ({ paperComponentNameDeprecated: string, }) => ` -if (global.__nativeComponentRegistry__hasComponent) { +if (global.RN$Bridgeless) { if (UIManager.hasViewManagerConfig('${componentName}')) { nativeComponentName = '${componentName}'; } else if (UIManager.hasViewManagerConfig('${paperComponentNameDeprecated}')) { diff --git a/vnext/src/index.windows.js b/vnext/src/index.windows.js index 81422fd2e27..1675ac37eef 100644 --- a/vnext/src/index.windows.js +++ b/vnext/src/index.windows.js @@ -30,7 +30,6 @@ import typeof RefreshControl from './Libraries/Components/RefreshControl/Refresh import typeof SafeAreaView from './Libraries/Components/SafeAreaView/SafeAreaView'; import typeof ScrollView from './Libraries/Components/ScrollView/ScrollView'; import typeof SectionList from './Libraries/Lists/SectionList'; -import typeof SegmentedControlIOS from './Libraries/Components/SegmentedControlIOS/SegmentedControlIOS'; import typeof Slider from './Libraries/Components/Slider/Slider'; import typeof StatusBar from './Libraries/Components/StatusBar/StatusBar'; import typeof Switch from './Libraries/Components/Switch/Switch'; @@ -196,16 +195,6 @@ module.exports = { get SectionList(): SectionList { return require('./Libraries/Lists/SectionList').default; }, - // $FlowFixMe[value-as-type] - get SegmentedControlIOS(): SegmentedControlIOS { - warnOnce( - 'segmented-control-ios-moved', - 'SegmentedControlIOS has been extracted from react-native core and will be removed in a future release. ' + - "It can now be installed and imported from '@react-native-segmented-control/segmented-control' instead of 'react-native'. " + - 'See https://github.com/react-native-segmented-control/segmented-control', - ); - return require('./Libraries/Components/SegmentedControlIOS/SegmentedControlIOS'); - }, get Slider(): Slider { warnOnce( 'slider-moved', @@ -705,8 +694,8 @@ if (__DEV__) { invariant( false, 'ViewPagerAndroid has been removed from React Native. ' + - "It can now be installed and imported from '@react-native-community/viewpager' instead of 'react-native'. " + - 'See https://github.com/callstack/react-native-viewpager', + "It can now be installed and imported from 'react-native-pager-view' instead of 'react-native'. " + + 'See https://github.com/callstack/react-native-pager-view', ); }, }); @@ -727,6 +716,22 @@ if (__DEV__) { }, }); + /* $FlowFixMe[prop-missing] This is intentional: Flow will error when + * attempting to access SegmentedControlIOS. */ + /* $FlowFixMe[invalid-export] This is intentional: Flow will error when + * attempting to access SegmentedControlIOS. */ + Object.defineProperty(module.exports, 'SegmentedControlIOS', { + configurable: true, + get() { + invariant( + false, + 'SegmentedControlIOS has been removed from React Native. ' + + "It can now be installed and imported from '@react-native-community/segmented-checkbox' instead of 'react-native'." + + 'See https://github.com/react-native-segmented-control/segmented-control', + ); + }, + }); + /* $FlowFixMe[prop-missing] This is intentional: Flow will error when * attempting to access StatusBarIOS. */ /* $FlowFixMe[invalid-export] This is intentional: Flow will error when diff --git a/yarn.lock b/yarn.lock index b6e14b64d30..88b76150126 100644 --- a/yarn.lock +++ b/yarn.lock @@ -430,7 +430,7 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" -"@babel/plugin-proposal-async-generator-functions@^7.16.8": +"@babel/plugin-proposal-async-generator-functions@^7.0.0", "@babel/plugin-proposal-async-generator-functions@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== @@ -1820,7 +1820,7 @@ dependencies: ora "^3.4.0" -"@react-native-community/cli@^7.0.1": +"@react-native-community/cli@^7.0.3": version "7.0.3" resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-7.0.3.tgz#1addb462d71786fcbbd266fbceb41819b8cf7839" integrity sha512-WyJOA829KAhU1pw2MDQt0YhOS9kyR2KqyqgJyTuQhzFVCBPX4F5aDEkZYYn4jdldaDHCPrLJ3ho3gxYTXy+x7w== @@ -4258,7 +4258,7 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -coveralls@^3.0.2: +coveralls@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.1.tgz#f5d4431d8b5ae69c5079c8f8ca00d64ac77cf081" integrity sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww== @@ -7963,10 +7963,10 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== -metro-babel-register@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.67.0.tgz#03cb70cef59547cc43ab01ae187c046804175245" - integrity sha512-rlUTQSgVuVqnNqX/Mp+g8xqFl3OJlO28cmEJGixBekjx+hDEcPPUfwxpFgUgzNQZaG9D1dEqB9pVxn89ecZPVw== +metro-babel-register@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.68.0.tgz#7f68fc04b385daf702b31e1b8b9e0f8e94fbf13e" + integrity sha512-KgPNipU2CkU+f+6m4b6P6jHdgYbEQ6IVr/4mO0Tqd6MMZdDQj8V3x9BK9UT11OEXocUP4+AD87EpWp8mrnySSA== dependencies: "@babel/core" "^7.14.0" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" @@ -7989,6 +7989,16 @@ metro-babel-transformer@0.67.0: metro-source-map "0.67.0" nullthrows "^1.1.1" +metro-babel-transformer@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.68.0.tgz#90e0f6aac31082c1a1ca930d19fa2c3b829090f1" + integrity sha512-uVW3GiamyMjMSmZFWxuFrfdoGYtW56xojYl7BABpO8U634kK2tTGB8Hm+RGdzWEjBZ1B9NzKKrlM2Crtvxi72w== + dependencies: + "@babel/core" "^7.14.0" + hermes-parser "0.5.0" + metro-source-map "0.68.0" + nullthrows "^1.1.1" + metro-cache-key@0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.67.0.tgz#4df6a73cced199e1bddd0f3454bb931a27141eeb" @@ -8092,7 +8102,67 @@ metro-react-native-babel-preset@0.67.0, metro-react-native-babel-preset@^0.67.0: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.67.0, metro-react-native-babel-transformer@^0.67.0: +metro-react-native-babel-preset@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.68.0.tgz#5a896aeeabb4f0dc796724ed75efb3e05d81b02f" + integrity sha512-y0fi1DYi2AyYxqIjjJHN0OloX9qi8qC8/+UhJaAZr3QNEt1dXUE0yhydcKoARn6rzgqWHqbiL6nqJOqfvbHr1w== + dependencies: + "@babel/core" "^7.14.0" + "@babel/plugin-proposal-async-generator-functions" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-export-default-from" "^7.0.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.0.0" + "@babel/plugin-syntax-dynamic-import" "^7.0.0" + "@babel/plugin-syntax-export-default-from" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-async-to-generator" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-exponentiation-operator" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-for-of" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-object-assign" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + "@babel/plugin-transform-regenerator" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-sticky-regex" "^7.0.0" + "@babel/plugin-transform-template-literals" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.5.0" + "@babel/plugin-transform-unicode-regex" "^7.0.0" + "@babel/template" "^7.0.0" + react-refresh "^0.4.0" + +metro-react-native-babel-transformer@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.68.0.tgz#bfdfe3af860142fac8feb2a4f40c9c3bf1ea1706" + integrity sha512-NCV4yrefc3jshl8yJdialj7qnGkbh8went5jJvZyvviTMfpFAjBbnlm8Hj1dzzXbxt8Z5K6CQ8xVioMxDAJRCw== + dependencies: + "@babel/core" "^7.14.0" + babel-preset-fbjs "^3.4.0" + hermes-parser "0.5.0" + metro-babel-transformer "0.68.0" + metro-react-native-babel-preset "0.68.0" + metro-source-map "0.68.0" + nullthrows "^1.1.1" + +metro-react-native-babel-transformer@^0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.67.0.tgz#756d32eb3c05cab3d72fcb1700f8fd09322bb07f" integrity sha512-P0JT09n7T01epUtgL9mH6BPat3xn4JjBakl4lWHdL61cvEGcrxuIom1eoFFKkgU/K5AVLU4aCAttHS7nSFCcEQ== @@ -8124,6 +8194,11 @@ metro-runtime@0.67.0, metro-runtime@^0.67.0: resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.67.0.tgz#a8888dfd06bcebbac3c99dcac7cd622510dd8ee0" integrity sha512-IFtSL0JUt1xK3t9IoLflTDft82bjieSzdIJWLzrRzBMlesz8ox5bVmnpQbVQEwfYUpEOxbM3VOZauVbdCmXA7g== +metro-runtime@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.68.0.tgz#58cc80de8d615bf783e0446ad2830b724ccc0001" + integrity sha512-FDtecsblgru4ObES1EHIyyBCTYTHxxY3olv+4V/OtSfaD4AJVnqyVeSG865tz5fYnrJ4pk4jYj7vn8x16WJQdA== + metro-source-map@0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.67.0.tgz#e28db7253b9ca688e60d5710ebdccba60b45b2df" @@ -8138,6 +8213,20 @@ metro-source-map@0.67.0: source-map "^0.5.6" vlq "^1.0.0" +metro-source-map@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.68.0.tgz#8ea2563b1efa596fe0614b51e5cd5de3e4c0cdd3" + integrity sha512-/pDVGIUyb+Wf5aNxcf+PLqss1X2tt5BJAzSSv9CqCI6NdM0qUO+6sklIme0xkQ0DSfsf+SJQVhId3b8As8dfag== + dependencies: + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.0.0" + invariant "^2.2.4" + metro-symbolicate "0.68.0" + nullthrows "^1.1.1" + ob1 "0.68.0" + source-map "^0.5.6" + vlq "^1.0.0" + metro-symbolicate@0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.67.0.tgz#16729d05663d28176895244b3d932a898fca2b45" @@ -8150,6 +8239,18 @@ metro-symbolicate@0.67.0: through2 "^2.0.1" vlq "^1.0.0" +metro-symbolicate@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.68.0.tgz#166b571172cafaee91f98628132e379158b7f1b6" + integrity sha512-zX/aZBGutE9RN/mDZjUtQ478jaRiAn/ZekfnuvTtaUuBTpH+oxmqA5jYxEarzevwOAKptCGKlhIj9/+9RDk8zg== + dependencies: + invariant "^2.2.4" + metro-source-map "0.68.0" + nullthrows "^1.1.1" + source-map "^0.5.6" + through2 "^2.0.1" + vlq "^1.0.0" + metro-transform-plugins@0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.67.0.tgz#6122aa4e5e5f9a767cebcc5af6fd1695666683ce" @@ -8671,6 +8772,11 @@ ob1@0.67.0: resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.67.0.tgz#91f104c90641b1af8c364fc82a4b2c7d0801072d" integrity sha512-YvZtX8HKYackQ5PwdFIuuNFVsMChRPHvnARRRT0Vk59xsBvL5t9U1Ock3M1sYrKj+Gp73+0q9xcHLAxI+xLi5g== +ob1@0.68.0: + version "0.68.0" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.68.0.tgz#1da5746721190125f093add4b2ff6b8e457344b8" + integrity sha512-Ukcd9MascBFsYhwf1ovgVElHR4z0hEIL9rAifm296LqCceqg9atPwhLWJGYdtDvD19aQvUpNKfsahK5TkEW75A== + object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -9451,10 +9557,10 @@ react-native-codegen@*, react-native-codegen@^0.0.13: jscodeshift "^0.13.1" nullthrows "^1.1.1" -react-native-gradle-plugin@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.4.tgz#47adcc4f1e1f2c1558811ad78ad39546007d8667" - integrity sha512-D0lFhHy9uSkiPKsGEdEoFtN/jCjS70OxxzBXfq0s9J3ie8GXRBEDHsZuuX/enfRq5fvbCnhKjuYezf+DVYTNnw== +react-native-gradle-plugin@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.5.tgz#1f20d437b140eda65b6e3bdf6eb102bbab1a5a10" + integrity sha512-kGupXo+pD2mB6Z+Oyowor3qlCroiS32FNGoiGQdwU19u8o+NNhEZKwoKfC5Qt03bMZSmFlcAlTyf79vrS2BZKQ== dependencies: react-native-codegen "*" @@ -9476,13 +9582,13 @@ react-native-xaml@^0.0.50: "@types/react-native" "*" typescript "^4.4.3" -react-native@0.0.0-20220213-2008-7cece3423: - version "0.0.0-20220213-2008-7cece3423" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.0.0-20220213-2008-7cece3423.tgz#5d46dc530c5beb1d5987368bfe793f115d5ca853" - integrity sha512-dbF3VVtPZAJalaOCrUeYId6JFHhZbZ6dKVDPKULPQIf3kuw62XTu/4ud4aDqUjAtW+9WxK1QycTQActp4kDj8g== +react-native@0.0.0-20220227-2009-189c2c895: + version "0.0.0-20220227-2009-189c2c895" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.0.0-20220227-2009-189c2c895.tgz#f542413a108a03e2f4a8996b1161454c02360e96" + integrity sha512-GQ8kIOuwXwmO0pFuCgMSWi4uzvVI6HTizpPmt8/2FR5KZfAacHu0c0A4Sp3uce2qW9m8fY44aWPtAqt0uK2gMQ== dependencies: "@jest/create-cache-key-function" "^27.0.1" - "@react-native-community/cli" "^7.0.1" + "@react-native-community/cli" "^7.0.3" "@react-native-community/cli-platform-android" "^7.0.1" "@react-native-community/cli-platform-ios" "^7.0.1" "@react-native/assets" "1.0.0" @@ -9495,15 +9601,15 @@ react-native@0.0.0-20220213-2008-7cece3423: hermes-engine "~0.11.0" invariant "^2.2.4" jsc-android "^250230.2.1" - metro-react-native-babel-transformer "0.67.0" - metro-runtime "0.67.0" - metro-source-map "0.67.0" + metro-react-native-babel-transformer "0.68.0" + metro-runtime "0.68.0" + metro-source-map "0.68.0" nullthrows "^1.1.1" pretty-format "^26.5.2" promise "^8.0.3" react-devtools-core "^4.23.0" react-native-codegen "^0.0.13" - react-native-gradle-plugin "^0.0.4" + react-native-gradle-plugin "^0.0.5" react-refresh "^0.4.0" react-shallow-renderer "16.14.1" regenerator-runtime "^0.13.2" From 8e263f5ba19afe5ca812eb98d57c9bb4141d377b Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Tue, 8 Mar 2022 17:01:40 -0800 Subject: [PATCH 02/10] Change files --- ...-native-win32-3e9a2f0b-e14e-4de2-bae8-fe92b29112dd.json | 7 +++++++ ...ation-channel-7a87de4b-82ba-412b-a105-ef96dda7437a.json | 7 +++++++ ...tualized-list-ba05714e-8a5f-4e00-8a69-28e147d800fa.json | 7 +++++++ ...ative-windows-57a34396-bb10-414e-8bd6-f08fcb1f6cef.json | 7 +++++++ 4 files changed, 28 insertions(+) create mode 100644 change/@office-iss-react-native-win32-3e9a2f0b-e14e-4de2-bae8-fe92b29112dd.json create mode 100644 change/@react-native-windows-automation-channel-7a87de4b-82ba-412b-a105-ef96dda7437a.json create mode 100644 change/@react-native-windows-virtualized-list-ba05714e-8a5f-4e00-8a69-28e147d800fa.json create mode 100644 change/react-native-windows-57a34396-bb10-414e-8bd6-f08fcb1f6cef.json diff --git a/change/@office-iss-react-native-win32-3e9a2f0b-e14e-4de2-bae8-fe92b29112dd.json b/change/@office-iss-react-native-win32-3e9a2f0b-e14e-4de2-bae8-fe92b29112dd.json new file mode 100644 index 00000000000..d05cfc2980c --- /dev/null +++ b/change/@office-iss-react-native-win32-3e9a2f0b-e14e-4de2-bae8-fe92b29112dd.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "integrates 2/27/22", + "packageName": "@office-iss/react-native-win32", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-automation-channel-7a87de4b-82ba-412b-a105-ef96dda7437a.json b/change/@react-native-windows-automation-channel-7a87de4b-82ba-412b-a105-ef96dda7437a.json new file mode 100644 index 00000000000..dca2745cb4d --- /dev/null +++ b/change/@react-native-windows-automation-channel-7a87de4b-82ba-412b-a105-ef96dda7437a.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "integrates 2/27/22", + "packageName": "@react-native-windows/automation-channel", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-virtualized-list-ba05714e-8a5f-4e00-8a69-28e147d800fa.json b/change/@react-native-windows-virtualized-list-ba05714e-8a5f-4e00-8a69-28e147d800fa.json new file mode 100644 index 00000000000..1856eeee316 --- /dev/null +++ b/change/@react-native-windows-virtualized-list-ba05714e-8a5f-4e00-8a69-28e147d800fa.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "integrates 2/27/22", + "packageName": "@react-native-windows/virtualized-list", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-57a34396-bb10-414e-8bd6-f08fcb1f6cef.json b/change/react-native-windows-57a34396-bb10-414e-8bd6-f08fcb1f6cef.json new file mode 100644 index 00000000000..e6e36f96970 --- /dev/null +++ b/change/react-native-windows-57a34396-bb10-414e-8bd6-f08fcb1f6cef.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "integrates 2/27/22", + "packageName": "react-native-windows", + "email": "email not defined", + "dependentChangeType": "patch" +} From 581d45738543babe170a2e5541aa1d5b82a5aabb Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Wed, 9 Mar 2022 11:12:05 -0800 Subject: [PATCH 03/10] adds static cast to RawPropsParser --- .../react/renderer/core/RawPropsParser.cpp | 162 ++++++++++++++++++ vnext/overrides.json | 7 + 2 files changed, 169 insertions(+) create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp new file mode 100644 index 00000000000..0f82d139b81 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RawPropsParser.h" + +#include +#include +#include + +#include + +namespace facebook { +namespace react { + +// During parser initialization, Props structs are used to parse +// "fake"/empty objects, and `at` is called repeatedly which tells us +// which props are accessed during parsing, and in which order. +RawValue const *RawPropsParser::at( + RawProps const &rawProps, + RawPropsKey const &key) const noexcept { + if (UNLIKELY(!ready_)) { + // Check against the same key being inserted more than once. + // This happens commonly with nested Props structs, where the higher-level + // struct may access all fields, and then the nested Props struct may + // access fields a second (or third, etc) time. + // Without this, multiple entries will be created for the same key, but + // only the first access of the key will return a sensible value. + // The complexity of this is (n + (n - 1) + (n - 2) + ... + (n - (n - 1) + + // 1))) or n*n - (1/2)(n*(n+1)). If there are 100 props, this will result in + // 4950 lookups and equality checks on initialization of the parser, which + // happens exactly once per component. + size_t size = keys_.size(); + for (int i = 0; i < size; i++) { + if (keys_[i] == key) { + return nullptr; + } + } + // This is not thread-safe part; this happens only during initialization of + // a `ComponentDescriptor` where it is actually safe. + keys_.push_back(key); + nameToIndex_.insert(key, static_cast(size)); + return nullptr; + } + + // Normally, keys are looked up in-order. For performance we can simply + // increment this key counter, and if the key is equal to the key at the next + // index, there's no need to do any lookups. However, it's possible for keys + // to be accessed out-of-order or multiple times, in which case we start + // searching again from index 0. + // To prevent infinite loops (which can occur if + // you look up a key that doesn't exist) we keep track of whether or not we've + // already looped around, and log and return nullptr if so. However, we ONLY + // do this in debug mode, where you're more likely to look up a nonexistent + // key as part of debugging. You can (and must) ensure infinite loops are not + // possible in production by: (1) constructing all props objects without + // conditionals, or (2) if there are conditionals, ensure that in the parsing + // setup case, the Props constructor will access _all_ possible props. To + // ensure this performance optimization is utilized, always access props in + // the same order every time. This is trivial if you have a simple Props + // constructor, but difficult or impossible if you have a shared sub-prop + // Struct that is used by multiple parent Props. +#ifdef REACT_NATIVE_DEBUG + bool resetLoop = false; +#endif + do { + rawProps.keyIndexCursor_++; + + if (UNLIKELY(rawProps.keyIndexCursor_ >= keys_.size())) { +#ifdef REACT_NATIVE_DEBUG + if (resetLoop) { + LOG(ERROR) << "Looked up RawProps key that does not exist: " + << (std::string)key; + return nullptr; + } + resetLoop = true; +#endif + rawProps.keyIndexCursor_ = 0; + } + } while (UNLIKELY(key != keys_[rawProps.keyIndexCursor_])); + + auto valueIndex = rawProps.keyIndexToValueIndex_[rawProps.keyIndexCursor_]; + return valueIndex == kRawPropsValueIndexEmpty ? nullptr + : &rawProps.values_[valueIndex]; +} + +void RawPropsParser::postPrepare() noexcept { + ready_ = true; + nameToIndex_.reindex(); +} + +void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { + const size_t keyCount = keys_.size(); + rawProps.keyIndexToValueIndex_.resize(keyCount, kRawPropsValueIndexEmpty); + + // Resetting the cursor, the next increment will give `0`. + rawProps.keyIndexCursor_ = static_cast(keyCount - 1); + + switch (rawProps.mode_) { + case RawProps::Mode::Empty: + return; + + case RawProps::Mode::JSI: { + auto &runtime = *rawProps.runtime_; + if (!rawProps.value_.isObject()) { + LOG(ERROR) << "Preparse props: rawProps value is not object"; + } + react_native_assert(rawProps.value_.isObject()); + auto object = rawProps.value_.asObject(runtime); + + auto names = object.getPropertyNames(runtime); + auto count = names.size(runtime); + auto valueIndex = RawPropsValueIndex{0}; + + for (size_t i = 0; i < count; i++) { + auto nameValue = names.getValueAtIndex(runtime, i).getString(runtime); + auto value = object.getProperty(runtime, nameValue); + + auto name = nameValue.utf8(runtime); + + auto keyIndex = nameToIndex_.at( + name.data(), static_cast(name.size())); + if (keyIndex == kRawPropsValueIndexEmpty) { + continue; + } + + rawProps.keyIndexToValueIndex_[keyIndex] = valueIndex; + rawProps.values_.push_back( + RawValue(jsi::dynamicFromValue(runtime, value))); + valueIndex++; + } + + break; + } + + case RawProps::Mode::Dynamic: { + auto const &dynamic = rawProps.dynamic_; + auto valueIndex = RawPropsValueIndex{0}; + + for (auto const &pair : dynamic.items()) { + auto name = pair.first.getString(); + + auto keyIndex = nameToIndex_.at( + name.data(), static_cast(name.size())); + if (keyIndex == kRawPropsValueIndexEmpty) { + continue; + } + + rawProps.keyIndexToValueIndex_[keyIndex] = valueIndex; + rawProps.values_.push_back(RawValue{pair.second}); + valueIndex++; + } + break; + } + } +} + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/vnext/overrides.json b/vnext/overrides.json index a926e2acbc7..663c80c9c22 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -58,6 +58,13 @@ "baseHash": "6ef3f675e8af4060ebd43ba75136d58932b02eb4", "issue": 111 }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp", + "baseFile": "ReactCommon/react/renderer/core/RawPropsParser.cpp", + "baseHash": "738a8dcf33d1e2bae57f2727625c8ca609f5fd0b", + "issue": 111 + }, { "type": "patch", "file": "ReactCommon/Yoga.cpp", From ddd6566d434a21c423005385a1a02b64f07b44e3 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Wed, 9 Mar 2022 15:42:57 -0800 Subject: [PATCH 04/10] removes overrides and adds function to JSCRuntime --- .../JSI/JsiAbiApi.cpp | 10 +- .../Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h | 1 + .../jsi/JSCRuntime.cpp | 1480 ----------------- .../jsi/jsi/decorator.h | 753 --------- .../TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h | 1321 --------------- vnext/overrides.json | 21 - 6 files changed, 10 insertions(+), 3576 deletions(-) delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp index f5aa9be9524..dbe7222d774 100644 --- a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp +++ b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp @@ -306,6 +306,14 @@ PropNameID JsiAbiRuntime::createPropNameIDFromString(const String &str) try { throw; } +PropNameID JsiAbiRuntime::createPropNameIDFromSymbol(const Symbol &sym) { + // TODO: Support for symbols through the native API in JSC is very limited. + // While we could construct a PropNameID here, we would not be able to get a + // symbol property through the C++ API. + RethrowJsiError(); + throw; +} + std::string JsiAbiRuntime::utf8(const PropNameID &propertyId) try { std::string dataResult; m_runtime.PropertyIdToUtf8(AsJsiPropertyIdRef(propertyId), [&dataResult](array_view utf8) { @@ -1004,4 +1012,4 @@ JsiAbiRuntime::PropNameIDRef::operator facebook::jsi::PropNameID const &() const } // namespace winrt::Microsoft::ReactNative -#pragma warning(pop) \ No newline at end of file +#pragma warning(pop) diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h index 6be1dad3351..1c3db3fb410 100644 --- a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h +++ b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h @@ -99,6 +99,7 @@ struct JsiAbiRuntime : facebook::jsi::Runtime { facebook::jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromString(const facebook::jsi::String &str) override; + facebook::jsi::PropNameID createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym); std::string utf8(const facebook::jsi::PropNameID &propertyNameId) override; bool compare(const facebook::jsi::PropNameID &left, const facebook::jsi::PropNameID &right) override; diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp deleted file mode 100644 index 58804af1739..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp +++ /dev/null @@ -1,1480 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "JSCRuntime.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace jsc { - -namespace detail { -class ArgsConverter; -} // namespace detail - -class JSCRuntime; - -struct Lock { - void lock(const jsc::JSCRuntime &) const {} - void unlock(const jsc::JSCRuntime &) const {} -}; - -class JSCRuntime : public jsi::Runtime { - public: - // Creates new context in new context group - JSCRuntime(); - // Retains ctx - JSCRuntime(JSGlobalContextRef ctx); - ~JSCRuntime(); - - std::shared_ptr prepareJavaScript( - const std::shared_ptr &buffer, - std::string sourceURL) override; - - jsi::Value evaluatePreparedJavaScript( - const std::shared_ptr &js) override; - - jsi::Value evaluateJavaScript( - const std::shared_ptr &buffer, - const std::string &sourceURL) override; - - bool drainMicrotasks(int maxMicrotasksHint = -1) override; - - jsi::Object global() override; - - std::string description() override; - - bool isInspectable() override; - - void setDescription(const std::string &desc); - - // Please don't use the following two functions, only exposed for - // integration efforts. - JSGlobalContextRef getContext() { - return ctx_; - } - - // JSValueRef->JSValue (needs make.*Value so it must be member function) - jsi::Value createValue(JSValueRef value) const; - - // Value->JSValueRef (similar to above) - JSValueRef valueRef(const jsi::Value &value); - - protected: - friend class detail::ArgsConverter; - class JSCSymbolValue final : public PointerValue { -#ifndef NDEBUG - JSCSymbolValue( - JSGlobalContextRef ctx, - const std::atomic &ctxInvalid, - JSValueRef sym, - std::atomic &counter); -#else - JSCSymbolValue( - JSGlobalContextRef ctx, - const std::atomic &ctxInvalid, - JSValueRef sym); -#endif - void invalidate() override; - - JSGlobalContextRef ctx_; - const std::atomic &ctxInvalid_; - // There is no C type in the JSC API to represent Symbol, so this stored - // a JSValueRef which contains the Symbol. - JSValueRef sym_; -#ifndef NDEBUG - std::atomic &counter_; -#endif - protected: - friend class JSCRuntime; - }; - - class JSCStringValue final : public PointerValue { -#ifndef NDEBUG - JSCStringValue(JSStringRef str, std::atomic &counter); -#else - JSCStringValue(JSStringRef str); -#endif - void invalidate() override; - - JSStringRef str_; -#ifndef NDEBUG - std::atomic &counter_; -#endif - protected: - friend class JSCRuntime; - }; - - class JSCObjectValue final : public PointerValue { - JSCObjectValue( - JSGlobalContextRef ctx, - const std::atomic &ctxInvalid, - JSObjectRef obj -#ifndef NDEBUG - , - std::atomic &counter -#endif - ); - - void invalidate() override; - - JSGlobalContextRef ctx_; - const std::atomic &ctxInvalid_; - JSObjectRef obj_; -#ifndef NDEBUG - std::atomic &counter_; -#endif - protected: - friend class JSCRuntime; - }; - - PointerValue *cloneSymbol(const Runtime::PointerValue *pv) override; - PointerValue *cloneString(const Runtime::PointerValue *pv) override; - PointerValue *cloneObject(const Runtime::PointerValue *pv) override; - PointerValue *clonePropNameID(const Runtime::PointerValue *pv) override; - - jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) - override; - jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) - override; - jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override; - std::string utf8(const jsi::PropNameID &) override; - bool compare(const jsi::PropNameID &, const jsi::PropNameID &) override; - - std::string symbolToString(const jsi::Symbol &) override; - - jsi::String createStringFromAscii(const char *str, size_t length) override; - jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; - std::string utf8(const jsi::String &) override; - - jsi::Object createObject() override; - jsi::Object createObject(std::shared_ptr ho) override; - virtual std::shared_ptr getHostObject( - const jsi::Object &) override; - jsi::HostFunctionType &getHostFunction(const jsi::Function &) override; - - jsi::Value getProperty(const jsi::Object &, const jsi::String &name) override; - jsi::Value getProperty(const jsi::Object &, const jsi::PropNameID &name) - override; - bool hasProperty(const jsi::Object &, const jsi::String &name) override; - bool hasProperty(const jsi::Object &, const jsi::PropNameID &name) override; - void setPropertyValue( - jsi::Object &, - const jsi::String &name, - const jsi::Value &value) override; - void setPropertyValue( - jsi::Object &, - const jsi::PropNameID &name, - const jsi::Value &value) override; - bool isArray(const jsi::Object &) const override; - bool isArrayBuffer(const jsi::Object &) const override; - bool isFunction(const jsi::Object &) const override; - bool isHostObject(const jsi::Object &) const override; - bool isHostFunction(const jsi::Function &) const override; - jsi::Array getPropertyNames(const jsi::Object &) override; - - // TODO: revisit this implementation - jsi::WeakObject createWeakObject(const jsi::Object &) override; - jsi::Value lockWeakObject(jsi::WeakObject &) override; - - jsi::Array createArray(size_t length) override; - size_t size(const jsi::Array &) override; - size_t size(const jsi::ArrayBuffer &) override; - uint8_t *data(const jsi::ArrayBuffer &) override; - jsi::Value getValueAtIndex(const jsi::Array &, size_t i) override; - void setValueAtIndexImpl(jsi::Array &, size_t i, const jsi::Value &value) - override; - - jsi::Function createFunctionFromHostFunction( - const jsi::PropNameID &name, - unsigned int paramCount, - jsi::HostFunctionType func) override; - jsi::Value call( - const jsi::Function &, - const jsi::Value &jsThis, - const jsi::Value *args, - size_t count) override; - jsi::Value callAsConstructor( - const jsi::Function &, - const jsi::Value *args, - size_t count) override; - - bool strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const override; - bool strictEquals(const jsi::String &a, const jsi::String &b) const override; - bool strictEquals(const jsi::Object &a, const jsi::Object &b) const override; - bool instanceOf(const jsi::Object &o, const jsi::Function &f) override; - - private: - // Basically convenience casts - static JSValueRef symbolRef(const jsi::Symbol &str); - static JSStringRef stringRef(const jsi::String &str); - static JSStringRef stringRef(const jsi::PropNameID &sym); - static JSObjectRef objectRef(const jsi::Object &obj); - -#ifdef RN_FABRIC_ENABLED - static JSObjectRef objectRef(const jsi::WeakObject &obj); -#endif - - // Factory methods for creating String/Object - jsi::Symbol createSymbol(JSValueRef symbolRef) const; - jsi::String createString(JSStringRef stringRef) const; - jsi::PropNameID createPropNameID(JSStringRef stringRef); - jsi::Object createObject(JSObjectRef objectRef) const; - - // Used by factory methods and clone methods - jsi::Runtime::PointerValue *makeSymbolValue(JSValueRef sym) const; - jsi::Runtime::PointerValue *makeStringValue(JSStringRef str) const; - jsi::Runtime::PointerValue *makeObjectValue(JSObjectRef obj) const; - - void checkException(JSValueRef exc); - void checkException(JSValueRef res, JSValueRef exc); - void checkException(JSValueRef exc, const char *msg); - void checkException(JSValueRef res, JSValueRef exc, const char *msg); - - JSGlobalContextRef ctx_; - std::atomic ctxInvalid_; - std::string desc_; -#ifndef NDEBUG - mutable std::atomic objectCounter_; - mutable std::atomic symbolCounter_; - mutable std::atomic stringCounter_; -#endif -}; - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#if __has_builtin(__builtin_expect) || defined(__GNUC__) -#define JSC_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) -#define JSC_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) -#else -#define JSC_LIKELY(EXPR) (EXPR) -#define JSC_UNLIKELY(EXPR) (EXPR) -#endif - -#define JSC_ASSERT(x) \ - do { \ - if (JSC_UNLIKELY(!!(x))) { \ - abort(); \ - } \ - } while (0) - -#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -// This takes care of watch and tvos (due to backwards compatibility in -// Availability.h -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0 -#define _JSC_FAST_IS_ARRAY -#endif -#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 -#define _JSC_NO_ARRAY_BUFFERS -#endif -#endif -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11 -// Only one of these should be set for a build. If somehow that's not -// true, this will be a compile-time error and it can be resolved when -// we understand why. -#define _JSC_FAST_IS_ARRAY -#endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12 -#define _JSC_NO_ARRAY_BUFFERS -#endif -#endif - -// JSStringRef utilities -namespace { -std::string JSStringToSTLString(JSStringRef str) { - // Small string optimization: Avoid one heap allocation for strings that fit - // in stackBuffer.size() bytes of UTF-8 (including the null terminator). - std::array stackBuffer; - std::unique_ptr heapBuffer; - char *buffer; - // NOTE: By definition, maxBytes >= 1 since the null terminator is included. - size_t maxBytes = JSStringGetMaximumUTF8CStringSize(str); - if (maxBytes <= stackBuffer.size()) { - buffer = stackBuffer.data(); - } else { - heapBuffer = std::make_unique(maxBytes); - buffer = heapBuffer.get(); - } - size_t actualBytes = JSStringGetUTF8CString(str, buffer, maxBytes); - if (!actualBytes) { - // Happens if maxBytes == 0 (never the case here) or if str contains - // invalid UTF-16 data, since JSStringGetUTF8CString attempts a strict - // conversion. - // When converting an invalid string, JSStringGetUTF8CString writes a null - // terminator before returning. So we can reliably treat our buffer as a C - // string and return the truncated data to our caller. This is slightly - // slower than if we knew the length (like below) but better than crashing. - // TODO(T62295565): Perform a non-strict, best effort conversion of the - // full string instead, like we did before the JSI migration. - return std::string(buffer); - } - return std::string(buffer, actualBytes - 1); -} - -JSStringRef getLengthString() { - static JSStringRef length = JSStringCreateWithUTF8CString("length"); - return length; -} - -JSStringRef getNameString() { - static JSStringRef name = JSStringCreateWithUTF8CString("name"); - return name; -} - -JSStringRef getFunctionString() { - static JSStringRef func = JSStringCreateWithUTF8CString("Function"); - return func; -} - -#if !defined(_JSC_FAST_IS_ARRAY) -JSStringRef getArrayString() { - static JSStringRef array = JSStringCreateWithUTF8CString("Array"); - return array; -} - -JSStringRef getIsArrayString() { - static JSStringRef isArray = JSStringCreateWithUTF8CString("isArray"); - return isArray; -} -#endif -} // namespace - -// std::string utility -namespace { -std::string to_string(void *value) { - std::ostringstream ss; - ss << value; - return ss.str(); -} -} // namespace - -JSCRuntime::JSCRuntime() - : JSCRuntime(JSGlobalContextCreateInGroup(nullptr, nullptr)) { - JSGlobalContextRelease(ctx_); -} - -JSCRuntime::JSCRuntime(JSGlobalContextRef ctx) - : ctx_(JSGlobalContextRetain(ctx)), - ctxInvalid_(false) -#ifndef NDEBUG - , - objectCounter_(0), - stringCounter_(0) -#endif -{ -} - -JSCRuntime::~JSCRuntime() { - // On shutting down and cleaning up: when JSC is actually torn down, - // it calls JSC::Heap::lastChanceToFinalize internally which - // finalizes anything left over. But at this point, - // JSValueUnprotect() can no longer be called. We use an - // atomic to avoid unsafe unprotects happening after shutdown - // has started. - ctxInvalid_ = true; - JSGlobalContextRelease(ctx_); -#ifndef NDEBUG - assert( - objectCounter_ == 0 && "JSCRuntime destroyed with a dangling API object"); - assert( - stringCounter_ == 0 && "JSCRuntime destroyed with a dangling API string"); -#endif -} - -std::shared_ptr JSCRuntime::prepareJavaScript( - const std::shared_ptr &buffer, - std::string sourceURL) { - return std::make_shared( - buffer, std::move(sourceURL)); -} - -jsi::Value JSCRuntime::evaluatePreparedJavaScript( - const std::shared_ptr &js) { - assert( - dynamic_cast(js.get()) && - "preparedJavaScript must be a SourceJavaScriptPreparation"); - auto sourceJs = - std::static_pointer_cast(js); - return evaluateJavaScript(sourceJs, sourceJs->sourceURL()); -} - -jsi::Value JSCRuntime::evaluateJavaScript( - const std::shared_ptr &buffer, - const std::string &sourceURL) { - std::string tmp( - reinterpret_cast(buffer->data()), buffer->size()); - JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str()); - JSStringRef sourceURLRef = nullptr; - if (!sourceURL.empty()) { - sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str()); - } - JSValueRef exc = nullptr; - JSValueRef res = - JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc); - JSStringRelease(sourceRef); - if (sourceURLRef) { - JSStringRelease(sourceURLRef); - } - checkException(res, exc); - return createValue(res); -} - -bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) { - return true; -} - -jsi::Object JSCRuntime::global() { - return createObject(JSContextGetGlobalObject(ctx_)); -} - -std::string JSCRuntime::description() { - if (desc_.empty()) { - desc_ = std::string(""; - } - return desc_; -} - -bool JSCRuntime::isInspectable() { - return false; -} - -namespace { - -bool smellsLikeES6Symbol(JSGlobalContextRef ctx, JSValueRef ref) { - // Since iOS 13, JSValueGetType will return kJSTypeSymbol - // Before: Empirically, an es6 Symbol is not an object, but its type is - // object. This makes no sense, but we'll run with it. - // https://github.com/WebKit/webkit/blob/master/Source/JavaScriptCore/API/JSValueRef.cpp#L79-L82 - - JSType type = JSValueGetType(ctx, ref); - - if (type == /* kJSTypeSymbol */ 6) { - return true; - } - - return (!JSValueIsObject(ctx, ref) && type == kJSTypeObject); -} - -} // namespace - -JSCRuntime::JSCSymbolValue::JSCSymbolValue( - JSGlobalContextRef ctx, - const std::atomic &ctxInvalid, - JSValueRef sym -#ifndef NDEBUG - , - std::atomic &counter -#endif - ) - : ctx_(ctx), - ctxInvalid_(ctxInvalid), - sym_(sym) -#ifndef NDEBUG - , - counter_(counter) -#endif -{ - assert(smellsLikeES6Symbol(ctx_, sym_)); - JSValueProtect(ctx_, sym_); -#ifndef NDEBUG - counter_ += 1; -#endif -} - -void JSCRuntime::JSCSymbolValue::invalidate() { -#ifndef NDEBUG - counter_ -= 1; -#endif - - if (!ctxInvalid_) { - JSValueUnprotect(ctx_, sym_); - } - delete this; -} - -#ifndef NDEBUG -JSCRuntime::JSCStringValue::JSCStringValue( - JSStringRef str, - std::atomic &counter) - : str_(JSStringRetain(str)), counter_(counter) { - // Since std::atomic returns a copy instead of a reference when calling - // operator+= we must do this explicitly in the constructor - counter_ += 1; -} -#else -JSCRuntime::JSCStringValue::JSCStringValue(JSStringRef str) - : str_(JSStringRetain(str)) {} -#endif - -void JSCRuntime::JSCStringValue::invalidate() { - // These JSC{String,Object}Value objects are implicitly owned by the - // {String,Object} objects, thus when a String/Object is destructed - // the JSC{String,Object}Value should be released. -#ifndef NDEBUG - counter_ -= 1; -#endif - JSStringRelease(str_); - // Angery reaccs only - delete this; -} - -JSCRuntime::JSCObjectValue::JSCObjectValue( - JSGlobalContextRef ctx, - const std::atomic &ctxInvalid, - JSObjectRef obj -#ifndef NDEBUG - , - std::atomic &counter -#endif - ) - : ctx_(ctx), - ctxInvalid_(ctxInvalid), - obj_(obj) -#ifndef NDEBUG - , - counter_(counter) -#endif -{ - JSValueProtect(ctx_, obj_); -#ifndef NDEBUG - counter_ += 1; -#endif -} - -void JSCRuntime::JSCObjectValue::invalidate() { -#ifndef NDEBUG - counter_ -= 1; -#endif - // When shutting down the VM, if there is a HostObject which - // contains or otherwise owns a jsi::Object, then the final GC will - // finalize the HostObject, leading to a call to invalidate(). But - // at that point, making calls to JSValueUnprotect will crash. - // It is up to the application to make sure that any other calls to - // invalidate() happen before VM destruction; see the comment on - // jsi::Runtime. - // - // Another potential concern here is that in the non-shutdown case, - // if a HostObject is GCd, JSValueUnprotect will be called from the - // JSC finalizer. The documentation warns against this: "You must - // not call any function that may cause a garbage collection or an - // allocation of a garbage collected object from within a - // JSObjectFinalizeCallback. This includes all functions that have a - // JSContextRef parameter." However, an audit of the source code for - // JSValueUnprotect in late 2018 shows that it cannot cause - // allocation or a GC, and further, this code has not changed in - // about two years. In the future, we may choose to reintroduce the - // mechanism previously used here which uses a separate thread for - // JSValueUnprotect, in order to conform to the documented API, but - // use the "unsafe" synchronous version on iOS 11 and earlier. - - if (!ctxInvalid_) { - JSValueUnprotect(ctx_, obj_); - } - delete this; -} - -jsi::Runtime::PointerValue *JSCRuntime::cloneSymbol( - const jsi::Runtime::PointerValue *pv) { - if (!pv) { - return nullptr; - } - const JSCSymbolValue *symbol = static_cast(pv); - return makeSymbolValue(symbol->sym_); -} - -jsi::Runtime::PointerValue *JSCRuntime::cloneString( - const jsi::Runtime::PointerValue *pv) { - if (!pv) { - return nullptr; - } - const JSCStringValue *string = static_cast(pv); - return makeStringValue(string->str_); -} - -jsi::Runtime::PointerValue *JSCRuntime::cloneObject( - const jsi::Runtime::PointerValue *pv) { - if (!pv) { - return nullptr; - } - const JSCObjectValue *object = static_cast(pv); - assert( - object->ctx_ == ctx_ && - "Don't try to clone an object backed by a different Runtime"); - return makeObjectValue(object->obj_); -} - -jsi::Runtime::PointerValue *JSCRuntime::clonePropNameID( - const jsi::Runtime::PointerValue *pv) { - if (!pv) { - return nullptr; - } - const JSCStringValue *string = static_cast(pv); - return makeStringValue(string->str_); -} - -jsi::PropNameID JSCRuntime::createPropNameIDFromAscii( - const char *str, - size_t length) { - // For system JSC this must is identical to a string - std::string tmp(str, length); - JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str()); - auto res = createPropNameID(strRef); - JSStringRelease(strRef); - return res; -} - -jsi::PropNameID JSCRuntime::createPropNameIDFromUtf8( - const uint8_t *utf8, - size_t length) { - std::string tmp(reinterpret_cast(utf8), length); - JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str()); - auto res = createPropNameID(strRef); - JSStringRelease(strRef); - return res; -} - -jsi::PropNameID JSCRuntime::createPropNameIDFromString(const jsi::String &str) { - return createPropNameID(stringRef(str)); -} - -std::string JSCRuntime::utf8(const jsi::PropNameID &sym) { - return JSStringToSTLString(stringRef(sym)); -} - -bool JSCRuntime::compare(const jsi::PropNameID &a, const jsi::PropNameID &b) { - return JSStringIsEqual(stringRef(a), stringRef(b)); -} - -std::string JSCRuntime::symbolToString(const jsi::Symbol &sym) { - return jsi::Value(*this, sym).toString(*this).utf8(*this); -} - -jsi::String JSCRuntime::createStringFromAscii(const char *str, size_t length) { - // Yes we end up double casting for semantic reasons (UTF8 contains ASCII, - // not the other way around) - return this->createStringFromUtf8( - reinterpret_cast(str), length); -} - -jsi::String JSCRuntime::createStringFromUtf8( - const uint8_t *str, - size_t length) { - std::string tmp(reinterpret_cast(str), length); - JSStringRef stringRef = JSStringCreateWithUTF8CString(tmp.c_str()); - auto result = createString(stringRef); - JSStringRelease(stringRef); - return result; -} - -std::string JSCRuntime::utf8(const jsi::String &str) { - return JSStringToSTLString(stringRef(str)); -} - -jsi::Object JSCRuntime::createObject() { - return createObject(static_cast(nullptr)); -} - -// HostObject details -namespace detail { -struct HostObjectProxyBase { - HostObjectProxyBase( - JSCRuntime &rt, - const std::shared_ptr &sho) - : runtime(rt), hostObject(sho) {} - - JSCRuntime &runtime; - std::shared_ptr hostObject; -}; -} // namespace detail - -namespace { -std::once_flag hostObjectClassOnceFlag; -JSClassRef hostObjectClass{}; -} // namespace - -jsi::Object JSCRuntime::createObject(std::shared_ptr ho) { - struct HostObjectProxy : public detail::HostObjectProxyBase { - static JSValueRef getProperty( - JSContextRef ctx, - JSObjectRef object, - JSStringRef propName, - JSValueRef *exception) { - auto proxy = static_cast(JSObjectGetPrivate(object)); - auto &rt = proxy->runtime; - jsi::PropNameID sym = rt.createPropNameID(propName); - jsi::Value ret; - try { - ret = proxy->hostObject->get(rt, sym); - } catch (const jsi::JSError &error) { - *exception = rt.valueRef(error.value()); - return JSValueMakeUndefined(ctx); - } catch (const std::exception &ex) { - auto excValue = - rt.global() - .getPropertyAsFunction(rt, "Error") - .call( - rt, - std::string("Exception in HostObject::get(propName:") + - JSStringToSTLString(propName) + std::string("): ") + - ex.what()); - *exception = rt.valueRef(excValue); - return JSValueMakeUndefined(ctx); - } catch (...) { - auto excValue = - rt.global() - .getPropertyAsFunction(rt, "Error") - .call( - rt, - std::string("Exception in HostObject::get(propName:") + - JSStringToSTLString(propName) + - std::string("): ")); - *exception = rt.valueRef(excValue); - return JSValueMakeUndefined(ctx); - } - return rt.valueRef(ret); - } - -#define JSC_UNUSED(x) (void)(x); - - static bool setProperty( - JSContextRef ctx, - JSObjectRef object, - JSStringRef propName, - JSValueRef value, - JSValueRef *exception) { - JSC_UNUSED(ctx); - auto proxy = static_cast(JSObjectGetPrivate(object)); - auto &rt = proxy->runtime; - jsi::PropNameID sym = rt.createPropNameID(propName); - try { - proxy->hostObject->set(rt, sym, rt.createValue(value)); - } catch (const jsi::JSError &error) { - *exception = rt.valueRef(error.value()); - return false; - } catch (const std::exception &ex) { - auto excValue = - rt.global() - .getPropertyAsFunction(rt, "Error") - .call( - rt, - std::string("Exception in HostObject::set(propName:") + - JSStringToSTLString(propName) + std::string("): ") + - ex.what()); - *exception = rt.valueRef(excValue); - return false; - } catch (...) { - auto excValue = - rt.global() - .getPropertyAsFunction(rt, "Error") - .call( - rt, - std::string("Exception in HostObject::set(propName:") + - JSStringToSTLString(propName) + - std::string("): ")); - *exception = rt.valueRef(excValue); - return false; - } - return true; - } - - // JSC does not provide means to communicate errors from this callback, - // so the error handling strategy is very brutal - we'll just crash - // due to noexcept. - static void getPropertyNames( - JSContextRef ctx, - JSObjectRef object, - JSPropertyNameAccumulatorRef propertyNames) noexcept { - JSC_UNUSED(ctx); - auto proxy = static_cast(JSObjectGetPrivate(object)); - auto &rt = proxy->runtime; - auto names = proxy->hostObject->getPropertyNames(rt); - for (auto &name : names) { - JSPropertyNameAccumulatorAddName(propertyNames, stringRef(name)); - } - } - -#undef JSC_UNUSED - - static void finalize(JSObjectRef obj) { - auto hostObject = static_cast(JSObjectGetPrivate(obj)); - JSObjectSetPrivate(obj, nullptr); - delete hostObject; - } - - using HostObjectProxyBase::HostObjectProxyBase; - }; - - std::call_once(hostObjectClassOnceFlag, []() { - JSClassDefinition hostObjectClassDef = kJSClassDefinitionEmpty; - hostObjectClassDef.version = 0; - hostObjectClassDef.attributes = kJSClassAttributeNoAutomaticPrototype; - hostObjectClassDef.finalize = HostObjectProxy::finalize; - hostObjectClassDef.getProperty = HostObjectProxy::getProperty; - hostObjectClassDef.setProperty = HostObjectProxy::setProperty; - hostObjectClassDef.getPropertyNames = HostObjectProxy::getPropertyNames; - hostObjectClass = JSClassCreate(&hostObjectClassDef); - }); - - JSObjectRef obj = - JSObjectMake(ctx_, hostObjectClass, new HostObjectProxy(*this, ho)); - return createObject(obj); -} - -std::shared_ptr JSCRuntime::getHostObject( - const jsi::Object &obj) { - // We are guaranteed at this point to have isHostObject(obj) == true - // so the private data should be HostObjectMetadata - JSObjectRef object = objectRef(obj); - auto metadata = - static_cast(JSObjectGetPrivate(object)); - assert(metadata); - return metadata->hostObject; -} - -jsi::Value JSCRuntime::getProperty( - const jsi::Object &obj, - const jsi::String &name) { - JSObjectRef objRef = objectRef(obj); - JSValueRef exc = nullptr; - JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc); - checkException(exc); - return createValue(res); -} - -jsi::Value JSCRuntime::getProperty( - const jsi::Object &obj, - const jsi::PropNameID &name) { - JSObjectRef objRef = objectRef(obj); - JSValueRef exc = nullptr; - JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc); - checkException(exc); - return createValue(res); -} - -bool JSCRuntime::hasProperty(const jsi::Object &obj, const jsi::String &name) { - JSObjectRef objRef = objectRef(obj); - return JSObjectHasProperty(ctx_, objRef, stringRef(name)); -} - -bool JSCRuntime::hasProperty( - const jsi::Object &obj, - const jsi::PropNameID &name) { - JSObjectRef objRef = objectRef(obj); - return JSObjectHasProperty(ctx_, objRef, stringRef(name)); -} - -void JSCRuntime::setPropertyValue( - jsi::Object &object, - const jsi::PropNameID &name, - const jsi::Value &value) { - JSValueRef exc = nullptr; - JSObjectSetProperty( - ctx_, - objectRef(object), - stringRef(name), - valueRef(value), - kJSPropertyAttributeNone, - &exc); - checkException(exc); -} - -void JSCRuntime::setPropertyValue( - jsi::Object &object, - const jsi::String &name, - const jsi::Value &value) { - JSValueRef exc = nullptr; - JSObjectSetProperty( - ctx_, - objectRef(object), - stringRef(name), - valueRef(value), - kJSPropertyAttributeNone, - &exc); - checkException(exc); -} - -bool JSCRuntime::isArray(const jsi::Object &obj) const { -#if !defined(_JSC_FAST_IS_ARRAY) - JSObjectRef global = JSContextGetGlobalObject(ctx_); - JSStringRef arrayString = getArrayString(); - JSValueRef exc = nullptr; - JSValueRef arrayCtorValue = - JSObjectGetProperty(ctx_, global, arrayString, &exc); - JSC_ASSERT(exc); - JSObjectRef arrayCtor = JSValueToObject(ctx_, arrayCtorValue, &exc); - JSC_ASSERT(exc); - JSStringRef isArrayString = getIsArrayString(); - JSValueRef isArrayValue = - JSObjectGetProperty(ctx_, arrayCtor, isArrayString, &exc); - JSC_ASSERT(exc); - JSObjectRef isArray = JSValueToObject(ctx_, isArrayValue, &exc); - JSC_ASSERT(exc); - JSValueRef arg = objectRef(obj); - JSValueRef result = - JSObjectCallAsFunction(ctx_, isArray, nullptr, 1, &arg, &exc); - JSC_ASSERT(exc); - return JSValueToBoolean(ctx_, result); -#else - return JSValueIsArray(ctx_, objectRef(obj)); -#endif -} - -bool JSCRuntime::isArrayBuffer(const jsi::Object &obj) const { -#if defined(_JSC_NO_ARRAY_BUFFERS) - throw std::runtime_error("Unsupported"); -#else - auto typedArrayType = JSValueGetTypedArrayType(ctx_, objectRef(obj), nullptr); - return typedArrayType == kJSTypedArrayTypeArrayBuffer; -#endif -} - -uint8_t *JSCRuntime::data(const jsi::ArrayBuffer &obj) { -#if defined(_JSC_NO_ARRAY_BUFFERS) - throw std::runtime_error("Unsupported"); -#else - return static_cast( - JSObjectGetArrayBufferBytesPtr(ctx_, objectRef(obj), nullptr)); -#endif -} - -size_t JSCRuntime::size(const jsi::ArrayBuffer &obj) { -#if defined(_JSC_NO_ARRAY_BUFFERS) - throw std::runtime_error("Unsupported"); -#else - return JSObjectGetArrayBufferByteLength(ctx_, objectRef(obj), nullptr); -#endif -} - -bool JSCRuntime::isFunction(const jsi::Object &obj) const { - return JSObjectIsFunction(ctx_, objectRef(obj)); -} - -bool JSCRuntime::isHostObject(const jsi::Object &obj) const { - auto cls = hostObjectClass; - return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls); -} - -// Very expensive -jsi::Array JSCRuntime::getPropertyNames(const jsi::Object &obj) { - JSPropertyNameArrayRef names = - JSObjectCopyPropertyNames(ctx_, objectRef(obj)); - size_t len = JSPropertyNameArrayGetCount(names); - // Would be better if we could create an array with explicit elements - auto result = createArray(len); - for (size_t i = 0; i < len; i++) { - JSStringRef str = JSPropertyNameArrayGetNameAtIndex(names, i); - result.setValueAtIndex(*this, i, createString(str)); - } - JSPropertyNameArrayRelease(names); - return result; -} - -jsi::WeakObject JSCRuntime::createWeakObject(const jsi::Object &obj) { -#ifdef RN_FABRIC_ENABLED - // TODO: revisit this implementation - JSObjectRef objRef = objectRef(obj); - return make(makeObjectValue(objRef)); -#else - throw std::logic_error("Not implemented"); -#endif -} - -jsi::Value JSCRuntime::lockWeakObject(jsi::WeakObject &obj) { -#ifdef RN_FABRIC_ENABLED - // TODO: revisit this implementation - JSObjectRef objRef = objectRef(obj); - return jsi::Value(createObject(objRef)); -#else - throw std::logic_error("Not implemented"); -#endif -} - -jsi::Array JSCRuntime::createArray(size_t length) { - JSValueRef exc = nullptr; - JSObjectRef obj = JSObjectMakeArray(ctx_, 0, nullptr, &exc); - checkException(obj, exc); - JSObjectSetProperty( - ctx_, - obj, - getLengthString(), - JSValueMakeNumber(ctx_, static_cast(length)), - 0, - &exc); - checkException(exc); - return createObject(obj).getArray(*this); -} - -size_t JSCRuntime::size(const jsi::Array &arr) { - return static_cast( - getProperty(arr, createPropNameID(getLengthString())).getNumber()); -} - -jsi::Value JSCRuntime::getValueAtIndex(const jsi::Array &arr, size_t i) { - JSValueRef exc = nullptr; - auto res = JSObjectGetPropertyAtIndex(ctx_, objectRef(arr), (int)i, &exc); - checkException(exc); - return createValue(res); -} - -void JSCRuntime::setValueAtIndexImpl( - jsi::Array &arr, - size_t i, - const jsi::Value &value) { - JSValueRef exc = nullptr; - JSObjectSetPropertyAtIndex( - ctx_, objectRef(arr), (int)i, valueRef(value), &exc); - checkException(exc); -} - -namespace { -std::once_flag hostFunctionClassOnceFlag; -JSClassRef hostFunctionClass{}; - -class HostFunctionProxy { - public: - HostFunctionProxy(jsi::HostFunctionType hostFunction) - : hostFunction_(hostFunction) {} - - jsi::HostFunctionType &getHostFunction() { - return hostFunction_; - } - - protected: - jsi::HostFunctionType hostFunction_; -}; -} // namespace - -jsi::Function JSCRuntime::createFunctionFromHostFunction( - const jsi::PropNameID &name, - unsigned int paramCount, - jsi::HostFunctionType func) { - class HostFunctionMetadata : public HostFunctionProxy { - public: - static void initialize(JSContextRef ctx, JSObjectRef object) { - // We need to set up the prototype chain properly here. In theory we - // could set func.prototype.prototype = Function.prototype to get the - // same result. Not sure which approach is better. - HostFunctionMetadata *metadata = - static_cast(JSObjectGetPrivate(object)); - - JSValueRef exc = nullptr; - JSObjectSetProperty( - ctx, - object, - getLengthString(), - JSValueMakeNumber(ctx, metadata->argCount), - kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | - kJSPropertyAttributeDontDelete, - &exc); - if (exc) { - // Silently fail to set length - exc = nullptr; - } - - JSStringRef name = nullptr; - std::swap(metadata->name, name); - JSObjectSetProperty( - ctx, - object, - getNameString(), - JSValueMakeString(ctx, name), - kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | - kJSPropertyAttributeDontDelete, - &exc); - JSStringRelease(name); - if (exc) { - // Silently fail to set name - exc = nullptr; - } - - JSObjectRef global = JSContextGetGlobalObject(ctx); - JSValueRef value = - JSObjectGetProperty(ctx, global, getFunctionString(), &exc); - // If we don't have Function then something bad is going on. - if (JSC_UNLIKELY(exc)) { - abort(); - } - JSObjectRef funcCtor = JSValueToObject(ctx, value, &exc); - if (!funcCtor) { - // We can't do anything if Function is not an object - return; - } - JSValueRef funcProto = JSObjectGetPrototype(ctx, funcCtor); - JSObjectSetPrototype(ctx, object, funcProto); - } - - static JSValueRef makeError(JSCRuntime &rt, const std::string &desc) { - jsi::Value value = - rt.global().getPropertyAsFunction(rt, "Error").call(rt, desc); - return rt.valueRef(value); - } - - static JSValueRef call( - JSContextRef ctx, - JSObjectRef function, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[], - JSValueRef *exception) { - HostFunctionMetadata *metadata = - static_cast(JSObjectGetPrivate(function)); - JSCRuntime &rt = *(metadata->runtime); - const unsigned maxStackArgCount = 8; - jsi::Value stackArgs[maxStackArgCount]; - std::unique_ptr heapArgs; - jsi::Value *args; - if (argumentCount > maxStackArgCount) { - heapArgs = std::make_unique(argumentCount); - for (size_t i = 0; i < argumentCount; i++) { - heapArgs[i] = rt.createValue(arguments[i]); - } - args = heapArgs.get(); - } else { - for (size_t i = 0; i < argumentCount; i++) { - stackArgs[i] = rt.createValue(arguments[i]); - } - args = stackArgs; - } - JSValueRef res; - jsi::Value thisVal(rt.createObject(thisObject)); - try { - res = rt.valueRef( - metadata->hostFunction_(rt, thisVal, args, argumentCount)); - } catch (const jsi::JSError &error) { - *exception = rt.valueRef(error.value()); - res = JSValueMakeUndefined(ctx); - } catch (const std::exception &ex) { - std::string exceptionString("Exception in HostFunction: "); - exceptionString += ex.what(); - *exception = makeError(rt, exceptionString); - res = JSValueMakeUndefined(ctx); - } catch (...) { - std::string exceptionString("Exception in HostFunction: "); - *exception = makeError(rt, exceptionString); - res = JSValueMakeUndefined(ctx); - } - return res; - } - - static void finalize(JSObjectRef object) { - HostFunctionMetadata *metadata = - static_cast(JSObjectGetPrivate(object)); - JSObjectSetPrivate(object, nullptr); - delete metadata; - } - - HostFunctionMetadata( - JSCRuntime *rt, - jsi::HostFunctionType hf, - unsigned ac, - JSStringRef n) - : HostFunctionProxy(hf), - runtime(rt), - argCount(ac), - name(JSStringRetain(n)) {} - - JSCRuntime *runtime; - unsigned argCount; - JSStringRef name; - }; - - std::call_once(hostFunctionClassOnceFlag, []() { - JSClassDefinition functionClass = kJSClassDefinitionEmpty; - functionClass.version = 0; - functionClass.attributes = kJSClassAttributeNoAutomaticPrototype; - functionClass.initialize = HostFunctionMetadata::initialize; - functionClass.finalize = HostFunctionMetadata::finalize; - functionClass.callAsFunction = HostFunctionMetadata::call; - - hostFunctionClass = JSClassCreate(&functionClass); - }); - - JSObjectRef funcRef = JSObjectMake( - ctx_, - hostFunctionClass, - new HostFunctionMetadata(this, func, paramCount, stringRef(name))); - return createObject(funcRef).getFunction(*this); -} - -namespace detail { - -class ArgsConverter { - public: - ArgsConverter(JSCRuntime &rt, const jsi::Value *args, size_t count) { - JSValueRef *destination = inline_; - if (count > maxStackArgs) { - outOfLine_ = std::make_unique(count); - destination = outOfLine_.get(); - } - - for (size_t i = 0; i < count; ++i) { - destination[i] = rt.valueRef(args[i]); - } - } - - operator JSValueRef *() { - return outOfLine_ ? outOfLine_.get() : inline_; - } - - private: - constexpr static unsigned maxStackArgs = 8; - JSValueRef inline_[maxStackArgs]; - std::unique_ptr outOfLine_; -}; -} // namespace detail - -bool JSCRuntime::isHostFunction(const jsi::Function &obj) const { - auto cls = hostFunctionClass; - return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls); -} - -jsi::HostFunctionType &JSCRuntime::getHostFunction(const jsi::Function &obj) { - // We know that isHostFunction(obj) is true here, so its safe to proceed - auto proxy = - static_cast(JSObjectGetPrivate(objectRef(obj))); - return proxy->getHostFunction(); -} - -jsi::Value JSCRuntime::call( - const jsi::Function &f, - const jsi::Value &jsThis, - const jsi::Value *args, - size_t count) { - JSValueRef exc = nullptr; - auto res = JSObjectCallAsFunction( - ctx_, - objectRef(f), - jsThis.isUndefined() ? nullptr : objectRef(jsThis.getObject(*this)), - count, - detail::ArgsConverter(*this, args, count), - &exc); - checkException(exc); - return createValue(res); -} - -jsi::Value JSCRuntime::callAsConstructor( - const jsi::Function &f, - const jsi::Value *args, - size_t count) { - JSValueRef exc = nullptr; - auto res = JSObjectCallAsConstructor( - ctx_, - objectRef(f), - count, - detail::ArgsConverter(*this, args, count), - &exc); - checkException(exc); - return createValue(res); -} - -bool JSCRuntime::strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) - const { - JSValueRef exc = nullptr; - bool ret = JSValueIsEqual(ctx_, symbolRef(a), symbolRef(b), &exc); - const_cast(this)->checkException(exc); - return ret; -} - -bool JSCRuntime::strictEquals(const jsi::String &a, const jsi::String &b) - const { - return JSStringIsEqual(stringRef(a), stringRef(b)); -} - -bool JSCRuntime::strictEquals(const jsi::Object &a, const jsi::Object &b) - const { - return objectRef(a) == objectRef(b); -} - -bool JSCRuntime::instanceOf(const jsi::Object &o, const jsi::Function &f) { - JSValueRef exc = nullptr; - bool res = - JSValueIsInstanceOfConstructor(ctx_, objectRef(o), objectRef(f), &exc); - checkException(exc); - return res; -} - -jsi::Runtime::PointerValue *JSCRuntime::makeSymbolValue( - JSValueRef symbolRef) const { -#ifndef NDEBUG - return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef, symbolCounter_); -#else - return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef); -#endif -} - -namespace { -JSStringRef getEmptyString() { - static JSStringRef empty = JSStringCreateWithUTF8CString(""); - return empty; -} -} // namespace - -jsi::Runtime::PointerValue *JSCRuntime::makeStringValue( - JSStringRef stringRef) const { - if (!stringRef) { - stringRef = getEmptyString(); - } -#ifndef NDEBUG - return new JSCStringValue(stringRef, stringCounter_); -#else - return new JSCStringValue(stringRef); -#endif -} - -jsi::Symbol JSCRuntime::createSymbol(JSValueRef sym) const { - return make(makeSymbolValue(sym)); -} - -jsi::String JSCRuntime::createString(JSStringRef str) const { - return make(makeStringValue(str)); -} - -jsi::PropNameID JSCRuntime::createPropNameID(JSStringRef str) { - return make(makeStringValue(str)); -} - -jsi::Runtime::PointerValue *JSCRuntime::makeObjectValue( - JSObjectRef objectRef) const { - if (!objectRef) { - objectRef = JSObjectMake(ctx_, nullptr, nullptr); - } -#ifndef NDEBUG - return new JSCObjectValue(ctx_, ctxInvalid_, objectRef, objectCounter_); -#else - return new JSCObjectValue(ctx_, ctxInvalid_, objectRef); -#endif -} - -jsi::Object JSCRuntime::createObject(JSObjectRef obj) const { - return make(makeObjectValue(obj)); -} - -jsi::Value JSCRuntime::createValue(JSValueRef value) const { - JSType type = JSValueGetType(ctx_, value); - - switch (type) { - case kJSTypeNumber: - return jsi::Value(JSValueToNumber(ctx_, value, nullptr)); - case kJSTypeBoolean: - return jsi::Value(JSValueToBoolean(ctx_, value)); - case kJSTypeNull: - return jsi::Value(nullptr); - case kJSTypeUndefined: - return jsi::Value(); - case kJSTypeString: { - JSStringRef str = JSValueToStringCopy(ctx_, value, nullptr); - auto result = jsi::Value(createString(str)); - JSStringRelease(str); - return result; - } - case kJSTypeObject: { - JSObjectRef objRef = JSValueToObject(ctx_, value, nullptr); - return jsi::Value(createObject(objRef)); - } - // TODO: Uncomment this when all supported JSC versions have this symbol - // case kJSTypeSymbol: - default: { - if (smellsLikeES6Symbol(ctx_, value)) { - return jsi::Value(createSymbol(value)); - } else { - // WHAT ARE YOU - abort(); - } - } - } -} - -JSValueRef JSCRuntime::valueRef(const jsi::Value &value) { - // I would rather switch on value.kind_ - if (value.isUndefined()) { - return JSValueMakeUndefined(ctx_); - } else if (value.isNull()) { - return JSValueMakeNull(ctx_); - } else if (value.isBool()) { - return JSValueMakeBoolean(ctx_, value.getBool()); - } else if (value.isNumber()) { - return JSValueMakeNumber(ctx_, value.getNumber()); - } else if (value.isSymbol()) { - return symbolRef(value.getSymbol(*this)); - } else if (value.isString()) { - return JSValueMakeString(ctx_, stringRef(value.getString(*this))); - } else if (value.isObject()) { - return objectRef(value.getObject(*this)); - } else { - // What are you? - abort(); - } -} - -JSValueRef JSCRuntime::symbolRef(const jsi::Symbol &sym) { - return static_cast(getPointerValue(sym))->sym_; -} - -JSStringRef JSCRuntime::stringRef(const jsi::String &str) { - return static_cast(getPointerValue(str))->str_; -} - -JSStringRef JSCRuntime::stringRef(const jsi::PropNameID &sym) { - return static_cast(getPointerValue(sym))->str_; -} - -JSObjectRef JSCRuntime::objectRef(const jsi::Object &obj) { - return static_cast(getPointerValue(obj))->obj_; -} - -#ifdef RN_FABRIC_ENABLED -JSObjectRef JSCRuntime::objectRef(const jsi::WeakObject &obj) { - // TODO: revisit this implementation - return static_cast(getPointerValue(obj))->obj_; -} -#endif - -void JSCRuntime::checkException(JSValueRef exc) { - if (JSC_UNLIKELY(exc)) { - throw jsi::JSError(*this, createValue(exc)); - } -} - -void JSCRuntime::checkException(JSValueRef res, JSValueRef exc) { - if (JSC_UNLIKELY(!res)) { - throw jsi::JSError(*this, createValue(exc)); - } -} - -void JSCRuntime::checkException(JSValueRef exc, const char *msg) { - if (JSC_UNLIKELY(exc)) { - throw jsi::JSError(std::string(msg), *this, createValue(exc)); - } -} - -void JSCRuntime::checkException( - JSValueRef res, - JSValueRef exc, - const char *msg) { - if (JSC_UNLIKELY(!res)) { - throw jsi::JSError(std::string(msg), *this, createValue(exc)); - } -} - -std::unique_ptr makeJSCRuntime() { - return std::make_unique(); -} - -} // namespace jsc -} // namespace facebook diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h deleted file mode 100644 index a3619544459..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include - -// This file contains objects to help API users create their own -// runtime adapters, i.e. if you want to compose runtimes to add your -// own behavior. - -namespace facebook { -namespace jsi { - -// Use this to wrap host functions. It will pass the member runtime as -// the first arg to the callback. The first argument to the ctor -// should be the decorated runtime, not the plain one. -class DecoratedHostFunction { - public: - DecoratedHostFunction(Runtime& drt, HostFunctionType plainHF) - : drt_(drt), plainHF_(std::move(plainHF)) {} - - Runtime& decoratedRuntime() { - return drt_; - } - - Value - operator()(Runtime&, const Value& thisVal, const Value* args, size_t count) { - return plainHF_(decoratedRuntime(), thisVal, args, count); - } - - private: - template - friend class RuntimeDecorator; - - Runtime& drt_; - HostFunctionType plainHF_; -}; - -// From the perspective of the caller, a plain HostObject is passed to -// the decorated Runtime, and the HostObject methods expect to get -// passed that Runtime. But the plain Runtime will pass itself to its -// callback, so we need a helper here which curries the decorated -// Runtime, and calls the plain HostObject with it. -// -// If the concrete RuntimeDecorator derives DecoratedHostObject, it -// should call the base class get() and set() to invoke the plain -// HostObject functionality. The Runtime& it passes does not matter, -// as it is not used. -class DecoratedHostObject : public HostObject { - public: - DecoratedHostObject(Runtime& drt, std::shared_ptr plainHO) - : drt_(drt), plainHO_(plainHO) {} - - // The derived class methods can call this to get a reference to the - // decorated runtime, since the rt passed to the callback will be - // the plain runtime. - Runtime& decoratedRuntime() { - return drt_; - } - - Value get(Runtime&, const PropNameID& name) override { - return plainHO_->get(decoratedRuntime(), name); - } - - void set(Runtime&, const PropNameID& name, const Value& value) override { - plainHO_->set(decoratedRuntime(), name, value); - } - - std::vector getPropertyNames(Runtime&) override { - return plainHO_->getPropertyNames(decoratedRuntime()); - } - - private: - template - friend class RuntimeDecorator; - - Runtime& drt_; - std::shared_ptr plainHO_; -}; - -/// C++ variant on a standard Decorator pattern, using template -/// parameters. The \c Plain template parameter type is the -/// undecorated Runtime type. You can usually use \c Runtime here, -/// but if you know the concrete type ahead of time and it's final, -/// the compiler can devirtualize calls to the decorated -/// implementation. The \c Base template parameter type will be used -/// as the base class of the decorated type. Here, too, you can -/// usually use \c Runtime, but if you want the decorated type to -/// implement a derived class of Runtime, you can specify that here. -/// For an example, see threadsafe.h. -template -class RuntimeDecorator : public Base, private jsi::Instrumentation { - public: - Plain& plain() { - static_assert( - std::is_base_of::value, - "RuntimeDecorator's Plain type must derive from jsi::Runtime"); - static_assert( - std::is_base_of::value, - "RuntimeDecorator's Base type must derive from jsi::Runtime"); - return plain_; - } - const Plain& plain() const { - return plain_; - } - - Value evaluateJavaScript( - const std::shared_ptr& buffer, - const std::string& sourceURL) override { - return plain().evaluateJavaScript(buffer, sourceURL); - } - std::shared_ptr prepareJavaScript( - const std::shared_ptr& buffer, - std::string sourceURL) override { - return plain().prepareJavaScript(buffer, std::move(sourceURL)); - } - Value evaluatePreparedJavaScript( - const std::shared_ptr& js) override { - return plain().evaluatePreparedJavaScript(js); - } - bool drainMicrotasks(int maxMicrotasksHint) override { - return plain().drainMicrotasks(maxMicrotasksHint); - } - Object global() override { - return plain().global(); - } - std::string description() override { - return plain().description(); - }; - bool isInspectable() override { - return plain().isInspectable(); - }; - Instrumentation& instrumentation() override { - return *this; - } - - protected: - // plain is generally going to be a reference to an object managed - // by a derived class. We cache it here so this class can be - // concrete, and avoid making virtual calls to find the plain - // Runtime. Note that the ctor and dtor do not access through the - // reference, so passing a reference to an object before its - // lifetime has started is ok. - RuntimeDecorator(Plain& plain) : plain_(plain) {} - - Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { - return plain_.cloneSymbol(pv); - }; - Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { - return plain_.cloneString(pv); - }; - Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { - return plain_.cloneObject(pv); - }; - Runtime::PointerValue* clonePropNameID( - const Runtime::PointerValue* pv) override { - return plain_.clonePropNameID(pv); - }; - - PropNameID createPropNameIDFromAscii(const char* str, size_t length) - override { - return plain_.createPropNameIDFromAscii(str, length); - }; - PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) - override { - return plain_.createPropNameIDFromUtf8(utf8, length); - }; - PropNameID createPropNameIDFromString(const String& str) override { - return plain_.createPropNameIDFromString(str); - }; - std::string utf8(const PropNameID& id) override { - return plain_.utf8(id); - }; - bool compare(const PropNameID& a, const PropNameID& b) override { - return plain_.compare(a, b); - }; - - std::string symbolToString(const Symbol& sym) override { - return plain_.symbolToString(sym); - } - - String createStringFromAscii(const char* str, size_t length) override { - return plain_.createStringFromAscii(str, length); - }; - String createStringFromUtf8(const uint8_t* utf8, size_t length) override { - return plain_.createStringFromUtf8(utf8, length); - }; - std::string utf8(const String& s) override { - return plain_.utf8(s); - } - - Object createObject() override { - return plain_.createObject(); - }; - - Object createObject(std::shared_ptr ho) override { - return plain_.createObject( - std::make_shared(*this, std::move(ho))); - }; - std::shared_ptr getHostObject(const jsi::Object& o) override { - std::shared_ptr dho = plain_.getHostObject(o); - return static_cast(*dho).plainHO_; - }; - HostFunctionType& getHostFunction(const jsi::Function& f) override { - HostFunctionType& dhf = plain_.getHostFunction(f); - // This will fail if a cpp file including this header is not compiled - // with RTTI. - return dhf.target()->plainHF_; - }; - - Value getProperty(const Object& o, const PropNameID& name) override { - return plain_.getProperty(o, name); - }; - Value getProperty(const Object& o, const String& name) override { - return plain_.getProperty(o, name); - }; - bool hasProperty(const Object& o, const PropNameID& name) override { - return plain_.hasProperty(o, name); - }; - bool hasProperty(const Object& o, const String& name) override { - return plain_.hasProperty(o, name); - }; - void setPropertyValue(Object& o, const PropNameID& name, const Value& value) - override { - plain_.setPropertyValue(o, name, value); - }; - void setPropertyValue(Object& o, const String& name, const Value& value) - override { - plain_.setPropertyValue(o, name, value); - }; - - bool isArray(const Object& o) const override { - return plain_.isArray(o); - }; - bool isArrayBuffer(const Object& o) const override { - return plain_.isArrayBuffer(o); - }; - bool isFunction(const Object& o) const override { - return plain_.isFunction(o); - }; - bool isHostObject(const jsi::Object& o) const override { - return plain_.isHostObject(o); - }; - bool isHostFunction(const jsi::Function& f) const override { - return plain_.isHostFunction(f); - }; - Array getPropertyNames(const Object& o) override { - return plain_.getPropertyNames(o); - }; - - WeakObject createWeakObject(const Object& o) override { - return plain_.createWeakObject(o); - }; - Value lockWeakObject(WeakObject& wo) override { - return plain_.lockWeakObject(wo); - }; - - Array createArray(size_t length) override { - return plain_.createArray(length); - }; - size_t size(const Array& a) override { - return plain_.size(a); - }; - size_t size(const ArrayBuffer& ab) override { - return plain_.size(ab); - }; - uint8_t* data(const ArrayBuffer& ab) override { - return plain_.data(ab); - }; - Value getValueAtIndex(const Array& a, size_t i) override { - return plain_.getValueAtIndex(a, i); - }; - void setValueAtIndexImpl(Array& a, size_t i, const Value& value) override { - plain_.setValueAtIndexImpl(a, i, value); - }; - - Function createFunctionFromHostFunction( - const PropNameID& name, - unsigned int paramCount, - HostFunctionType func) override { - return plain_.createFunctionFromHostFunction( - name, paramCount, DecoratedHostFunction(*this, std::move(func))); - }; - Value call( - const Function& f, - const Value& jsThis, - const Value* args, - size_t count) override { - return plain_.call(f, jsThis, args, count); - }; - Value callAsConstructor(const Function& f, const Value* args, size_t count) - override { - return plain_.callAsConstructor(f, args, count); - }; - - // Private data for managing scopes. - Runtime::ScopeState* pushScope() override { - return plain_.pushScope(); - } - void popScope(Runtime::ScopeState* ss) override { - plain_.popScope(ss); - } - - bool strictEquals(const Symbol& a, const Symbol& b) const override { - return plain_.strictEquals(a, b); - }; - bool strictEquals(const String& a, const String& b) const override { - return plain_.strictEquals(a, b); - }; - bool strictEquals(const Object& a, const Object& b) const override { - return plain_.strictEquals(a, b); - }; - - bool instanceOf(const Object& o, const Function& f) override { - return plain_.instanceOf(o, f); - }; - - // jsi::Instrumentation methods - - std::string getRecordedGCStats() override { - return plain().instrumentation().getRecordedGCStats(); - } - - std::unordered_map getHeapInfo( - bool includeExpensive) override { - return plain().instrumentation().getHeapInfo(includeExpensive); - } - - void collectGarbage(std::string cause) override { - plain().instrumentation().collectGarbage(std::move(cause)); - } - - void startTrackingHeapObjectStackTraces( - std::function)> callback) override { - plain().instrumentation().startTrackingHeapObjectStackTraces( - std::move(callback)); - } - - void stopTrackingHeapObjectStackTraces() override { - plain().instrumentation().stopTrackingHeapObjectStackTraces(); - } - - void startHeapSampling(size_t samplingInterval) override { - plain().instrumentation().startHeapSampling(samplingInterval); - } - - void stopHeapSampling(std::ostream& os) override { - plain().instrumentation().stopHeapSampling(os); - } - - void createSnapshotToFile(const std::string& path) override { - plain().instrumentation().createSnapshotToFile(path); - } - - void createSnapshotToStream(std::ostream& os) override { - plain().instrumentation().createSnapshotToStream(os); - } - - std::string flushAndDisableBridgeTrafficTrace() override { - return const_cast(plain()) - .instrumentation() - .flushAndDisableBridgeTrafficTrace(); - } - - void writeBasicBlockProfileTraceToFile( - const std::string& fileName) const override { - const_cast(plain()) - .instrumentation() - .writeBasicBlockProfileTraceToFile(fileName); - } - - /// Dump external profiler symbols to the given file name. - void dumpProfilerSymbolsToFile(const std::string& fileName) const override { - const_cast(plain()).instrumentation().dumpProfilerSymbolsToFile( - fileName); - } - - private: - Plain& plain_; -}; - -namespace detail { - -// This metaprogramming allows the With type's methods to be -// optional. - -template -struct BeforeCaller { - static void before(T&) {} -}; - -template -struct AfterCaller { - static void after(T&) {} -}; - -// decltype((void)&...) is either SFINAE, or void. -// So, if SFINAE does not happen for T, then this specialization exists -// for BeforeCaller, and always applies. If not, only the -// default above exists, and that is used instead. -template -struct BeforeCaller { - static void before(T& t) { - t.before(); - } -}; - -template -struct AfterCaller { - static void after(T& t) { - t.after(); - } -}; - -// It's possible to use multiple decorators by nesting -// WithRuntimeDecorator<...>, but this specialization allows use of -// std::tuple of decorator classes instead. See testlib.cpp for an -// example. -template -struct BeforeCaller> { - static void before(std::tuple& tuple) { - all_before<0, T...>(tuple); - } - - private: - template - static void all_before(std::tuple& tuple) { - detail::BeforeCaller::before(std::get(tuple)); - all_before(tuple); - } - - template - static void all_before(std::tuple&) {} -}; - -template -struct AfterCaller> { - static void after(std::tuple& tuple) { - all_after<0, T...>(tuple); - } - - private: - template - static void all_after(std::tuple& tuple) { - all_after(tuple); - detail::AfterCaller::after(std::get(tuple)); - } - - template - static void all_after(std::tuple&) {} -}; - -} // namespace detail - -// A decorator which implements an around idiom. A With instance is -// RAII constructed before each call to the undecorated class; the -// ctor is passed a single argument of type WithArg&. Plain and Base -// are used as in the base class. -template -class WithRuntimeDecorator : public RuntimeDecorator { - public: - using RD = RuntimeDecorator; - - // The reference arguments to the ctor are stored, but not used by - // the ctor, and there is no ctor, so they can be passed members of - // the derived class. - WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {} - - Value evaluateJavaScript( - const std::shared_ptr& buffer, - const std::string& sourceURL) override { - Around around{with_}; - return RD::evaluateJavaScript(buffer, sourceURL); - } - std::shared_ptr prepareJavaScript( - const std::shared_ptr& buffer, - std::string sourceURL) override { - Around around{with_}; - return RD::prepareJavaScript(buffer, std::move(sourceURL)); - } - Value evaluatePreparedJavaScript( - const std::shared_ptr& js) override { - Around around{with_}; - return RD::evaluatePreparedJavaScript(js); - } - bool drainMicrotasks(int maxMicrotasksHint) override { - Around around{with_}; - return RD::drainMicrotasks(maxMicrotasksHint); - } - Object global() override { - Around around{with_}; - return RD::global(); - } - std::string description() override { - Around around{with_}; - return RD::description(); - }; - bool isInspectable() override { - Around around{with_}; - return RD::isInspectable(); - }; - - // The jsi:: prefix is necessary because MSVC compiler complains C2247: - // Instrumentation is not accessible because RuntimeDecorator uses private - // to inherit from Instrumentation. - // TODO(T40821815) Consider removing this workaround when updating MSVC - jsi::Instrumentation& instrumentation() override { - Around around{with_}; - return RD::instrumentation(); - } - - protected: - Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneSymbol(pv); - }; - Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneString(pv); - }; - Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneObject(pv); - }; - Runtime::PointerValue* clonePropNameID( - const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::clonePropNameID(pv); - }; - - PropNameID createPropNameIDFromAscii(const char* str, size_t length) - override { - Around around{with_}; - return RD::createPropNameIDFromAscii(str, length); - }; - PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) - override { - Around around{with_}; - return RD::createPropNameIDFromUtf8(utf8, length); - }; - PropNameID createPropNameIDFromString(const String& str) override { - Around around{with_}; - return RD::createPropNameIDFromString(str); - }; - std::string utf8(const PropNameID& id) override { - Around around{with_}; - return RD::utf8(id); - }; - bool compare(const PropNameID& a, const PropNameID& b) override { - Around around{with_}; - return RD::compare(a, b); - }; - - std::string symbolToString(const Symbol& sym) override { - Around around{with_}; - return RD::symbolToString(sym); - }; - - String createStringFromAscii(const char* str, size_t length) override { - Around around{with_}; - return RD::createStringFromAscii(str, length); - }; - String createStringFromUtf8(const uint8_t* utf8, size_t length) override { - Around around{with_}; - return RD::createStringFromUtf8(utf8, length); - }; - std::string utf8(const String& s) override { - Around around{with_}; - return RD::utf8(s); - } - - Object createObject() override { - Around around{with_}; - return RD::createObject(); - }; - Object createObject(std::shared_ptr ho) override { - Around around{with_}; - return RD::createObject(std::move(ho)); - }; - std::shared_ptr getHostObject(const jsi::Object& o) override { - Around around{with_}; - return RD::getHostObject(o); - }; - HostFunctionType& getHostFunction(const jsi::Function& f) override { - Around around{with_}; - return RD::getHostFunction(f); - }; - - Value getProperty(const Object& o, const PropNameID& name) override { - Around around{with_}; - return RD::getProperty(o, name); - }; - Value getProperty(const Object& o, const String& name) override { - Around around{with_}; - return RD::getProperty(o, name); - }; - bool hasProperty(const Object& o, const PropNameID& name) override { - Around around{with_}; - return RD::hasProperty(o, name); - }; - bool hasProperty(const Object& o, const String& name) override { - Around around{with_}; - return RD::hasProperty(o, name); - }; - void setPropertyValue(Object& o, const PropNameID& name, const Value& value) - override { - Around around{with_}; - RD::setPropertyValue(o, name, value); - }; - void setPropertyValue(Object& o, const String& name, const Value& value) - override { - Around around{with_}; - RD::setPropertyValue(o, name, value); - }; - - bool isArray(const Object& o) const override { - Around around{with_}; - return RD::isArray(o); - }; - bool isArrayBuffer(const Object& o) const override { - Around around{with_}; - return RD::isArrayBuffer(o); - }; - bool isFunction(const Object& o) const override { - Around around{with_}; - return RD::isFunction(o); - }; - bool isHostObject(const jsi::Object& o) const override { - Around around{with_}; - return RD::isHostObject(o); - }; - bool isHostFunction(const jsi::Function& f) const override { - Around around{with_}; - return RD::isHostFunction(f); - }; - Array getPropertyNames(const Object& o) override { - Around around{with_}; - return RD::getPropertyNames(o); - }; - - WeakObject createWeakObject(const Object& o) override { - Around around{with_}; - return RD::createWeakObject(o); - }; - Value lockWeakObject(WeakObject& wo) override { - Around around{with_}; - return RD::lockWeakObject(wo); - }; - - Array createArray(size_t length) override { - Around around{with_}; - return RD::createArray(length); - }; - size_t size(const Array& a) override { - Around around{with_}; - return RD::size(a); - }; - size_t size(const ArrayBuffer& ab) override { - Around around{with_}; - return RD::size(ab); - }; - uint8_t* data(const ArrayBuffer& ab) override { - Around around{with_}; - return RD::data(ab); - }; - Value getValueAtIndex(const Array& a, size_t i) override { - Around around{with_}; - return RD::getValueAtIndex(a, i); - }; - void setValueAtIndexImpl(Array& a, size_t i, const Value& value) override { - Around around{with_}; - RD::setValueAtIndexImpl(a, i, value); - }; - - Function createFunctionFromHostFunction( - const PropNameID& name, - unsigned int paramCount, - HostFunctionType func) override { - Around around{with_}; - return RD::createFunctionFromHostFunction( - name, paramCount, std::move(func)); - }; - Value call( - const Function& f, - const Value& jsThis, - const Value* args, - size_t count) override { - Around around{with_}; - return RD::call(f, jsThis, args, count); - }; - Value callAsConstructor(const Function& f, const Value* args, size_t count) - override { - Around around{with_}; - return RD::callAsConstructor(f, args, count); - }; - - // Private data for managing scopes. - Runtime::ScopeState* pushScope() override { - Around around{with_}; - return RD::pushScope(); - } - void popScope(Runtime::ScopeState* ss) override { - Around around{with_}; - RD::popScope(ss); - } - - bool strictEquals(const Symbol& a, const Symbol& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - }; - bool strictEquals(const String& a, const String& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - }; - bool strictEquals(const Object& a, const Object& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - }; - - bool instanceOf(const Object& o, const Function& f) override { - Around around{with_}; - return RD::instanceOf(o, f); - }; - - private: - // Wrap an RAII type around With& to guarantee after always happens. - struct Around { - Around(With& with) : with_(with) { - detail::BeforeCaller::before(with_); - } - ~Around() { - detail::AfterCaller::after(with_); - } - - With& with_; - }; - - With& with_; -}; - -} // namespace jsi -} // namespace facebook diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h deleted file mode 100644 index e4764ce2f12..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h +++ /dev/null @@ -1,1321 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#ifndef JSI_EXPORT -#ifdef _MSC_VER -#ifdef CREATE_SHARED_LIBRARY -#define JSI_EXPORT __declspec(dllexport) -#else -#define JSI_EXPORT -#endif // CREATE_SHARED_LIBRARY -#else // _MSC_VER -#define JSI_EXPORT __attribute__((visibility("default"))) -#endif // _MSC_VER -#endif // !defined(JSI_EXPORT) - -class FBJSRuntime; -namespace facebook { -namespace jsi { - -class JSI_EXPORT Buffer { - public: - virtual ~Buffer(); - virtual size_t size() const = 0; - virtual const uint8_t* data() const = 0; -}; - -class JSI_EXPORT StringBuffer : public Buffer { - public: - StringBuffer(std::string s) : s_(std::move(s)) {} - size_t size() const override { - return s_.size(); - } - const uint8_t* data() const override { - return reinterpret_cast(s_.data()); - } - - private: - std::string s_; -}; - -/// PreparedJavaScript is a base class representing JavaScript which is in a -/// form optimized for execution, in a runtime-specific way. Construct one via -/// jsi::Runtime::prepareJavaScript(). -/// ** This is an experimental API that is subject to change. ** -class JSI_EXPORT PreparedJavaScript { - protected: - PreparedJavaScript() = default; - - public: - virtual ~PreparedJavaScript() = 0; -}; - -class Runtime; -class Pointer; -class PropNameID; -class Symbol; -class String; -class Object; -class WeakObject; -class Array; -class ArrayBuffer; -class Function; -class Value; -class Instrumentation; -class Scope; -class JSIException; -class JSError; - -/// A function which has this type can be registered as a function -/// callable from JavaScript using Function::createFromHostFunction(). -/// When the function is called, args will point to the arguments, and -/// count will indicate how many arguments are passed. The function -/// can return a Value to the caller, or throw an exception. If a C++ -/// exception is thrown, a JS Error will be created and thrown into -/// JS; if the C++ exception extends std::exception, the Error's -/// message will be whatever what() returns. Note that it is undefined whether -/// HostFunctions may or may not be called in strict mode; that is `thisVal` -/// can be any value - it will not necessarily be coerced to an object or -/// or set to the global object. -using HostFunctionType = std::function< - Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>; - -/// An object which implements this interface can be registered as an -/// Object with the JS runtime. -class JSI_EXPORT HostObject { - public: - // The C++ object's dtor will be called when the GC finalizes this - // object. (This may be as late as when the Runtime is shut down.) - // You have no control over which thread it is called on. This will - // be called from inside the GC, so it is unsafe to do any VM - // operations which require a Runtime&. Derived classes' dtors - // should also avoid doing anything expensive. Calling the dtor on - // a jsi object is explicitly ok. If you want to do JS operations, - // or any nontrivial work, you should add it to a work queue, and - // manage it externally. - virtual ~HostObject(); - - // When JS wants a property with a given name from the HostObject, - // it will call this method. If it throws an exception, the call - // will throw a JS \c Error object. By default this returns undefined. - // \return the value for the property. - virtual Value get(Runtime&, const PropNameID& name); - - // When JS wants to set a property with a given name on the HostObject, - // it will call this method. If it throws an exception, the call will - // throw a JS \c Error object. By default this throws a type error exception - // mimicking the behavior of a frozen object in strict mode. - virtual void set(Runtime&, const PropNameID& name, const Value& value); - - // When JS wants a list of property names for the HostObject, it will - // call this method. If it throws an exception, the call will throw a - // JS \c Error object. The default implementation returns empty vector. - virtual std::vector getPropertyNames(Runtime& rt); -}; - -/// Represents a JS runtime. Movable, but not copyable. Note that -/// this object may not be thread-aware, but cannot be used safely from -/// multiple threads at once. The application is responsible for -/// ensuring that it is used safely. This could mean using the -/// Runtime from a single thread, using a mutex, doing all work on a -/// serial queue, etc. This restriction applies to the methods of -/// this class, and any method in the API which take a Runtime& as an -/// argument. Destructors (all but ~Scope), operators, or other methods -/// which do not take Runtime& as an argument are safe to call from any -/// thread, but it is still forbidden to make write operations on a single -/// instance of any class from more than one thread. In addition, to -/// make shutdown safe, destruction of objects associated with the Runtime -/// must be destroyed before the Runtime is destroyed, or from the -/// destructor of a managed HostObject or HostFunction. Informally, this -/// means that the main source of unsafe behavior is to hold a jsi object -/// in a non-Runtime-managed object, and not clean it up before the Runtime -/// is shut down. If your lifecycle is such that avoiding this is hard, -/// you will probably need to do use your own locks. -class JSI_EXPORT Runtime { - public: - virtual ~Runtime(); - - /// Evaluates the given JavaScript \c buffer. \c sourceURL is used - /// to annotate the stack trace if there is an exception. The - /// contents may be utf8-encoded JS source code, or binary bytecode - /// whose format is specific to the implementation. If the input - /// format is unknown, or evaluation causes an error, a JSIException - /// will be thrown. - /// Note this function should ONLY be used when there isn't another means - /// through the JSI API. For example, it will be much slower to use this to - /// call a global function than using the JSI APIs to read the function - /// property from the global object and then calling it explicitly. - virtual Value evaluateJavaScript( - const std::shared_ptr& buffer, - const std::string& sourceURL) = 0; - - /// Prepares to evaluate the given JavaScript \c buffer by processing it into - /// a form optimized for execution. This may include pre-parsing, compiling, - /// etc. If the input is invalid (for example, cannot be parsed), a - /// JSIException will be thrown. The resulting object is tied to the - /// particular concrete type of Runtime from which it was created. It may be - /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete - /// type. - /// The PreparedJavaScript object may be passed to multiple VM instances, so - /// they can all share and benefit from the prepared script. - /// As with evaluateJavaScript(), using JavaScript code should be avoided - /// when the JSI API is sufficient. - virtual std::shared_ptr prepareJavaScript( - const std::shared_ptr& buffer, - std::string sourceURL) = 0; - - /// Evaluates a PreparedJavaScript. If evaluation causes an error, a - /// JSIException will be thrown. - /// As with evaluateJavaScript(), using JavaScript code should be avoided - /// when the JSI API is sufficient. - virtual Value evaluatePreparedJavaScript( - const std::shared_ptr& js) = 0; - - /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue. - /// - /// \param maxMicrotasksHint a hint to tell an implementation that it should - /// make a best effort not execute more than the given number. It's default - /// to -1 for infinity (unbounded execution). - /// \return true if the queue is drained or false if there is more work to do. - /// - /// When there were exceptions thrown from the execution of microtasks, - /// implementations shall discard the exceptional jobs. An implementation may - /// \throw a \c JSError object to signal the hosts to handle. In that case, an - /// implementation may or may not suspend the draining. - /// - /// Hosts may call this function again to resume the draining if it was - /// suspended due to either exceptions or the \p maxMicrotasksHint bound. - /// E.g. a host may repetitively invoke this function until the queue is - /// drained to implement the "microtask checkpoint" defined in WHATWG HTML - /// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint. - /// - /// Note that error propagation is only a concern if a host needs to implement - /// `queueMicrotask`, a recent API that allows enqueueing arbitrary functions - /// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are - /// handled internally to VMs and are never propagated to hosts. - /// - /// This API offers some queue management to hosts at its best effort due to - /// different behaviors and limitations imposed by different VMs and APIs. By - /// the time this is written, An implementation may swallow exceptions (JSC), - /// may not pause (V8), and may not support bounded executions. - virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; - - /// \return the global object - virtual Object global() = 0; - - /// \return a short printable description of the instance. It should - /// at least include some human-readable indication of the runtime - /// implementation. This should only be used by logging, debugging, - /// and other developer-facing callers. - virtual std::string description() = 0; - - /// \return whether or not the underlying runtime supports debugging via the - /// Chrome remote debugging protocol. - /// - /// NOTE: the API for determining whether a runtime is debuggable and - /// registering a runtime with the debugger is still in flux, so please don't - /// use this API unless you know what you're doing. - virtual bool isInspectable() = 0; - - /// \return an interface to extract metrics from this \c Runtime. The default - /// implementation of this function returns an \c Instrumentation instance - /// which returns no metrics. - virtual Instrumentation& instrumentation(); - - protected: - friend class Pointer; - friend class PropNameID; - friend class Symbol; - friend class String; - friend class Object; - friend class WeakObject; - friend class Array; - friend class ArrayBuffer; - friend class Function; - friend class Value; - friend class Scope; - friend class JSError; - - // Potential optimization: avoid the cloneFoo() virtual dispatch, - // and instead just fix the number of fields, and copy them, since - // in practice they are trivially copyable. Sufficient use of - // rvalue arguments/methods would also reduce the number of clones. - - struct PointerValue { - virtual void invalidate() = 0; - - protected: - virtual ~PointerValue() = default; - }; - - virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0; - - virtual PropNameID createPropNameIDFromAscii( - const char* str, - size_t length) = 0; - virtual PropNameID createPropNameIDFromUtf8( - const uint8_t* utf8, - size_t length) = 0; - virtual PropNameID createPropNameIDFromString(const String& str) = 0; - virtual std::string utf8(const PropNameID&) = 0; - virtual bool compare(const PropNameID&, const PropNameID&) = 0; - - virtual std::string symbolToString(const Symbol&) = 0; - - virtual String createStringFromAscii(const char* str, size_t length) = 0; - virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; - virtual std::string utf8(const String&) = 0; - - // \return a \c Value created from a utf8-encoded JSON string. The default - // implementation creates a \c String and invokes JSON.parse. - virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length); - - virtual Object createObject() = 0; - virtual Object createObject(std::shared_ptr ho) = 0; - virtual std::shared_ptr getHostObject(const jsi::Object&) = 0; - virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0; - - virtual Value getProperty(const Object&, const PropNameID& name) = 0; - virtual Value getProperty(const Object&, const String& name) = 0; - virtual bool hasProperty(const Object&, const PropNameID& name) = 0; - virtual bool hasProperty(const Object&, const String& name) = 0; - virtual void - setPropertyValue(Object&, const PropNameID& name, const Value& value) = 0; - virtual void - setPropertyValue(Object&, const String& name, const Value& value) = 0; - - virtual bool isArray(const Object&) const = 0; - virtual bool isArrayBuffer(const Object&) const = 0; - virtual bool isFunction(const Object&) const = 0; - virtual bool isHostObject(const jsi::Object&) const = 0; - virtual bool isHostFunction(const jsi::Function&) const = 0; - virtual Array getPropertyNames(const Object&) = 0; - - virtual WeakObject createWeakObject(const Object&) = 0; - virtual Value lockWeakObject(WeakObject&) = 0; - - virtual Array createArray(size_t length) = 0; - virtual size_t size(const Array&) = 0; - virtual size_t size(const ArrayBuffer&) = 0; - virtual uint8_t* data(const ArrayBuffer&) = 0; - virtual Value getValueAtIndex(const Array&, size_t i) = 0; - virtual void setValueAtIndexImpl(Array&, size_t i, const Value& value) = 0; - - virtual Function createFunctionFromHostFunction( - const PropNameID& name, - unsigned int paramCount, - HostFunctionType func) = 0; - virtual Value call( - const Function&, - const Value& jsThis, - const Value* args, - size_t count) = 0; - virtual Value - callAsConstructor(const Function&, const Value* args, size_t count) = 0; - - // Private data for managing scopes. - struct ScopeState; - virtual ScopeState* pushScope(); - virtual void popScope(ScopeState*); - - virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0; - virtual bool strictEquals(const String& a, const String& b) const = 0; - virtual bool strictEquals(const Object& a, const Object& b) const = 0; - - virtual bool instanceOf(const Object& o, const Function& f) = 0; - - // These exist so derived classes can access the private parts of - // Value, Symbol, String, and Object, which are all friends of Runtime. - template - static T make(PointerValue* pv); - static PointerValue* getPointerValue(Pointer& pointer); - static const PointerValue* getPointerValue(const Pointer& pointer); - static const PointerValue* getPointerValue(const Value& value); - - friend class ::FBJSRuntime; - template - friend class RuntimeDecorator; -}; - -// Base class for pointer-storing types. -class JSI_EXPORT Pointer { - protected: - explicit Pointer(Pointer&& other) : ptr_(other.ptr_) { - other.ptr_ = nullptr; - } - - ~Pointer() { - if (ptr_) { - ptr_->invalidate(); - } - } - - Pointer& operator=(Pointer&& other); - - friend class Runtime; - friend class Value; - - explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {} - - typename Runtime::PointerValue* ptr_; -}; - -/// Represents something that can be a JS property key. Movable, not copyable. -class JSI_EXPORT PropNameID : public Pointer { - public: - using Pointer::Pointer; - - PropNameID(Runtime& runtime, const PropNameID& other) - : Pointer(runtime.clonePropNameID(other.ptr_)) {} - - PropNameID(PropNameID&& other) = default; - PropNameID& operator=(PropNameID&& other) = default; - - /// Create a JS property name id from ascii values. The data is - /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) { - return runtime.createPropNameIDFromAscii(str, length); - } - - /// Create a property name id from a nul-terminated C ascii name. The data is - /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str) { - return forAscii(runtime, str, strlen(str)); - } - - /// Create a PropNameID from a C++ string. The string is copied. - static PropNameID forAscii(Runtime& runtime, const std::string& str) { - return forAscii(runtime, str.c_str(), str.size()); - } - - /// Create a PropNameID from utf8 values. The data is copied. - /// Results are undefined if \p utf8 contains invalid code points. - static PropNameID - forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { - return runtime.createPropNameIDFromUtf8(utf8, length); - } - - /// Create a PropNameID from utf8-encoded octets stored in a - /// std::string. The string data is transformed and copied. - /// Results are undefined if \p utf8 contains invalid code points. - static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) { - return runtime.createPropNameIDFromUtf8( - reinterpret_cast(utf8.data()), utf8.size()); - } - - /// Create a PropNameID from a JS string. - static PropNameID forString(Runtime& runtime, const jsi::String& str) { - return runtime.createPropNameIDFromString(str); - } - - // Creates a vector of PropNameIDs constructed from given arguments. - template - static std::vector names(Runtime& runtime, Args&&... args); - - // Creates a vector of given PropNameIDs. - template - static std::vector names(PropNameID(&&propertyNames)[N]); - - /// Copies the data in a PropNameID as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { - return runtime.utf8(*this); - } - - static bool compare( - Runtime& runtime, - const jsi::PropNameID& a, - const jsi::PropNameID& b) { - return runtime.compare(a, b); - } - - friend class Runtime; - friend class Value; -}; - -/// Represents a JS Symbol (es6). Movable, not copyable. -/// TODO T40778724: this is a limited implementation sufficient for -/// the debugger not to crash when a Symbol is a property in an Object -/// or element in an array. Complete support for creating will come -/// later. -class JSI_EXPORT Symbol : public Pointer { - public: - using Pointer::Pointer; - - Symbol(Symbol&& other) = default; - Symbol& operator=(Symbol&& other) = default; - - /// \return whether a and b refer to the same symbol. - static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) { - return runtime.strictEquals(a, b); - } - - /// Converts a Symbol into a C++ string as JS .toString would. The output - /// will look like \c Symbol(description) . - std::string toString(Runtime& runtime) const { - return runtime.symbolToString(*this); - } - - friend class Runtime; - friend class Value; -}; - -/// Represents a JS String. Movable, not copyable. -class JSI_EXPORT String : public Pointer { - public: - using Pointer::Pointer; - - String(String&& other) = default; - String& operator=(String&& other) = default; - - /// Create a JS string from ascii values. The string data is - /// copied. - static String - createFromAscii(Runtime& runtime, const char* str, size_t length) { - return runtime.createStringFromAscii(str, length); - } - - /// Create a JS string from a nul-terminated C ascii string. The - /// string data is copied. - static String createFromAscii(Runtime& runtime, const char* str) { - return createFromAscii(runtime, str, strlen(str)); - } - - /// Create a JS string from a C++ string. The string data is - /// copied. - static String createFromAscii(Runtime& runtime, const std::string& str) { - return createFromAscii(runtime, str.c_str(), str.size()); - } - - /// Create a JS string from utf8-encoded octets. The string data is - /// transformed and copied. Results are undefined if \p utf8 contains invalid - /// code points. - static String - createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { - return runtime.createStringFromUtf8(utf8, length); - } - - /// Create a JS string from utf8-encoded octets stored in a - /// std::string. The string data is transformed and copied. Results are - /// undefined if \p utf8 contains invalid code points. - static String createFromUtf8(Runtime& runtime, const std::string& utf8) { - return runtime.createStringFromUtf8( - reinterpret_cast(utf8.data()), utf8.length()); - } - - /// \return whether a and b contain the same characters. - static bool strictEquals(Runtime& runtime, const String& a, const String& b) { - return runtime.strictEquals(a, b); - } - - /// Copies the data in a JS string as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { - return runtime.utf8(*this); - } - - friend class Runtime; - friend class Value; -}; - -class Array; -class Function; - -/// Represents a JS Object. Movable, not copyable. -class JSI_EXPORT Object : public Pointer { - public: - using Pointer::Pointer; - - Object(Object&& other) = default; - Object& operator=(Object&& other) = default; - - /// Creates a new Object instance, like '{}' in JS. - Object(Runtime& runtime) : Object(runtime.createObject()) {} - - static Object createFromHostObject( - Runtime& runtime, - std::shared_ptr ho) { - return runtime.createObject(ho); - } - - /// \return whether this and \c obj are the same JSObject or not. - static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) { - return runtime.strictEquals(a, b); - } - - /// \return the result of `this instanceOf ctor` in JS. - bool instanceOf(Runtime& rt, const Function& ctor) { - return rt.instanceOf(*this, ctor); - } - - /// \return the property of the object with the given ascii name. - /// If the name isn't a property on the object, returns the - /// undefined value. - Value getProperty(Runtime& runtime, const char* name) const; - - /// \return the property of the object with the String name. - /// If the name isn't a property on the object, returns the - /// undefined value. - Value getProperty(Runtime& runtime, const String& name) const; - - /// \return the property of the object with the given JS PropNameID - /// name. If the name isn't a property on the object, returns the - /// undefined value. - Value getProperty(Runtime& runtime, const PropNameID& name) const; - - /// \return true if and only if the object has a property with the - /// given ascii name. - bool hasProperty(Runtime& runtime, const char* name) const; - - /// \return true if and only if the object has a property with the - /// given String name. - bool hasProperty(Runtime& runtime, const String& name) const; - - /// \return true if and only if the object has a property with the - /// given PropNameID name. - bool hasProperty(Runtime& runtime, const PropNameID& name) const; - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. - template - void setProperty(Runtime& runtime, const char* name, T&& value); - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. - template - void setProperty(Runtime& runtime, const String& name, T&& value); - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. - template - void setProperty(Runtime& runtime, const PropNameID& name, T&& value); - - /// \return true iff JS \c Array.isArray() would return \c true. If - /// so, then \c getArray() will succeed. - bool isArray(Runtime& runtime) const { - return runtime.isArray(*this); - } - - /// \return true iff the Object is an ArrayBuffer. If so, then \c - /// getArrayBuffer() will succeed. - bool isArrayBuffer(Runtime& runtime) const { - return runtime.isArrayBuffer(*this); - } - - /// \return true iff the Object is callable. If so, then \c - /// getFunction will succeed. - bool isFunction(Runtime& runtime) const { - return runtime.isFunction(*this); - } - - /// \return true iff the Object was initialized with \c createFromHostObject - /// and the HostObject passed is of type \c T. If returns \c true then - /// \c getHostObject will succeed. - template - bool isHostObject(Runtime& runtime) const; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) const&; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) &&; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will throw - /// JSIException. - Array asArray(Runtime& runtime) const&; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will throw - /// JSIException. - Array asArray(Runtime& runtime) &&; - - /// \return an ArrayBuffer instance which refers to the same underlying - /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) const&; - - /// \return an ArrayBuffer instance which refers to the same underlying - /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) &&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) const&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) &&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will throw - /// JSIException. - Function asFunction(Runtime& runtime) const&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will throw - /// JSIException. - Function asFunction(Runtime& runtime) &&; - - /// \return a shared_ptr which refers to the same underlying - /// \c HostObject that was used to create this object. If \c isHostObject - /// is false, this will assert. Note that this does a type check and will - /// assert if the underlying HostObject isn't of type \c T - template - std::shared_ptr getHostObject(Runtime& runtime) const; - - /// \return a shared_ptr which refers to the same underlying - /// \c HostObject that was used to create this object. If \c isHostObject - /// is false, this will throw. - template - std::shared_ptr asHostObject(Runtime& runtime) const; - - /// \return same as \c getProperty(name).asObject(), except with - /// a better exception message. - Object getPropertyAsObject(Runtime& runtime, const char* name) const; - - /// \return similar to \c - /// getProperty(name).getObject().getFunction(), except it will - /// throw JSIException instead of asserting if the property is - /// not an object, or the object is not callable. - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; - - /// \return an Array consisting of all enumerable property names in - /// the object and its prototype chain. All values in the return - /// will be isString(). (This is probably not optimal, but it - /// works. I only need it in one place.) - Array getPropertyNames(Runtime& runtime) const; - - protected: - void - setPropertyValue(Runtime& runtime, const String& name, const Value& value) { - return runtime.setPropertyValue(*this, name, value); - } - - void setPropertyValue( - Runtime& runtime, - const PropNameID& name, - const Value& value) { - return runtime.setPropertyValue(*this, name, value); - } - - friend class Runtime; - friend class Value; -}; - -/// Represents a weak reference to a JS Object. If the only reference -/// to an Object are these, the object is eligible for GC. Method -/// names are inspired by C++ weak_ptr. Movable, not copyable. -class JSI_EXPORT WeakObject : public Pointer { - public: - using Pointer::Pointer; - - WeakObject(WeakObject&& other) = default; - WeakObject& operator=(WeakObject&& other) = default; - - /// Create a WeakObject from an Object. - WeakObject(Runtime& runtime, const Object& o) - : WeakObject(runtime.createWeakObject(o)) {} - - /// \return a Value representing the underlying Object if it is still valid; - /// otherwise returns \c undefined. Note that this method has nothing to do - /// with threads or concurrency. The name is based on std::weak_ptr::lock() - /// which serves a similar purpose. - Value lock(Runtime& runtime); - - friend class Runtime; -}; - -/// Represents a JS Object which can be efficiently used as an array -/// with integral indices. -class JSI_EXPORT Array : public Object { - public: - Array(Array&&) = default; - /// Creates a new Array instance, with \c length undefined elements. - Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {} - - Array& operator=(Array&&) = default; - - /// \return the size of the Array, according to its length property. - /// (C++ naming convention) - size_t size(Runtime& runtime) const { - return runtime.size(*this); - } - - /// \return the size of the Array, according to its length property. - /// (JS naming convention) - size_t length(Runtime& runtime) const { - return size(runtime); - } - - /// \return the property of the array at index \c i. If there is no - /// such property, returns the undefined value. If \c i is out of - /// range [ 0..\c length ] throws a JSIException. - Value getValueAtIndex(Runtime& runtime, size_t i) const; - - /// Sets the property of the array at index \c i. The argument - /// value behaves as with Object::setProperty(). If \c i is out of - /// range [ 0..\c length ] throws a JSIException. - template - void setValueAtIndex(Runtime& runtime, size_t i, T&& value); - - /// There is no current API for changing the size of an array once - /// created. We'll probably need that eventually. - - /// Creates a new Array instance from provided values - template - static Array createWithElements(Runtime&, Args&&... args); - - /// Creates a new Array instance from initializer list. - static Array createWithElements( - Runtime& runtime, - std::initializer_list elements); - - private: - friend class Object; - friend class Value; - - void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) { - return runtime.setValueAtIndexImpl(*this, i, value); - } - - Array(Runtime::PointerValue* value) : Object(value) {} -}; - -/// Represents a JSArrayBuffer -class JSI_EXPORT ArrayBuffer : public Object { - public: - ArrayBuffer(ArrayBuffer&&) = default; - ArrayBuffer& operator=(ArrayBuffer&&) = default; - - /// \return the size of the ArrayBuffer, according to its byteLength property. - /// (C++ naming convention) - size_t size(Runtime& runtime) const { - return runtime.size(*this); - } - - size_t length(Runtime& runtime) const { - return runtime.size(*this); - } - - uint8_t* data(Runtime& runtime) { - return runtime.data(*this); - } - - private: - friend class Object; - friend class Value; - - ArrayBuffer(Runtime::PointerValue* value) : Object(value) {} -}; - -/// Represents a JS Object which is guaranteed to be Callable. -class JSI_EXPORT Function : public Object { - public: - Function(Function&&) = default; - Function& operator=(Function&&) = default; - - /// Create a function which, when invoked, calls C++ code. If the - /// function throws an exception, a JS Error will be created and - /// thrown. - /// \param name the name property for the function. - /// \param paramCount the length property for the function, which - /// may not be the number of arguments the function is passed. - static Function createFromHostFunction( - Runtime& runtime, - const jsi::PropNameID& name, - unsigned int paramCount, - jsi::HostFunctionType func); - - /// Calls the function with \c count \c args. The \c this value of the JS - /// function will not be set by the C++ caller, similar to calling - /// Function.prototype.apply(undefined, args) in JS. - /// \b Note: as with Function.prototype.apply, \c this may not always be - /// \c undefined in the function itself. If the function is non-strict, - /// \c this will be set to the global object. - Value call(Runtime& runtime, const Value* args, size_t count) const; - - /// Calls the function with a \c std::initializer_list of Value - /// arguments. The \c this value of the JS function will not be set by the - /// C++ caller, similar to calling Function.prototype.apply(undefined, args) - /// in JS. - /// \b Note: as with Function.prototype.apply, \c this may not always be - /// \c undefined in the function itself. If the function is non-strict, - /// \c this will be set to the global object. - Value call(Runtime& runtime, std::initializer_list args) const; - - /// Calls the function with any number of arguments similarly to - /// Object::setProperty(). The \c this value of the JS function will not be - /// set by the C++ caller, similar to calling - /// Function.prototype.call(undefined, ...args) in JS. - /// \b Note: as with Function.prototype.call, \c this may not always be - /// \c undefined in the function itself. If the function is non-strict, - /// \c this will be set to the global object. - template - Value call(Runtime& runtime, Args&&... args) const; - - /// Calls the function with \c count \c args and \c jsThis value passed - /// as the \c this value. - Value callWithThis( - Runtime& Runtime, - const Object& jsThis, - const Value* args, - size_t count) const; - - /// Calls the function with a \c std::initializer_list of Value - /// arguments and \c jsThis passed as the \c this value. - Value callWithThis( - Runtime& runtime, - const Object& jsThis, - std::initializer_list args) const; - - /// Calls the function with any number of arguments similarly to - /// Object::setProperty(), and with \c jsThis passed as the \c this value. - template - Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args) - const; - - /// Calls the function as a constructor with \c count \c args. Equivalent - /// to calling `new Func` where `Func` is the js function reqresented by - /// this. - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) - const; - - /// Same as above `callAsConstructor`, except use an initializer_list to - /// supply the arguments. - Value callAsConstructor(Runtime& runtime, std::initializer_list args) - const; - - /// Same as above `callAsConstructor`, but automatically converts/wraps - /// any argument with a jsi Value. - template - Value callAsConstructor(Runtime& runtime, Args&&... args) const; - - /// Returns whether this was created with Function::createFromHostFunction. - /// If true then you can use getHostFunction to get the underlying - /// HostFunctionType. - bool isHostFunction(Runtime& runtime) const { - return runtime.isHostFunction(*this); - } - - /// Returns the underlying HostFunctionType iff isHostFunction returns true - /// and asserts otherwise. You can use this to use std::function<>::target - /// to get the object that was passed to create the HostFunctionType. - /// - /// Note: The reference returned is borrowed from the JS object underlying - /// \c this, and thus only lasts as long as the object underlying - /// \c this does. - HostFunctionType& getHostFunction(Runtime& runtime) const { - assert(isHostFunction(runtime)); - return runtime.getHostFunction(*this); - } - - private: - friend class Object; - friend class Value; - - Function(Runtime::PointerValue* value) : Object(value) {} -}; - -/// Represents any JS Value (undefined, null, boolean, number, symbol, -/// string, or object). Movable, or explicitly copyable (has no copy -/// ctor). -class JSI_EXPORT Value { - public: - /// Default ctor creates an \c undefined JS value. - Value() : Value(UndefinedKind) {} - - /// Creates a \c null JS value. - /* implicit */ Value(std::nullptr_t) : kind_(NullKind) {} - - /// Creates a boolean JS value. - /* implicit */ Value(bool b) : Value(BooleanKind) { - data_.boolean = b; - } - - /// Creates a number JS value. - /* implicit */ Value(double d) : Value(NumberKind) { - data_.number = d; - } - - /// Creates a number JS value. - /* implicit */ Value(int i) : Value(NumberKind) { - data_.number = i; - } - - /// Moves a Symbol, String, or Object rvalue into a new JS value. - template - /* implicit */ Value(T&& other) : Value(kindOf(other)) { - static_assert( - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value, - "Value cannot be implicitly move-constructed from this type"); - new (&data_.pointer) T(std::move(other)); - } - - /// Value("foo") will treat foo as a bool. This makes doing that a - /// compile error. - template - Value(const char*) { - static_assert( - !std::is_same::value, - "Value cannot be constructed directly from const char*"); - } - - Value(Value&& value); - - /// Copies a Symbol lvalue into a new JS value. - Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) { - new (&data_.pointer) String(runtime.cloneSymbol(sym.ptr_)); - } - - /// Copies a String lvalue into a new JS value. - Value(Runtime& runtime, const String& str) : Value(StringKind) { - new (&data_.pointer) String(runtime.cloneString(str.ptr_)); - } - - /// Copies a Object lvalue into a new JS value. - Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) { - new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_)); - } - - /// Creates a JS value from another Value lvalue. - Value(Runtime& runtime, const Value& value); - - /// Value(rt, "foo") will treat foo as a bool. This makes doing - /// that a compile error. - template - Value(Runtime&, const char*) { - static_assert( - !std::is_same::value, - "Value cannot be constructed directly from const char*"); - } - - ~Value(); - // \return the undefined \c Value. - static Value undefined() { - return Value(); - } - - // \return the null \c Value. - static Value null() { - return Value(nullptr); - } - - // \return a \c Value created from a utf8-encoded JSON string. - static Value - createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length) { - return runtime.createValueFromJsonUtf8(json, length); - } - - /// \return according to the Strict Equality Comparison algorithm, see: - /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison - static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); - - Value& operator=(Value&& other) { - this->~Value(); - new (this) Value(std::move(other)); - return *this; - } - - bool isUndefined() const { - return kind_ == UndefinedKind; - } - - bool isNull() const { - return kind_ == NullKind; - } - - bool isBool() const { - return kind_ == BooleanKind; - } - - bool isNumber() const { - return kind_ == NumberKind; - } - - bool isString() const { - return kind_ == StringKind; - } - - bool isSymbol() const { - return kind_ == SymbolKind; - } - - bool isObject() const { - return kind_ == ObjectKind; - } - - /// \return the boolean value, or asserts if not a boolean. - bool getBool() const { - assert(isBool()); - return data_.boolean; - } - - /// \return the number value, or asserts if not a number. - double getNumber() const { - assert(isNumber()); - return data_.number; - } - - /// \return the number value, or throws JSIException if not a - /// number. - double asNumber() const; - - /// \return the Symbol value, or asserts if not a symbol. - Symbol getSymbol(Runtime& runtime) const& { - assert(isSymbol()); - return Symbol(runtime.cloneSymbol(data_.pointer.ptr_)); - } - - /// \return the Symbol value, or asserts if not a symbol. - /// Can be used on rvalue references to avoid cloning more symbols. - Symbol getSymbol(Runtime&) && { - assert(isSymbol()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the Symbol value, or throws JSIException if not a - /// symbol - Symbol asSymbol(Runtime& runtime) const&; - Symbol asSymbol(Runtime& runtime) &&; - - /// \return the String value, or asserts if not a string. - String getString(Runtime& runtime) const& { - assert(isString()); - return String(runtime.cloneString(data_.pointer.ptr_)); - } - - /// \return the String value, or asserts if not a string. - /// Can be used on rvalue references to avoid cloning more strings. - String getString(Runtime&) && { - assert(isString()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the String value, or throws JSIException if not a - /// string. - String asString(Runtime& runtime) const&; - String asString(Runtime& runtime) &&; - - /// \return the Object value, or asserts if not an object. - Object getObject(Runtime& runtime) const& { - assert(isObject()); - return Object(runtime.cloneObject(data_.pointer.ptr_)); - } - - /// \return the Object value, or asserts if not an object. - /// Can be used on rvalue references to avoid cloning more objects. - Object getObject(Runtime&) && { - assert(isObject()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the Object value, or throws JSIException if not an - /// object. - Object asObject(Runtime& runtime) const&; - Object asObject(Runtime& runtime) &&; - - // \return a String like JS .toString() would do. - String toString(Runtime& runtime) const; - - private: - friend class Runtime; - - enum ValueKind { - UndefinedKind, - NullKind, - BooleanKind, - NumberKind, - SymbolKind, - StringKind, - ObjectKind, - PointerKind = SymbolKind, - }; - - union Data { - // Value's ctor and dtor will manage the lifecycle of the contained Data. - Data() { - static_assert( - sizeof(Data) == sizeof(uint64_t), - "Value data should fit in a 64-bit register"); - } - ~Data() {} - - // scalars - bool boolean; - double number; - // pointers - Pointer pointer; // Symbol, String, Object, Array, Function - }; - - Value(ValueKind kind) : kind_(kind) {} - - constexpr static ValueKind kindOf(const Symbol&) { - return SymbolKind; - } - constexpr static ValueKind kindOf(const String&) { - return StringKind; - } - constexpr static ValueKind kindOf(const Object&) { - return ObjectKind; - } - - ValueKind kind_; - Data data_; - - // In the future: Value becomes NaN-boxed. See T40538354. -}; - -/// Not movable and not copyable RAII marker advising the underlying -/// JavaScript VM to track resources allocated since creation until -/// destruction so that they can be recycled eagerly when the Scope -/// goes out of scope instead of floating in the air until the next -/// garbage collection or any other delayed release occurs. -/// -/// This API should be treated only as advice, implementations can -/// choose to ignore the fact that Scopes are created or destroyed. -/// -/// This class is an exception to the rule allowing destructors to be -/// called without proper synchronization (see Runtime documentation). -/// The whole point of this class is to enable all sorts of clean ups -/// when the destructor is called and this proper synchronization is -/// required at that time. -/// -/// Instances of this class are intended to be created as automatic stack -/// variables in which case destructor calls don't require any additional -/// locking, provided that the lock (if any) is managed with RAII helpers. -class JSI_EXPORT Scope { - public: - explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {} - ~Scope() { - rt_.popScope(prv_); - }; - - Scope(const Scope&) = delete; - Scope(Scope&&) = delete; - - Scope& operator=(const Scope&) = delete; - Scope& operator=(Scope&&) = delete; - - template - static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) { - Scope s(rt); - return f(); - } - - private: - Runtime& rt_; - Runtime::ScopeState* prv_; -}; - -/// Base class for jsi exceptions -class JSI_EXPORT JSIException : public std::exception { - protected: - JSIException(){}; - JSIException(std::string what) : what_(std::move(what)){}; - - public: - virtual const char* what() const noexcept override { - return what_.c_str(); - } - - virtual ~JSIException(); - - protected: - std::string what_; -}; - -/// This exception will be thrown by API functions on errors not related to -/// JavaScript execution. -class JSI_EXPORT JSINativeException : public JSIException { - public: - JSINativeException(std::string what) : JSIException(std::move(what)) {} - - virtual ~JSINativeException(); -}; - -/// This exception will be thrown by API functions whenever a JS -/// operation causes an exception as described by the spec, or as -/// otherwise described. -class JSI_EXPORT JSError : public JSIException { - public: - /// Creates a JSError referring to provided \c value - JSError(Runtime& r, Value&& value); - - /// Creates a JSError referring to new \c Error instance capturing current - /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, std::string message); - - /// Creates a JSError referring to new \c Error instance capturing current - /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, const char* message) - : JSError(rt, std::string(message)){}; - - /// Creates a JSError referring to a JavaScript Object having message and - /// stack properties set to provided values. - JSError(Runtime& rt, std::string message, std::string stack); - - /// Creates a JSError referring to provided value and what string - /// set to provided message. This argument order is a bit weird, - /// but necessary to avoid ambiguity with the above. - JSError(std::string what, Runtime& rt, Value&& value); - - virtual ~JSError(); - - const std::string& getStack() const { - return stack_; - } - - const std::string& getMessage() const { - return message_; - } - - const jsi::Value& value() const { - assert(value_); - return *value_; - } - - private: - // This initializes the value_ member and does some other - // validation, so it must be called by every branch through the - // constructors. - void setValue(Runtime& rt, Value&& value); - - // This needs to be on the heap, because throw requires the object - // be copyable, and Value is not. - std::shared_ptr value_; - std::string message_; - std::string stack_; -}; - -} // namespace jsi -} // namespace facebook - -#include \ No newline at end of file diff --git a/vnext/overrides.json b/vnext/overrides.json index 663c80c9c22..3cead60bbae 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -37,27 +37,6 @@ "baseHash": "010970b09aebda9348dfed9dd7b8fefb365bcc9b", "issue": 7821 }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp", - "baseFile": "ReactCommon/jsi/JSCRuntime.cpp", - "baseHash": "da73ed017a05d0fdc4302bd0794a343dc5ac935a", - "issue": 111 - }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h", - "baseFile": "ReactCommon/jsi/jsi/decorator.h", - "baseHash": "c93e4d10173716218503599610eb48f40bd2a47c", - "issue": 111 - }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h", - "baseFile": "ReactCommon/jsi/jsi/jsi.h", - "baseHash": "6ef3f675e8af4060ebd43ba75136d58932b02eb4", - "issue": 111 - }, { "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp", From 4feedf2fb98dec0a4edc3604bbaf4877a19bb1b8 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Wed, 9 Mar 2022 16:00:33 -0800 Subject: [PATCH 05/10] fixes unreferenced parameter --- vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp index dbe7222d774..420e72b9834 100644 --- a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp +++ b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp @@ -310,6 +310,7 @@ PropNameID JsiAbiRuntime::createPropNameIDFromSymbol(const Symbol &sym) { // TODO: Support for symbols through the native API in JSC is very limited. // While we could construct a PropNameID here, we would not be able to get a // symbol property through the C++ API. + UNREFERENCED_PARAMETER(sym); RethrowJsiError(); throw; } From 6ea7760a80417a6d2a0df1031b4dac6bfe46a3ac Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Wed, 9 Mar 2022 16:19:32 -0800 Subject: [PATCH 06/10] adds to function to Chakra --- vnext/Shared/JSI/ChakraRuntime.cpp | 8 ++++++++ vnext/Shared/JSI/ChakraRuntime.h | 1 + 2 files changed, 9 insertions(+) diff --git a/vnext/Shared/JSI/ChakraRuntime.cpp b/vnext/Shared/JSI/ChakraRuntime.cpp index 82170d5aefc..d7a0ba0b75b 100644 --- a/vnext/Shared/JSI/ChakraRuntime.cpp +++ b/vnext/Shared/JSI/ChakraRuntime.cpp @@ -312,6 +312,14 @@ facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromString(const facebo return MakePointer(propertyId); } +facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym) { + // TODO: Support for symbols through the native API in JSC is very limited. + // While we could construct a PropNameID here, we would not be able to get a + // symbol property through the C++ API. + UNREFERENCED_PARAMETER(sym); + throw; +} + std::string ChakraRuntime::utf8(const facebook::jsi::PropNameID &id) { return Common::Unicode::Utf16ToUtf8(GetPropertyNameFromId(GetJsRef(id))); } diff --git a/vnext/Shared/JSI/ChakraRuntime.h b/vnext/Shared/JSI/ChakraRuntime.h index 4f21438c4dd..df5d179159b 100644 --- a/vnext/Shared/JSI/ChakraRuntime.h +++ b/vnext/Shared/JSI/ChakraRuntime.h @@ -63,6 +63,7 @@ class ChakraRuntime : public facebook::jsi::Runtime, public ChakraApi, ChakraApi facebook::jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromString(const facebook::jsi::String &str) override; + facebook::jsi::PropNameID createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym); std::string utf8(const facebook::jsi::PropNameID &id) override; bool compare(const facebook::jsi::PropNameID &lhs, const facebook::jsi::PropNameID &rhs) override; From f3931d5294718f4a9f6cb734a70fc268529ca54a Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Wed, 9 Mar 2022 16:59:23 -0800 Subject: [PATCH 07/10] adds function to NodeApiJsiRuntime --- .../Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp b/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp index c46029e47a3..174809996f6 100644 --- a/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp +++ b/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp @@ -137,6 +137,7 @@ struct NapiJsiRuntime : facebook::jsi::Runtime { facebook::jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromString(const facebook::jsi::String &str) override; + facebook::jsi::PropNameID createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym); std::string utf8(const facebook::jsi::PropNameID &id) override; bool compare(const facebook::jsi::PropNameID &lhs, const facebook::jsi::PropNameID &rhs) override; @@ -711,6 +712,14 @@ PropNameID NapiJsiRuntime::createPropNameIDFromString(const String &str) { return MakePointer(uniqueStr); } +PropNameID NapiJsiRuntime::createPropNameIDFromSymbol(const Symbol &sym) { + // TODO: Support for symbols through the native API in JSC is very limited. + // While we could construct a PropNameID here, we would not be able to get a + // symbol property through the C++ API. + UNREFERENCED_PARAMETER(sym); + throw; +} + string NapiJsiRuntime::utf8(const PropNameID &id) { EnvScope scope{m_env}; From ca790c6d68aef5b8da8b738f7f4d7e4da270d236 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Wed, 9 Mar 2022 17:42:40 -0800 Subject: [PATCH 08/10] removes empty issue number --- vnext/overrides.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vnext/overrides.json b/vnext/overrides.json index 3cead60bbae..eed5bdf63f5 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -41,8 +41,7 @@ "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp", "baseFile": "ReactCommon/react/renderer/core/RawPropsParser.cpp", - "baseHash": "738a8dcf33d1e2bae57f2727625c8ca609f5fd0b", - "issue": 111 + "baseHash": "738a8dcf33d1e2bae57f2727625c8ca609f5fd0b" }, { "type": "patch", From b5b99c80180de2863b11acf224eaeed961e9ce98 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Mon, 14 Mar 2022 17:11:43 -0700 Subject: [PATCH 09/10] adds overrides for Hermes --- .../jsi/JSCRuntime.cpp | 1480 +++++++++++++++++ .../jsi/jsi/decorator.h | 753 +++++++++ .../TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h | 1321 +++++++++++++++ vnext/Shared/JSI/ChakraRuntime.cpp | 8 - vnext/Shared/JSI/ChakraRuntime.h | 1 - vnext/overrides.json | 21 + 6 files changed, 3575 insertions(+), 9 deletions(-) create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h create mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp new file mode 100644 index 00000000000..58804af1739 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp @@ -0,0 +1,1480 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "JSCRuntime.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook { +namespace jsc { + +namespace detail { +class ArgsConverter; +} // namespace detail + +class JSCRuntime; + +struct Lock { + void lock(const jsc::JSCRuntime &) const {} + void unlock(const jsc::JSCRuntime &) const {} +}; + +class JSCRuntime : public jsi::Runtime { + public: + // Creates new context in new context group + JSCRuntime(); + // Retains ctx + JSCRuntime(JSGlobalContextRef ctx); + ~JSCRuntime(); + + std::shared_ptr prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) override; + + jsi::Value evaluatePreparedJavaScript( + const std::shared_ptr &js) override; + + jsi::Value evaluateJavaScript( + const std::shared_ptr &buffer, + const std::string &sourceURL) override; + + bool drainMicrotasks(int maxMicrotasksHint = -1) override; + + jsi::Object global() override; + + std::string description() override; + + bool isInspectable() override; + + void setDescription(const std::string &desc); + + // Please don't use the following two functions, only exposed for + // integration efforts. + JSGlobalContextRef getContext() { + return ctx_; + } + + // JSValueRef->JSValue (needs make.*Value so it must be member function) + jsi::Value createValue(JSValueRef value) const; + + // Value->JSValueRef (similar to above) + JSValueRef valueRef(const jsi::Value &value); + + protected: + friend class detail::ArgsConverter; + class JSCSymbolValue final : public PointerValue { +#ifndef NDEBUG + JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym, + std::atomic &counter); +#else + JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym); +#endif + void invalidate() override; + + JSGlobalContextRef ctx_; + const std::atomic &ctxInvalid_; + // There is no C type in the JSC API to represent Symbol, so this stored + // a JSValueRef which contains the Symbol. + JSValueRef sym_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + class JSCStringValue final : public PointerValue { +#ifndef NDEBUG + JSCStringValue(JSStringRef str, std::atomic &counter); +#else + JSCStringValue(JSStringRef str); +#endif + void invalidate() override; + + JSStringRef str_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + class JSCObjectValue final : public PointerValue { + JSCObjectValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSObjectRef obj +#ifndef NDEBUG + , + std::atomic &counter +#endif + ); + + void invalidate() override; + + JSGlobalContextRef ctx_; + const std::atomic &ctxInvalid_; + JSObjectRef obj_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + PointerValue *cloneSymbol(const Runtime::PointerValue *pv) override; + PointerValue *cloneString(const Runtime::PointerValue *pv) override; + PointerValue *cloneObject(const Runtime::PointerValue *pv) override; + PointerValue *clonePropNameID(const Runtime::PointerValue *pv) override; + + jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) + override; + jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) + override; + jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override; + std::string utf8(const jsi::PropNameID &) override; + bool compare(const jsi::PropNameID &, const jsi::PropNameID &) override; + + std::string symbolToString(const jsi::Symbol &) override; + + jsi::String createStringFromAscii(const char *str, size_t length) override; + jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; + std::string utf8(const jsi::String &) override; + + jsi::Object createObject() override; + jsi::Object createObject(std::shared_ptr ho) override; + virtual std::shared_ptr getHostObject( + const jsi::Object &) override; + jsi::HostFunctionType &getHostFunction(const jsi::Function &) override; + + jsi::Value getProperty(const jsi::Object &, const jsi::String &name) override; + jsi::Value getProperty(const jsi::Object &, const jsi::PropNameID &name) + override; + bool hasProperty(const jsi::Object &, const jsi::String &name) override; + bool hasProperty(const jsi::Object &, const jsi::PropNameID &name) override; + void setPropertyValue( + jsi::Object &, + const jsi::String &name, + const jsi::Value &value) override; + void setPropertyValue( + jsi::Object &, + const jsi::PropNameID &name, + const jsi::Value &value) override; + bool isArray(const jsi::Object &) const override; + bool isArrayBuffer(const jsi::Object &) const override; + bool isFunction(const jsi::Object &) const override; + bool isHostObject(const jsi::Object &) const override; + bool isHostFunction(const jsi::Function &) const override; + jsi::Array getPropertyNames(const jsi::Object &) override; + + // TODO: revisit this implementation + jsi::WeakObject createWeakObject(const jsi::Object &) override; + jsi::Value lockWeakObject(jsi::WeakObject &) override; + + jsi::Array createArray(size_t length) override; + size_t size(const jsi::Array &) override; + size_t size(const jsi::ArrayBuffer &) override; + uint8_t *data(const jsi::ArrayBuffer &) override; + jsi::Value getValueAtIndex(const jsi::Array &, size_t i) override; + void setValueAtIndexImpl(jsi::Array &, size_t i, const jsi::Value &value) + override; + + jsi::Function createFunctionFromHostFunction( + const jsi::PropNameID &name, + unsigned int paramCount, + jsi::HostFunctionType func) override; + jsi::Value call( + const jsi::Function &, + const jsi::Value &jsThis, + const jsi::Value *args, + size_t count) override; + jsi::Value callAsConstructor( + const jsi::Function &, + const jsi::Value *args, + size_t count) override; + + bool strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const override; + bool strictEquals(const jsi::String &a, const jsi::String &b) const override; + bool strictEquals(const jsi::Object &a, const jsi::Object &b) const override; + bool instanceOf(const jsi::Object &o, const jsi::Function &f) override; + + private: + // Basically convenience casts + static JSValueRef symbolRef(const jsi::Symbol &str); + static JSStringRef stringRef(const jsi::String &str); + static JSStringRef stringRef(const jsi::PropNameID &sym); + static JSObjectRef objectRef(const jsi::Object &obj); + +#ifdef RN_FABRIC_ENABLED + static JSObjectRef objectRef(const jsi::WeakObject &obj); +#endif + + // Factory methods for creating String/Object + jsi::Symbol createSymbol(JSValueRef symbolRef) const; + jsi::String createString(JSStringRef stringRef) const; + jsi::PropNameID createPropNameID(JSStringRef stringRef); + jsi::Object createObject(JSObjectRef objectRef) const; + + // Used by factory methods and clone methods + jsi::Runtime::PointerValue *makeSymbolValue(JSValueRef sym) const; + jsi::Runtime::PointerValue *makeStringValue(JSStringRef str) const; + jsi::Runtime::PointerValue *makeObjectValue(JSObjectRef obj) const; + + void checkException(JSValueRef exc); + void checkException(JSValueRef res, JSValueRef exc); + void checkException(JSValueRef exc, const char *msg); + void checkException(JSValueRef res, JSValueRef exc, const char *msg); + + JSGlobalContextRef ctx_; + std::atomic ctxInvalid_; + std::string desc_; +#ifndef NDEBUG + mutable std::atomic objectCounter_; + mutable std::atomic symbolCounter_; + mutable std::atomic stringCounter_; +#endif +}; + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__builtin_expect) || defined(__GNUC__) +#define JSC_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) +#define JSC_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) +#else +#define JSC_LIKELY(EXPR) (EXPR) +#define JSC_UNLIKELY(EXPR) (EXPR) +#endif + +#define JSC_ASSERT(x) \ + do { \ + if (JSC_UNLIKELY(!!(x))) { \ + abort(); \ + } \ + } while (0) + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +// This takes care of watch and tvos (due to backwards compatibility in +// Availability.h +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0 +#define _JSC_FAST_IS_ARRAY +#endif +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 +#define _JSC_NO_ARRAY_BUFFERS +#endif +#endif +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11 +// Only one of these should be set for a build. If somehow that's not +// true, this will be a compile-time error and it can be resolved when +// we understand why. +#define _JSC_FAST_IS_ARRAY +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12 +#define _JSC_NO_ARRAY_BUFFERS +#endif +#endif + +// JSStringRef utilities +namespace { +std::string JSStringToSTLString(JSStringRef str) { + // Small string optimization: Avoid one heap allocation for strings that fit + // in stackBuffer.size() bytes of UTF-8 (including the null terminator). + std::array stackBuffer; + std::unique_ptr heapBuffer; + char *buffer; + // NOTE: By definition, maxBytes >= 1 since the null terminator is included. + size_t maxBytes = JSStringGetMaximumUTF8CStringSize(str); + if (maxBytes <= stackBuffer.size()) { + buffer = stackBuffer.data(); + } else { + heapBuffer = std::make_unique(maxBytes); + buffer = heapBuffer.get(); + } + size_t actualBytes = JSStringGetUTF8CString(str, buffer, maxBytes); + if (!actualBytes) { + // Happens if maxBytes == 0 (never the case here) or if str contains + // invalid UTF-16 data, since JSStringGetUTF8CString attempts a strict + // conversion. + // When converting an invalid string, JSStringGetUTF8CString writes a null + // terminator before returning. So we can reliably treat our buffer as a C + // string and return the truncated data to our caller. This is slightly + // slower than if we knew the length (like below) but better than crashing. + // TODO(T62295565): Perform a non-strict, best effort conversion of the + // full string instead, like we did before the JSI migration. + return std::string(buffer); + } + return std::string(buffer, actualBytes - 1); +} + +JSStringRef getLengthString() { + static JSStringRef length = JSStringCreateWithUTF8CString("length"); + return length; +} + +JSStringRef getNameString() { + static JSStringRef name = JSStringCreateWithUTF8CString("name"); + return name; +} + +JSStringRef getFunctionString() { + static JSStringRef func = JSStringCreateWithUTF8CString("Function"); + return func; +} + +#if !defined(_JSC_FAST_IS_ARRAY) +JSStringRef getArrayString() { + static JSStringRef array = JSStringCreateWithUTF8CString("Array"); + return array; +} + +JSStringRef getIsArrayString() { + static JSStringRef isArray = JSStringCreateWithUTF8CString("isArray"); + return isArray; +} +#endif +} // namespace + +// std::string utility +namespace { +std::string to_string(void *value) { + std::ostringstream ss; + ss << value; + return ss.str(); +} +} // namespace + +JSCRuntime::JSCRuntime() + : JSCRuntime(JSGlobalContextCreateInGroup(nullptr, nullptr)) { + JSGlobalContextRelease(ctx_); +} + +JSCRuntime::JSCRuntime(JSGlobalContextRef ctx) + : ctx_(JSGlobalContextRetain(ctx)), + ctxInvalid_(false) +#ifndef NDEBUG + , + objectCounter_(0), + stringCounter_(0) +#endif +{ +} + +JSCRuntime::~JSCRuntime() { + // On shutting down and cleaning up: when JSC is actually torn down, + // it calls JSC::Heap::lastChanceToFinalize internally which + // finalizes anything left over. But at this point, + // JSValueUnprotect() can no longer be called. We use an + // atomic to avoid unsafe unprotects happening after shutdown + // has started. + ctxInvalid_ = true; + JSGlobalContextRelease(ctx_); +#ifndef NDEBUG + assert( + objectCounter_ == 0 && "JSCRuntime destroyed with a dangling API object"); + assert( + stringCounter_ == 0 && "JSCRuntime destroyed with a dangling API string"); +#endif +} + +std::shared_ptr JSCRuntime::prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) { + return std::make_shared( + buffer, std::move(sourceURL)); +} + +jsi::Value JSCRuntime::evaluatePreparedJavaScript( + const std::shared_ptr &js) { + assert( + dynamic_cast(js.get()) && + "preparedJavaScript must be a SourceJavaScriptPreparation"); + auto sourceJs = + std::static_pointer_cast(js); + return evaluateJavaScript(sourceJs, sourceJs->sourceURL()); +} + +jsi::Value JSCRuntime::evaluateJavaScript( + const std::shared_ptr &buffer, + const std::string &sourceURL) { + std::string tmp( + reinterpret_cast(buffer->data()), buffer->size()); + JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str()); + JSStringRef sourceURLRef = nullptr; + if (!sourceURL.empty()) { + sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str()); + } + JSValueRef exc = nullptr; + JSValueRef res = + JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc); + JSStringRelease(sourceRef); + if (sourceURLRef) { + JSStringRelease(sourceURLRef); + } + checkException(res, exc); + return createValue(res); +} + +bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) { + return true; +} + +jsi::Object JSCRuntime::global() { + return createObject(JSContextGetGlobalObject(ctx_)); +} + +std::string JSCRuntime::description() { + if (desc_.empty()) { + desc_ = std::string(""; + } + return desc_; +} + +bool JSCRuntime::isInspectable() { + return false; +} + +namespace { + +bool smellsLikeES6Symbol(JSGlobalContextRef ctx, JSValueRef ref) { + // Since iOS 13, JSValueGetType will return kJSTypeSymbol + // Before: Empirically, an es6 Symbol is not an object, but its type is + // object. This makes no sense, but we'll run with it. + // https://github.com/WebKit/webkit/blob/master/Source/JavaScriptCore/API/JSValueRef.cpp#L79-L82 + + JSType type = JSValueGetType(ctx, ref); + + if (type == /* kJSTypeSymbol */ 6) { + return true; + } + + return (!JSValueIsObject(ctx, ref) && type == kJSTypeObject); +} + +} // namespace + +JSCRuntime::JSCSymbolValue::JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym +#ifndef NDEBUG + , + std::atomic &counter +#endif + ) + : ctx_(ctx), + ctxInvalid_(ctxInvalid), + sym_(sym) +#ifndef NDEBUG + , + counter_(counter) +#endif +{ + assert(smellsLikeES6Symbol(ctx_, sym_)); + JSValueProtect(ctx_, sym_); +#ifndef NDEBUG + counter_ += 1; +#endif +} + +void JSCRuntime::JSCSymbolValue::invalidate() { +#ifndef NDEBUG + counter_ -= 1; +#endif + + if (!ctxInvalid_) { + JSValueUnprotect(ctx_, sym_); + } + delete this; +} + +#ifndef NDEBUG +JSCRuntime::JSCStringValue::JSCStringValue( + JSStringRef str, + std::atomic &counter) + : str_(JSStringRetain(str)), counter_(counter) { + // Since std::atomic returns a copy instead of a reference when calling + // operator+= we must do this explicitly in the constructor + counter_ += 1; +} +#else +JSCRuntime::JSCStringValue::JSCStringValue(JSStringRef str) + : str_(JSStringRetain(str)) {} +#endif + +void JSCRuntime::JSCStringValue::invalidate() { + // These JSC{String,Object}Value objects are implicitly owned by the + // {String,Object} objects, thus when a String/Object is destructed + // the JSC{String,Object}Value should be released. +#ifndef NDEBUG + counter_ -= 1; +#endif + JSStringRelease(str_); + // Angery reaccs only + delete this; +} + +JSCRuntime::JSCObjectValue::JSCObjectValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSObjectRef obj +#ifndef NDEBUG + , + std::atomic &counter +#endif + ) + : ctx_(ctx), + ctxInvalid_(ctxInvalid), + obj_(obj) +#ifndef NDEBUG + , + counter_(counter) +#endif +{ + JSValueProtect(ctx_, obj_); +#ifndef NDEBUG + counter_ += 1; +#endif +} + +void JSCRuntime::JSCObjectValue::invalidate() { +#ifndef NDEBUG + counter_ -= 1; +#endif + // When shutting down the VM, if there is a HostObject which + // contains or otherwise owns a jsi::Object, then the final GC will + // finalize the HostObject, leading to a call to invalidate(). But + // at that point, making calls to JSValueUnprotect will crash. + // It is up to the application to make sure that any other calls to + // invalidate() happen before VM destruction; see the comment on + // jsi::Runtime. + // + // Another potential concern here is that in the non-shutdown case, + // if a HostObject is GCd, JSValueUnprotect will be called from the + // JSC finalizer. The documentation warns against this: "You must + // not call any function that may cause a garbage collection or an + // allocation of a garbage collected object from within a + // JSObjectFinalizeCallback. This includes all functions that have a + // JSContextRef parameter." However, an audit of the source code for + // JSValueUnprotect in late 2018 shows that it cannot cause + // allocation or a GC, and further, this code has not changed in + // about two years. In the future, we may choose to reintroduce the + // mechanism previously used here which uses a separate thread for + // JSValueUnprotect, in order to conform to the documented API, but + // use the "unsafe" synchronous version on iOS 11 and earlier. + + if (!ctxInvalid_) { + JSValueUnprotect(ctx_, obj_); + } + delete this; +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneSymbol( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCSymbolValue *symbol = static_cast(pv); + return makeSymbolValue(symbol->sym_); +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneString( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCStringValue *string = static_cast(pv); + return makeStringValue(string->str_); +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneObject( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCObjectValue *object = static_cast(pv); + assert( + object->ctx_ == ctx_ && + "Don't try to clone an object backed by a different Runtime"); + return makeObjectValue(object->obj_); +} + +jsi::Runtime::PointerValue *JSCRuntime::clonePropNameID( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCStringValue *string = static_cast(pv); + return makeStringValue(string->str_); +} + +jsi::PropNameID JSCRuntime::createPropNameIDFromAscii( + const char *str, + size_t length) { + // For system JSC this must is identical to a string + std::string tmp(str, length); + JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str()); + auto res = createPropNameID(strRef); + JSStringRelease(strRef); + return res; +} + +jsi::PropNameID JSCRuntime::createPropNameIDFromUtf8( + const uint8_t *utf8, + size_t length) { + std::string tmp(reinterpret_cast(utf8), length); + JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str()); + auto res = createPropNameID(strRef); + JSStringRelease(strRef); + return res; +} + +jsi::PropNameID JSCRuntime::createPropNameIDFromString(const jsi::String &str) { + return createPropNameID(stringRef(str)); +} + +std::string JSCRuntime::utf8(const jsi::PropNameID &sym) { + return JSStringToSTLString(stringRef(sym)); +} + +bool JSCRuntime::compare(const jsi::PropNameID &a, const jsi::PropNameID &b) { + return JSStringIsEqual(stringRef(a), stringRef(b)); +} + +std::string JSCRuntime::symbolToString(const jsi::Symbol &sym) { + return jsi::Value(*this, sym).toString(*this).utf8(*this); +} + +jsi::String JSCRuntime::createStringFromAscii(const char *str, size_t length) { + // Yes we end up double casting for semantic reasons (UTF8 contains ASCII, + // not the other way around) + return this->createStringFromUtf8( + reinterpret_cast(str), length); +} + +jsi::String JSCRuntime::createStringFromUtf8( + const uint8_t *str, + size_t length) { + std::string tmp(reinterpret_cast(str), length); + JSStringRef stringRef = JSStringCreateWithUTF8CString(tmp.c_str()); + auto result = createString(stringRef); + JSStringRelease(stringRef); + return result; +} + +std::string JSCRuntime::utf8(const jsi::String &str) { + return JSStringToSTLString(stringRef(str)); +} + +jsi::Object JSCRuntime::createObject() { + return createObject(static_cast(nullptr)); +} + +// HostObject details +namespace detail { +struct HostObjectProxyBase { + HostObjectProxyBase( + JSCRuntime &rt, + const std::shared_ptr &sho) + : runtime(rt), hostObject(sho) {} + + JSCRuntime &runtime; + std::shared_ptr hostObject; +}; +} // namespace detail + +namespace { +std::once_flag hostObjectClassOnceFlag; +JSClassRef hostObjectClass{}; +} // namespace + +jsi::Object JSCRuntime::createObject(std::shared_ptr ho) { + struct HostObjectProxy : public detail::HostObjectProxyBase { + static JSValueRef getProperty( + JSContextRef ctx, + JSObjectRef object, + JSStringRef propName, + JSValueRef *exception) { + auto proxy = static_cast(JSObjectGetPrivate(object)); + auto &rt = proxy->runtime; + jsi::PropNameID sym = rt.createPropNameID(propName); + jsi::Value ret; + try { + ret = proxy->hostObject->get(rt, sym); + } catch (const jsi::JSError &error) { + *exception = rt.valueRef(error.value()); + return JSValueMakeUndefined(ctx); + } catch (const std::exception &ex) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::get(propName:") + + JSStringToSTLString(propName) + std::string("): ") + + ex.what()); + *exception = rt.valueRef(excValue); + return JSValueMakeUndefined(ctx); + } catch (...) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::get(propName:") + + JSStringToSTLString(propName) + + std::string("): ")); + *exception = rt.valueRef(excValue); + return JSValueMakeUndefined(ctx); + } + return rt.valueRef(ret); + } + +#define JSC_UNUSED(x) (void)(x); + + static bool setProperty( + JSContextRef ctx, + JSObjectRef object, + JSStringRef propName, + JSValueRef value, + JSValueRef *exception) { + JSC_UNUSED(ctx); + auto proxy = static_cast(JSObjectGetPrivate(object)); + auto &rt = proxy->runtime; + jsi::PropNameID sym = rt.createPropNameID(propName); + try { + proxy->hostObject->set(rt, sym, rt.createValue(value)); + } catch (const jsi::JSError &error) { + *exception = rt.valueRef(error.value()); + return false; + } catch (const std::exception &ex) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::set(propName:") + + JSStringToSTLString(propName) + std::string("): ") + + ex.what()); + *exception = rt.valueRef(excValue); + return false; + } catch (...) { + auto excValue = + rt.global() + .getPropertyAsFunction(rt, "Error") + .call( + rt, + std::string("Exception in HostObject::set(propName:") + + JSStringToSTLString(propName) + + std::string("): ")); + *exception = rt.valueRef(excValue); + return false; + } + return true; + } + + // JSC does not provide means to communicate errors from this callback, + // so the error handling strategy is very brutal - we'll just crash + // due to noexcept. + static void getPropertyNames( + JSContextRef ctx, + JSObjectRef object, + JSPropertyNameAccumulatorRef propertyNames) noexcept { + JSC_UNUSED(ctx); + auto proxy = static_cast(JSObjectGetPrivate(object)); + auto &rt = proxy->runtime; + auto names = proxy->hostObject->getPropertyNames(rt); + for (auto &name : names) { + JSPropertyNameAccumulatorAddName(propertyNames, stringRef(name)); + } + } + +#undef JSC_UNUSED + + static void finalize(JSObjectRef obj) { + auto hostObject = static_cast(JSObjectGetPrivate(obj)); + JSObjectSetPrivate(obj, nullptr); + delete hostObject; + } + + using HostObjectProxyBase::HostObjectProxyBase; + }; + + std::call_once(hostObjectClassOnceFlag, []() { + JSClassDefinition hostObjectClassDef = kJSClassDefinitionEmpty; + hostObjectClassDef.version = 0; + hostObjectClassDef.attributes = kJSClassAttributeNoAutomaticPrototype; + hostObjectClassDef.finalize = HostObjectProxy::finalize; + hostObjectClassDef.getProperty = HostObjectProxy::getProperty; + hostObjectClassDef.setProperty = HostObjectProxy::setProperty; + hostObjectClassDef.getPropertyNames = HostObjectProxy::getPropertyNames; + hostObjectClass = JSClassCreate(&hostObjectClassDef); + }); + + JSObjectRef obj = + JSObjectMake(ctx_, hostObjectClass, new HostObjectProxy(*this, ho)); + return createObject(obj); +} + +std::shared_ptr JSCRuntime::getHostObject( + const jsi::Object &obj) { + // We are guaranteed at this point to have isHostObject(obj) == true + // so the private data should be HostObjectMetadata + JSObjectRef object = objectRef(obj); + auto metadata = + static_cast(JSObjectGetPrivate(object)); + assert(metadata); + return metadata->hostObject; +} + +jsi::Value JSCRuntime::getProperty( + const jsi::Object &obj, + const jsi::String &name) { + JSObjectRef objRef = objectRef(obj); + JSValueRef exc = nullptr; + JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc); + checkException(exc); + return createValue(res); +} + +jsi::Value JSCRuntime::getProperty( + const jsi::Object &obj, + const jsi::PropNameID &name) { + JSObjectRef objRef = objectRef(obj); + JSValueRef exc = nullptr; + JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc); + checkException(exc); + return createValue(res); +} + +bool JSCRuntime::hasProperty(const jsi::Object &obj, const jsi::String &name) { + JSObjectRef objRef = objectRef(obj); + return JSObjectHasProperty(ctx_, objRef, stringRef(name)); +} + +bool JSCRuntime::hasProperty( + const jsi::Object &obj, + const jsi::PropNameID &name) { + JSObjectRef objRef = objectRef(obj); + return JSObjectHasProperty(ctx_, objRef, stringRef(name)); +} + +void JSCRuntime::setPropertyValue( + jsi::Object &object, + const jsi::PropNameID &name, + const jsi::Value &value) { + JSValueRef exc = nullptr; + JSObjectSetProperty( + ctx_, + objectRef(object), + stringRef(name), + valueRef(value), + kJSPropertyAttributeNone, + &exc); + checkException(exc); +} + +void JSCRuntime::setPropertyValue( + jsi::Object &object, + const jsi::String &name, + const jsi::Value &value) { + JSValueRef exc = nullptr; + JSObjectSetProperty( + ctx_, + objectRef(object), + stringRef(name), + valueRef(value), + kJSPropertyAttributeNone, + &exc); + checkException(exc); +} + +bool JSCRuntime::isArray(const jsi::Object &obj) const { +#if !defined(_JSC_FAST_IS_ARRAY) + JSObjectRef global = JSContextGetGlobalObject(ctx_); + JSStringRef arrayString = getArrayString(); + JSValueRef exc = nullptr; + JSValueRef arrayCtorValue = + JSObjectGetProperty(ctx_, global, arrayString, &exc); + JSC_ASSERT(exc); + JSObjectRef arrayCtor = JSValueToObject(ctx_, arrayCtorValue, &exc); + JSC_ASSERT(exc); + JSStringRef isArrayString = getIsArrayString(); + JSValueRef isArrayValue = + JSObjectGetProperty(ctx_, arrayCtor, isArrayString, &exc); + JSC_ASSERT(exc); + JSObjectRef isArray = JSValueToObject(ctx_, isArrayValue, &exc); + JSC_ASSERT(exc); + JSValueRef arg = objectRef(obj); + JSValueRef result = + JSObjectCallAsFunction(ctx_, isArray, nullptr, 1, &arg, &exc); + JSC_ASSERT(exc); + return JSValueToBoolean(ctx_, result); +#else + return JSValueIsArray(ctx_, objectRef(obj)); +#endif +} + +bool JSCRuntime::isArrayBuffer(const jsi::Object &obj) const { +#if defined(_JSC_NO_ARRAY_BUFFERS) + throw std::runtime_error("Unsupported"); +#else + auto typedArrayType = JSValueGetTypedArrayType(ctx_, objectRef(obj), nullptr); + return typedArrayType == kJSTypedArrayTypeArrayBuffer; +#endif +} + +uint8_t *JSCRuntime::data(const jsi::ArrayBuffer &obj) { +#if defined(_JSC_NO_ARRAY_BUFFERS) + throw std::runtime_error("Unsupported"); +#else + return static_cast( + JSObjectGetArrayBufferBytesPtr(ctx_, objectRef(obj), nullptr)); +#endif +} + +size_t JSCRuntime::size(const jsi::ArrayBuffer &obj) { +#if defined(_JSC_NO_ARRAY_BUFFERS) + throw std::runtime_error("Unsupported"); +#else + return JSObjectGetArrayBufferByteLength(ctx_, objectRef(obj), nullptr); +#endif +} + +bool JSCRuntime::isFunction(const jsi::Object &obj) const { + return JSObjectIsFunction(ctx_, objectRef(obj)); +} + +bool JSCRuntime::isHostObject(const jsi::Object &obj) const { + auto cls = hostObjectClass; + return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls); +} + +// Very expensive +jsi::Array JSCRuntime::getPropertyNames(const jsi::Object &obj) { + JSPropertyNameArrayRef names = + JSObjectCopyPropertyNames(ctx_, objectRef(obj)); + size_t len = JSPropertyNameArrayGetCount(names); + // Would be better if we could create an array with explicit elements + auto result = createArray(len); + for (size_t i = 0; i < len; i++) { + JSStringRef str = JSPropertyNameArrayGetNameAtIndex(names, i); + result.setValueAtIndex(*this, i, createString(str)); + } + JSPropertyNameArrayRelease(names); + return result; +} + +jsi::WeakObject JSCRuntime::createWeakObject(const jsi::Object &obj) { +#ifdef RN_FABRIC_ENABLED + // TODO: revisit this implementation + JSObjectRef objRef = objectRef(obj); + return make(makeObjectValue(objRef)); +#else + throw std::logic_error("Not implemented"); +#endif +} + +jsi::Value JSCRuntime::lockWeakObject(jsi::WeakObject &obj) { +#ifdef RN_FABRIC_ENABLED + // TODO: revisit this implementation + JSObjectRef objRef = objectRef(obj); + return jsi::Value(createObject(objRef)); +#else + throw std::logic_error("Not implemented"); +#endif +} + +jsi::Array JSCRuntime::createArray(size_t length) { + JSValueRef exc = nullptr; + JSObjectRef obj = JSObjectMakeArray(ctx_, 0, nullptr, &exc); + checkException(obj, exc); + JSObjectSetProperty( + ctx_, + obj, + getLengthString(), + JSValueMakeNumber(ctx_, static_cast(length)), + 0, + &exc); + checkException(exc); + return createObject(obj).getArray(*this); +} + +size_t JSCRuntime::size(const jsi::Array &arr) { + return static_cast( + getProperty(arr, createPropNameID(getLengthString())).getNumber()); +} + +jsi::Value JSCRuntime::getValueAtIndex(const jsi::Array &arr, size_t i) { + JSValueRef exc = nullptr; + auto res = JSObjectGetPropertyAtIndex(ctx_, objectRef(arr), (int)i, &exc); + checkException(exc); + return createValue(res); +} + +void JSCRuntime::setValueAtIndexImpl( + jsi::Array &arr, + size_t i, + const jsi::Value &value) { + JSValueRef exc = nullptr; + JSObjectSetPropertyAtIndex( + ctx_, objectRef(arr), (int)i, valueRef(value), &exc); + checkException(exc); +} + +namespace { +std::once_flag hostFunctionClassOnceFlag; +JSClassRef hostFunctionClass{}; + +class HostFunctionProxy { + public: + HostFunctionProxy(jsi::HostFunctionType hostFunction) + : hostFunction_(hostFunction) {} + + jsi::HostFunctionType &getHostFunction() { + return hostFunction_; + } + + protected: + jsi::HostFunctionType hostFunction_; +}; +} // namespace + +jsi::Function JSCRuntime::createFunctionFromHostFunction( + const jsi::PropNameID &name, + unsigned int paramCount, + jsi::HostFunctionType func) { + class HostFunctionMetadata : public HostFunctionProxy { + public: + static void initialize(JSContextRef ctx, JSObjectRef object) { + // We need to set up the prototype chain properly here. In theory we + // could set func.prototype.prototype = Function.prototype to get the + // same result. Not sure which approach is better. + HostFunctionMetadata *metadata = + static_cast(JSObjectGetPrivate(object)); + + JSValueRef exc = nullptr; + JSObjectSetProperty( + ctx, + object, + getLengthString(), + JSValueMakeNumber(ctx, metadata->argCount), + kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | + kJSPropertyAttributeDontDelete, + &exc); + if (exc) { + // Silently fail to set length + exc = nullptr; + } + + JSStringRef name = nullptr; + std::swap(metadata->name, name); + JSObjectSetProperty( + ctx, + object, + getNameString(), + JSValueMakeString(ctx, name), + kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | + kJSPropertyAttributeDontDelete, + &exc); + JSStringRelease(name); + if (exc) { + // Silently fail to set name + exc = nullptr; + } + + JSObjectRef global = JSContextGetGlobalObject(ctx); + JSValueRef value = + JSObjectGetProperty(ctx, global, getFunctionString(), &exc); + // If we don't have Function then something bad is going on. + if (JSC_UNLIKELY(exc)) { + abort(); + } + JSObjectRef funcCtor = JSValueToObject(ctx, value, &exc); + if (!funcCtor) { + // We can't do anything if Function is not an object + return; + } + JSValueRef funcProto = JSObjectGetPrototype(ctx, funcCtor); + JSObjectSetPrototype(ctx, object, funcProto); + } + + static JSValueRef makeError(JSCRuntime &rt, const std::string &desc) { + jsi::Value value = + rt.global().getPropertyAsFunction(rt, "Error").call(rt, desc); + return rt.valueRef(value); + } + + static JSValueRef call( + JSContextRef ctx, + JSObjectRef function, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef *exception) { + HostFunctionMetadata *metadata = + static_cast(JSObjectGetPrivate(function)); + JSCRuntime &rt = *(metadata->runtime); + const unsigned maxStackArgCount = 8; + jsi::Value stackArgs[maxStackArgCount]; + std::unique_ptr heapArgs; + jsi::Value *args; + if (argumentCount > maxStackArgCount) { + heapArgs = std::make_unique(argumentCount); + for (size_t i = 0; i < argumentCount; i++) { + heapArgs[i] = rt.createValue(arguments[i]); + } + args = heapArgs.get(); + } else { + for (size_t i = 0; i < argumentCount; i++) { + stackArgs[i] = rt.createValue(arguments[i]); + } + args = stackArgs; + } + JSValueRef res; + jsi::Value thisVal(rt.createObject(thisObject)); + try { + res = rt.valueRef( + metadata->hostFunction_(rt, thisVal, args, argumentCount)); + } catch (const jsi::JSError &error) { + *exception = rt.valueRef(error.value()); + res = JSValueMakeUndefined(ctx); + } catch (const std::exception &ex) { + std::string exceptionString("Exception in HostFunction: "); + exceptionString += ex.what(); + *exception = makeError(rt, exceptionString); + res = JSValueMakeUndefined(ctx); + } catch (...) { + std::string exceptionString("Exception in HostFunction: "); + *exception = makeError(rt, exceptionString); + res = JSValueMakeUndefined(ctx); + } + return res; + } + + static void finalize(JSObjectRef object) { + HostFunctionMetadata *metadata = + static_cast(JSObjectGetPrivate(object)); + JSObjectSetPrivate(object, nullptr); + delete metadata; + } + + HostFunctionMetadata( + JSCRuntime *rt, + jsi::HostFunctionType hf, + unsigned ac, + JSStringRef n) + : HostFunctionProxy(hf), + runtime(rt), + argCount(ac), + name(JSStringRetain(n)) {} + + JSCRuntime *runtime; + unsigned argCount; + JSStringRef name; + }; + + std::call_once(hostFunctionClassOnceFlag, []() { + JSClassDefinition functionClass = kJSClassDefinitionEmpty; + functionClass.version = 0; + functionClass.attributes = kJSClassAttributeNoAutomaticPrototype; + functionClass.initialize = HostFunctionMetadata::initialize; + functionClass.finalize = HostFunctionMetadata::finalize; + functionClass.callAsFunction = HostFunctionMetadata::call; + + hostFunctionClass = JSClassCreate(&functionClass); + }); + + JSObjectRef funcRef = JSObjectMake( + ctx_, + hostFunctionClass, + new HostFunctionMetadata(this, func, paramCount, stringRef(name))); + return createObject(funcRef).getFunction(*this); +} + +namespace detail { + +class ArgsConverter { + public: + ArgsConverter(JSCRuntime &rt, const jsi::Value *args, size_t count) { + JSValueRef *destination = inline_; + if (count > maxStackArgs) { + outOfLine_ = std::make_unique(count); + destination = outOfLine_.get(); + } + + for (size_t i = 0; i < count; ++i) { + destination[i] = rt.valueRef(args[i]); + } + } + + operator JSValueRef *() { + return outOfLine_ ? outOfLine_.get() : inline_; + } + + private: + constexpr static unsigned maxStackArgs = 8; + JSValueRef inline_[maxStackArgs]; + std::unique_ptr outOfLine_; +}; +} // namespace detail + +bool JSCRuntime::isHostFunction(const jsi::Function &obj) const { + auto cls = hostFunctionClass; + return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls); +} + +jsi::HostFunctionType &JSCRuntime::getHostFunction(const jsi::Function &obj) { + // We know that isHostFunction(obj) is true here, so its safe to proceed + auto proxy = + static_cast(JSObjectGetPrivate(objectRef(obj))); + return proxy->getHostFunction(); +} + +jsi::Value JSCRuntime::call( + const jsi::Function &f, + const jsi::Value &jsThis, + const jsi::Value *args, + size_t count) { + JSValueRef exc = nullptr; + auto res = JSObjectCallAsFunction( + ctx_, + objectRef(f), + jsThis.isUndefined() ? nullptr : objectRef(jsThis.getObject(*this)), + count, + detail::ArgsConverter(*this, args, count), + &exc); + checkException(exc); + return createValue(res); +} + +jsi::Value JSCRuntime::callAsConstructor( + const jsi::Function &f, + const jsi::Value *args, + size_t count) { + JSValueRef exc = nullptr; + auto res = JSObjectCallAsConstructor( + ctx_, + objectRef(f), + count, + detail::ArgsConverter(*this, args, count), + &exc); + checkException(exc); + return createValue(res); +} + +bool JSCRuntime::strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) + const { + JSValueRef exc = nullptr; + bool ret = JSValueIsEqual(ctx_, symbolRef(a), symbolRef(b), &exc); + const_cast(this)->checkException(exc); + return ret; +} + +bool JSCRuntime::strictEquals(const jsi::String &a, const jsi::String &b) + const { + return JSStringIsEqual(stringRef(a), stringRef(b)); +} + +bool JSCRuntime::strictEquals(const jsi::Object &a, const jsi::Object &b) + const { + return objectRef(a) == objectRef(b); +} + +bool JSCRuntime::instanceOf(const jsi::Object &o, const jsi::Function &f) { + JSValueRef exc = nullptr; + bool res = + JSValueIsInstanceOfConstructor(ctx_, objectRef(o), objectRef(f), &exc); + checkException(exc); + return res; +} + +jsi::Runtime::PointerValue *JSCRuntime::makeSymbolValue( + JSValueRef symbolRef) const { +#ifndef NDEBUG + return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef, symbolCounter_); +#else + return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef); +#endif +} + +namespace { +JSStringRef getEmptyString() { + static JSStringRef empty = JSStringCreateWithUTF8CString(""); + return empty; +} +} // namespace + +jsi::Runtime::PointerValue *JSCRuntime::makeStringValue( + JSStringRef stringRef) const { + if (!stringRef) { + stringRef = getEmptyString(); + } +#ifndef NDEBUG + return new JSCStringValue(stringRef, stringCounter_); +#else + return new JSCStringValue(stringRef); +#endif +} + +jsi::Symbol JSCRuntime::createSymbol(JSValueRef sym) const { + return make(makeSymbolValue(sym)); +} + +jsi::String JSCRuntime::createString(JSStringRef str) const { + return make(makeStringValue(str)); +} + +jsi::PropNameID JSCRuntime::createPropNameID(JSStringRef str) { + return make(makeStringValue(str)); +} + +jsi::Runtime::PointerValue *JSCRuntime::makeObjectValue( + JSObjectRef objectRef) const { + if (!objectRef) { + objectRef = JSObjectMake(ctx_, nullptr, nullptr); + } +#ifndef NDEBUG + return new JSCObjectValue(ctx_, ctxInvalid_, objectRef, objectCounter_); +#else + return new JSCObjectValue(ctx_, ctxInvalid_, objectRef); +#endif +} + +jsi::Object JSCRuntime::createObject(JSObjectRef obj) const { + return make(makeObjectValue(obj)); +} + +jsi::Value JSCRuntime::createValue(JSValueRef value) const { + JSType type = JSValueGetType(ctx_, value); + + switch (type) { + case kJSTypeNumber: + return jsi::Value(JSValueToNumber(ctx_, value, nullptr)); + case kJSTypeBoolean: + return jsi::Value(JSValueToBoolean(ctx_, value)); + case kJSTypeNull: + return jsi::Value(nullptr); + case kJSTypeUndefined: + return jsi::Value(); + case kJSTypeString: { + JSStringRef str = JSValueToStringCopy(ctx_, value, nullptr); + auto result = jsi::Value(createString(str)); + JSStringRelease(str); + return result; + } + case kJSTypeObject: { + JSObjectRef objRef = JSValueToObject(ctx_, value, nullptr); + return jsi::Value(createObject(objRef)); + } + // TODO: Uncomment this when all supported JSC versions have this symbol + // case kJSTypeSymbol: + default: { + if (smellsLikeES6Symbol(ctx_, value)) { + return jsi::Value(createSymbol(value)); + } else { + // WHAT ARE YOU + abort(); + } + } + } +} + +JSValueRef JSCRuntime::valueRef(const jsi::Value &value) { + // I would rather switch on value.kind_ + if (value.isUndefined()) { + return JSValueMakeUndefined(ctx_); + } else if (value.isNull()) { + return JSValueMakeNull(ctx_); + } else if (value.isBool()) { + return JSValueMakeBoolean(ctx_, value.getBool()); + } else if (value.isNumber()) { + return JSValueMakeNumber(ctx_, value.getNumber()); + } else if (value.isSymbol()) { + return symbolRef(value.getSymbol(*this)); + } else if (value.isString()) { + return JSValueMakeString(ctx_, stringRef(value.getString(*this))); + } else if (value.isObject()) { + return objectRef(value.getObject(*this)); + } else { + // What are you? + abort(); + } +} + +JSValueRef JSCRuntime::symbolRef(const jsi::Symbol &sym) { + return static_cast(getPointerValue(sym))->sym_; +} + +JSStringRef JSCRuntime::stringRef(const jsi::String &str) { + return static_cast(getPointerValue(str))->str_; +} + +JSStringRef JSCRuntime::stringRef(const jsi::PropNameID &sym) { + return static_cast(getPointerValue(sym))->str_; +} + +JSObjectRef JSCRuntime::objectRef(const jsi::Object &obj) { + return static_cast(getPointerValue(obj))->obj_; +} + +#ifdef RN_FABRIC_ENABLED +JSObjectRef JSCRuntime::objectRef(const jsi::WeakObject &obj) { + // TODO: revisit this implementation + return static_cast(getPointerValue(obj))->obj_; +} +#endif + +void JSCRuntime::checkException(JSValueRef exc) { + if (JSC_UNLIKELY(exc)) { + throw jsi::JSError(*this, createValue(exc)); + } +} + +void JSCRuntime::checkException(JSValueRef res, JSValueRef exc) { + if (JSC_UNLIKELY(!res)) { + throw jsi::JSError(*this, createValue(exc)); + } +} + +void JSCRuntime::checkException(JSValueRef exc, const char *msg) { + if (JSC_UNLIKELY(exc)) { + throw jsi::JSError(std::string(msg), *this, createValue(exc)); + } +} + +void JSCRuntime::checkException( + JSValueRef res, + JSValueRef exc, + const char *msg) { + if (JSC_UNLIKELY(!res)) { + throw jsi::JSError(std::string(msg), *this, createValue(exc)); + } +} + +std::unique_ptr makeJSCRuntime() { + return std::make_unique(); +} + +} // namespace jsc +} // namespace facebook diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h new file mode 100644 index 00000000000..a3619544459 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h @@ -0,0 +1,753 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include + +// This file contains objects to help API users create their own +// runtime adapters, i.e. if you want to compose runtimes to add your +// own behavior. + +namespace facebook { +namespace jsi { + +// Use this to wrap host functions. It will pass the member runtime as +// the first arg to the callback. The first argument to the ctor +// should be the decorated runtime, not the plain one. +class DecoratedHostFunction { + public: + DecoratedHostFunction(Runtime& drt, HostFunctionType plainHF) + : drt_(drt), plainHF_(std::move(plainHF)) {} + + Runtime& decoratedRuntime() { + return drt_; + } + + Value + operator()(Runtime&, const Value& thisVal, const Value* args, size_t count) { + return plainHF_(decoratedRuntime(), thisVal, args, count); + } + + private: + template + friend class RuntimeDecorator; + + Runtime& drt_; + HostFunctionType plainHF_; +}; + +// From the perspective of the caller, a plain HostObject is passed to +// the decorated Runtime, and the HostObject methods expect to get +// passed that Runtime. But the plain Runtime will pass itself to its +// callback, so we need a helper here which curries the decorated +// Runtime, and calls the plain HostObject with it. +// +// If the concrete RuntimeDecorator derives DecoratedHostObject, it +// should call the base class get() and set() to invoke the plain +// HostObject functionality. The Runtime& it passes does not matter, +// as it is not used. +class DecoratedHostObject : public HostObject { + public: + DecoratedHostObject(Runtime& drt, std::shared_ptr plainHO) + : drt_(drt), plainHO_(plainHO) {} + + // The derived class methods can call this to get a reference to the + // decorated runtime, since the rt passed to the callback will be + // the plain runtime. + Runtime& decoratedRuntime() { + return drt_; + } + + Value get(Runtime&, const PropNameID& name) override { + return plainHO_->get(decoratedRuntime(), name); + } + + void set(Runtime&, const PropNameID& name, const Value& value) override { + plainHO_->set(decoratedRuntime(), name, value); + } + + std::vector getPropertyNames(Runtime&) override { + return plainHO_->getPropertyNames(decoratedRuntime()); + } + + private: + template + friend class RuntimeDecorator; + + Runtime& drt_; + std::shared_ptr plainHO_; +}; + +/// C++ variant on a standard Decorator pattern, using template +/// parameters. The \c Plain template parameter type is the +/// undecorated Runtime type. You can usually use \c Runtime here, +/// but if you know the concrete type ahead of time and it's final, +/// the compiler can devirtualize calls to the decorated +/// implementation. The \c Base template parameter type will be used +/// as the base class of the decorated type. Here, too, you can +/// usually use \c Runtime, but if you want the decorated type to +/// implement a derived class of Runtime, you can specify that here. +/// For an example, see threadsafe.h. +template +class RuntimeDecorator : public Base, private jsi::Instrumentation { + public: + Plain& plain() { + static_assert( + std::is_base_of::value, + "RuntimeDecorator's Plain type must derive from jsi::Runtime"); + static_assert( + std::is_base_of::value, + "RuntimeDecorator's Base type must derive from jsi::Runtime"); + return plain_; + } + const Plain& plain() const { + return plain_; + } + + Value evaluateJavaScript( + const std::shared_ptr& buffer, + const std::string& sourceURL) override { + return plain().evaluateJavaScript(buffer, sourceURL); + } + std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) override { + return plain().prepareJavaScript(buffer, std::move(sourceURL)); + } + Value evaluatePreparedJavaScript( + const std::shared_ptr& js) override { + return plain().evaluatePreparedJavaScript(js); + } + bool drainMicrotasks(int maxMicrotasksHint) override { + return plain().drainMicrotasks(maxMicrotasksHint); + } + Object global() override { + return plain().global(); + } + std::string description() override { + return plain().description(); + }; + bool isInspectable() override { + return plain().isInspectable(); + }; + Instrumentation& instrumentation() override { + return *this; + } + + protected: + // plain is generally going to be a reference to an object managed + // by a derived class. We cache it here so this class can be + // concrete, and avoid making virtual calls to find the plain + // Runtime. Note that the ctor and dtor do not access through the + // reference, so passing a reference to an object before its + // lifetime has started is ok. + RuntimeDecorator(Plain& plain) : plain_(plain) {} + + Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { + return plain_.cloneSymbol(pv); + }; + Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { + return plain_.cloneString(pv); + }; + Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { + return plain_.cloneObject(pv); + }; + Runtime::PointerValue* clonePropNameID( + const Runtime::PointerValue* pv) override { + return plain_.clonePropNameID(pv); + }; + + PropNameID createPropNameIDFromAscii(const char* str, size_t length) + override { + return plain_.createPropNameIDFromAscii(str, length); + }; + PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) + override { + return plain_.createPropNameIDFromUtf8(utf8, length); + }; + PropNameID createPropNameIDFromString(const String& str) override { + return plain_.createPropNameIDFromString(str); + }; + std::string utf8(const PropNameID& id) override { + return plain_.utf8(id); + }; + bool compare(const PropNameID& a, const PropNameID& b) override { + return plain_.compare(a, b); + }; + + std::string symbolToString(const Symbol& sym) override { + return plain_.symbolToString(sym); + } + + String createStringFromAscii(const char* str, size_t length) override { + return plain_.createStringFromAscii(str, length); + }; + String createStringFromUtf8(const uint8_t* utf8, size_t length) override { + return plain_.createStringFromUtf8(utf8, length); + }; + std::string utf8(const String& s) override { + return plain_.utf8(s); + } + + Object createObject() override { + return plain_.createObject(); + }; + + Object createObject(std::shared_ptr ho) override { + return plain_.createObject( + std::make_shared(*this, std::move(ho))); + }; + std::shared_ptr getHostObject(const jsi::Object& o) override { + std::shared_ptr dho = plain_.getHostObject(o); + return static_cast(*dho).plainHO_; + }; + HostFunctionType& getHostFunction(const jsi::Function& f) override { + HostFunctionType& dhf = plain_.getHostFunction(f); + // This will fail if a cpp file including this header is not compiled + // with RTTI. + return dhf.target()->plainHF_; + }; + + Value getProperty(const Object& o, const PropNameID& name) override { + return plain_.getProperty(o, name); + }; + Value getProperty(const Object& o, const String& name) override { + return plain_.getProperty(o, name); + }; + bool hasProperty(const Object& o, const PropNameID& name) override { + return plain_.hasProperty(o, name); + }; + bool hasProperty(const Object& o, const String& name) override { + return plain_.hasProperty(o, name); + }; + void setPropertyValue(Object& o, const PropNameID& name, const Value& value) + override { + plain_.setPropertyValue(o, name, value); + }; + void setPropertyValue(Object& o, const String& name, const Value& value) + override { + plain_.setPropertyValue(o, name, value); + }; + + bool isArray(const Object& o) const override { + return plain_.isArray(o); + }; + bool isArrayBuffer(const Object& o) const override { + return plain_.isArrayBuffer(o); + }; + bool isFunction(const Object& o) const override { + return plain_.isFunction(o); + }; + bool isHostObject(const jsi::Object& o) const override { + return plain_.isHostObject(o); + }; + bool isHostFunction(const jsi::Function& f) const override { + return plain_.isHostFunction(f); + }; + Array getPropertyNames(const Object& o) override { + return plain_.getPropertyNames(o); + }; + + WeakObject createWeakObject(const Object& o) override { + return plain_.createWeakObject(o); + }; + Value lockWeakObject(WeakObject& wo) override { + return plain_.lockWeakObject(wo); + }; + + Array createArray(size_t length) override { + return plain_.createArray(length); + }; + size_t size(const Array& a) override { + return plain_.size(a); + }; + size_t size(const ArrayBuffer& ab) override { + return plain_.size(ab); + }; + uint8_t* data(const ArrayBuffer& ab) override { + return plain_.data(ab); + }; + Value getValueAtIndex(const Array& a, size_t i) override { + return plain_.getValueAtIndex(a, i); + }; + void setValueAtIndexImpl(Array& a, size_t i, const Value& value) override { + plain_.setValueAtIndexImpl(a, i, value); + }; + + Function createFunctionFromHostFunction( + const PropNameID& name, + unsigned int paramCount, + HostFunctionType func) override { + return plain_.createFunctionFromHostFunction( + name, paramCount, DecoratedHostFunction(*this, std::move(func))); + }; + Value call( + const Function& f, + const Value& jsThis, + const Value* args, + size_t count) override { + return plain_.call(f, jsThis, args, count); + }; + Value callAsConstructor(const Function& f, const Value* args, size_t count) + override { + return plain_.callAsConstructor(f, args, count); + }; + + // Private data for managing scopes. + Runtime::ScopeState* pushScope() override { + return plain_.pushScope(); + } + void popScope(Runtime::ScopeState* ss) override { + plain_.popScope(ss); + } + + bool strictEquals(const Symbol& a, const Symbol& b) const override { + return plain_.strictEquals(a, b); + }; + bool strictEquals(const String& a, const String& b) const override { + return plain_.strictEquals(a, b); + }; + bool strictEquals(const Object& a, const Object& b) const override { + return plain_.strictEquals(a, b); + }; + + bool instanceOf(const Object& o, const Function& f) override { + return plain_.instanceOf(o, f); + }; + + // jsi::Instrumentation methods + + std::string getRecordedGCStats() override { + return plain().instrumentation().getRecordedGCStats(); + } + + std::unordered_map getHeapInfo( + bool includeExpensive) override { + return plain().instrumentation().getHeapInfo(includeExpensive); + } + + void collectGarbage(std::string cause) override { + plain().instrumentation().collectGarbage(std::move(cause)); + } + + void startTrackingHeapObjectStackTraces( + std::function)> callback) override { + plain().instrumentation().startTrackingHeapObjectStackTraces( + std::move(callback)); + } + + void stopTrackingHeapObjectStackTraces() override { + plain().instrumentation().stopTrackingHeapObjectStackTraces(); + } + + void startHeapSampling(size_t samplingInterval) override { + plain().instrumentation().startHeapSampling(samplingInterval); + } + + void stopHeapSampling(std::ostream& os) override { + plain().instrumentation().stopHeapSampling(os); + } + + void createSnapshotToFile(const std::string& path) override { + plain().instrumentation().createSnapshotToFile(path); + } + + void createSnapshotToStream(std::ostream& os) override { + plain().instrumentation().createSnapshotToStream(os); + } + + std::string flushAndDisableBridgeTrafficTrace() override { + return const_cast(plain()) + .instrumentation() + .flushAndDisableBridgeTrafficTrace(); + } + + void writeBasicBlockProfileTraceToFile( + const std::string& fileName) const override { + const_cast(plain()) + .instrumentation() + .writeBasicBlockProfileTraceToFile(fileName); + } + + /// Dump external profiler symbols to the given file name. + void dumpProfilerSymbolsToFile(const std::string& fileName) const override { + const_cast(plain()).instrumentation().dumpProfilerSymbolsToFile( + fileName); + } + + private: + Plain& plain_; +}; + +namespace detail { + +// This metaprogramming allows the With type's methods to be +// optional. + +template +struct BeforeCaller { + static void before(T&) {} +}; + +template +struct AfterCaller { + static void after(T&) {} +}; + +// decltype((void)&...) is either SFINAE, or void. +// So, if SFINAE does not happen for T, then this specialization exists +// for BeforeCaller, and always applies. If not, only the +// default above exists, and that is used instead. +template +struct BeforeCaller { + static void before(T& t) { + t.before(); + } +}; + +template +struct AfterCaller { + static void after(T& t) { + t.after(); + } +}; + +// It's possible to use multiple decorators by nesting +// WithRuntimeDecorator<...>, but this specialization allows use of +// std::tuple of decorator classes instead. See testlib.cpp for an +// example. +template +struct BeforeCaller> { + static void before(std::tuple& tuple) { + all_before<0, T...>(tuple); + } + + private: + template + static void all_before(std::tuple& tuple) { + detail::BeforeCaller::before(std::get(tuple)); + all_before(tuple); + } + + template + static void all_before(std::tuple&) {} +}; + +template +struct AfterCaller> { + static void after(std::tuple& tuple) { + all_after<0, T...>(tuple); + } + + private: + template + static void all_after(std::tuple& tuple) { + all_after(tuple); + detail::AfterCaller::after(std::get(tuple)); + } + + template + static void all_after(std::tuple&) {} +}; + +} // namespace detail + +// A decorator which implements an around idiom. A With instance is +// RAII constructed before each call to the undecorated class; the +// ctor is passed a single argument of type WithArg&. Plain and Base +// are used as in the base class. +template +class WithRuntimeDecorator : public RuntimeDecorator { + public: + using RD = RuntimeDecorator; + + // The reference arguments to the ctor are stored, but not used by + // the ctor, and there is no ctor, so they can be passed members of + // the derived class. + WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {} + + Value evaluateJavaScript( + const std::shared_ptr& buffer, + const std::string& sourceURL) override { + Around around{with_}; + return RD::evaluateJavaScript(buffer, sourceURL); + } + std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) override { + Around around{with_}; + return RD::prepareJavaScript(buffer, std::move(sourceURL)); + } + Value evaluatePreparedJavaScript( + const std::shared_ptr& js) override { + Around around{with_}; + return RD::evaluatePreparedJavaScript(js); + } + bool drainMicrotasks(int maxMicrotasksHint) override { + Around around{with_}; + return RD::drainMicrotasks(maxMicrotasksHint); + } + Object global() override { + Around around{with_}; + return RD::global(); + } + std::string description() override { + Around around{with_}; + return RD::description(); + }; + bool isInspectable() override { + Around around{with_}; + return RD::isInspectable(); + }; + + // The jsi:: prefix is necessary because MSVC compiler complains C2247: + // Instrumentation is not accessible because RuntimeDecorator uses private + // to inherit from Instrumentation. + // TODO(T40821815) Consider removing this workaround when updating MSVC + jsi::Instrumentation& instrumentation() override { + Around around{with_}; + return RD::instrumentation(); + } + + protected: + Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::cloneSymbol(pv); + }; + Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::cloneString(pv); + }; + Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::cloneObject(pv); + }; + Runtime::PointerValue* clonePropNameID( + const Runtime::PointerValue* pv) override { + Around around{with_}; + return RD::clonePropNameID(pv); + }; + + PropNameID createPropNameIDFromAscii(const char* str, size_t length) + override { + Around around{with_}; + return RD::createPropNameIDFromAscii(str, length); + }; + PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) + override { + Around around{with_}; + return RD::createPropNameIDFromUtf8(utf8, length); + }; + PropNameID createPropNameIDFromString(const String& str) override { + Around around{with_}; + return RD::createPropNameIDFromString(str); + }; + std::string utf8(const PropNameID& id) override { + Around around{with_}; + return RD::utf8(id); + }; + bool compare(const PropNameID& a, const PropNameID& b) override { + Around around{with_}; + return RD::compare(a, b); + }; + + std::string symbolToString(const Symbol& sym) override { + Around around{with_}; + return RD::symbolToString(sym); + }; + + String createStringFromAscii(const char* str, size_t length) override { + Around around{with_}; + return RD::createStringFromAscii(str, length); + }; + String createStringFromUtf8(const uint8_t* utf8, size_t length) override { + Around around{with_}; + return RD::createStringFromUtf8(utf8, length); + }; + std::string utf8(const String& s) override { + Around around{with_}; + return RD::utf8(s); + } + + Object createObject() override { + Around around{with_}; + return RD::createObject(); + }; + Object createObject(std::shared_ptr ho) override { + Around around{with_}; + return RD::createObject(std::move(ho)); + }; + std::shared_ptr getHostObject(const jsi::Object& o) override { + Around around{with_}; + return RD::getHostObject(o); + }; + HostFunctionType& getHostFunction(const jsi::Function& f) override { + Around around{with_}; + return RD::getHostFunction(f); + }; + + Value getProperty(const Object& o, const PropNameID& name) override { + Around around{with_}; + return RD::getProperty(o, name); + }; + Value getProperty(const Object& o, const String& name) override { + Around around{with_}; + return RD::getProperty(o, name); + }; + bool hasProperty(const Object& o, const PropNameID& name) override { + Around around{with_}; + return RD::hasProperty(o, name); + }; + bool hasProperty(const Object& o, const String& name) override { + Around around{with_}; + return RD::hasProperty(o, name); + }; + void setPropertyValue(Object& o, const PropNameID& name, const Value& value) + override { + Around around{with_}; + RD::setPropertyValue(o, name, value); + }; + void setPropertyValue(Object& o, const String& name, const Value& value) + override { + Around around{with_}; + RD::setPropertyValue(o, name, value); + }; + + bool isArray(const Object& o) const override { + Around around{with_}; + return RD::isArray(o); + }; + bool isArrayBuffer(const Object& o) const override { + Around around{with_}; + return RD::isArrayBuffer(o); + }; + bool isFunction(const Object& o) const override { + Around around{with_}; + return RD::isFunction(o); + }; + bool isHostObject(const jsi::Object& o) const override { + Around around{with_}; + return RD::isHostObject(o); + }; + bool isHostFunction(const jsi::Function& f) const override { + Around around{with_}; + return RD::isHostFunction(f); + }; + Array getPropertyNames(const Object& o) override { + Around around{with_}; + return RD::getPropertyNames(o); + }; + + WeakObject createWeakObject(const Object& o) override { + Around around{with_}; + return RD::createWeakObject(o); + }; + Value lockWeakObject(WeakObject& wo) override { + Around around{with_}; + return RD::lockWeakObject(wo); + }; + + Array createArray(size_t length) override { + Around around{with_}; + return RD::createArray(length); + }; + size_t size(const Array& a) override { + Around around{with_}; + return RD::size(a); + }; + size_t size(const ArrayBuffer& ab) override { + Around around{with_}; + return RD::size(ab); + }; + uint8_t* data(const ArrayBuffer& ab) override { + Around around{with_}; + return RD::data(ab); + }; + Value getValueAtIndex(const Array& a, size_t i) override { + Around around{with_}; + return RD::getValueAtIndex(a, i); + }; + void setValueAtIndexImpl(Array& a, size_t i, const Value& value) override { + Around around{with_}; + RD::setValueAtIndexImpl(a, i, value); + }; + + Function createFunctionFromHostFunction( + const PropNameID& name, + unsigned int paramCount, + HostFunctionType func) override { + Around around{with_}; + return RD::createFunctionFromHostFunction( + name, paramCount, std::move(func)); + }; + Value call( + const Function& f, + const Value& jsThis, + const Value* args, + size_t count) override { + Around around{with_}; + return RD::call(f, jsThis, args, count); + }; + Value callAsConstructor(const Function& f, const Value* args, size_t count) + override { + Around around{with_}; + return RD::callAsConstructor(f, args, count); + }; + + // Private data for managing scopes. + Runtime::ScopeState* pushScope() override { + Around around{with_}; + return RD::pushScope(); + } + void popScope(Runtime::ScopeState* ss) override { + Around around{with_}; + RD::popScope(ss); + } + + bool strictEquals(const Symbol& a, const Symbol& b) const override { + Around around{with_}; + return RD::strictEquals(a, b); + }; + bool strictEquals(const String& a, const String& b) const override { + Around around{with_}; + return RD::strictEquals(a, b); + }; + bool strictEquals(const Object& a, const Object& b) const override { + Around around{with_}; + return RD::strictEquals(a, b); + }; + + bool instanceOf(const Object& o, const Function& f) override { + Around around{with_}; + return RD::instanceOf(o, f); + }; + + private: + // Wrap an RAII type around With& to guarantee after always happens. + struct Around { + Around(With& with) : with_(with) { + detail::BeforeCaller::before(with_); + } + ~Around() { + detail::AfterCaller::after(with_); + } + + With& with_; + }; + + With& with_; +}; + +} // namespace jsi +} // namespace facebook diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h new file mode 100644 index 00000000000..55b9e48c432 --- /dev/null +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h @@ -0,0 +1,1321 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#ifndef JSI_EXPORT +#ifdef _MSC_VER +#ifdef CREATE_SHARED_LIBRARY +#define JSI_EXPORT __declspec(dllexport) +#else +#define JSI_EXPORT +#endif // CREATE_SHARED_LIBRARY +#else // _MSC_VER +#define JSI_EXPORT __attribute__((visibility("default"))) +#endif // _MSC_VER +#endif // !defined(JSI_EXPORT) + +class FBJSRuntime; +namespace facebook { +namespace jsi { + +class JSI_EXPORT Buffer { + public: + virtual ~Buffer(); + virtual size_t size() const = 0; + virtual const uint8_t* data() const = 0; +}; + +class JSI_EXPORT StringBuffer : public Buffer { + public: + StringBuffer(std::string s) : s_(std::move(s)) {} + size_t size() const override { + return s_.size(); + } + const uint8_t* data() const override { + return reinterpret_cast(s_.data()); + } + + private: + std::string s_; +}; + +/// PreparedJavaScript is a base class representing JavaScript which is in a +/// form optimized for execution, in a runtime-specific way. Construct one via +/// jsi::Runtime::prepareJavaScript(). +/// ** This is an experimental API that is subject to change. ** +class JSI_EXPORT PreparedJavaScript { + protected: + PreparedJavaScript() = default; + + public: + virtual ~PreparedJavaScript() = 0; +}; + +class Runtime; +class Pointer; +class PropNameID; +class Symbol; +class String; +class Object; +class WeakObject; +class Array; +class ArrayBuffer; +class Function; +class Value; +class Instrumentation; +class Scope; +class JSIException; +class JSError; + +/// A function which has this type can be registered as a function +/// callable from JavaScript using Function::createFromHostFunction(). +/// When the function is called, args will point to the arguments, and +/// count will indicate how many arguments are passed. The function +/// can return a Value to the caller, or throw an exception. If a C++ +/// exception is thrown, a JS Error will be created and thrown into +/// JS; if the C++ exception extends std::exception, the Error's +/// message will be whatever what() returns. Note that it is undefined whether +/// HostFunctions may or may not be called in strict mode; that is `thisVal` +/// can be any value - it will not necessarily be coerced to an object or +/// or set to the global object. +using HostFunctionType = std::function< + Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>; + +/// An object which implements this interface can be registered as an +/// Object with the JS runtime. +class JSI_EXPORT HostObject { + public: + // The C++ object's dtor will be called when the GC finalizes this + // object. (This may be as late as when the Runtime is shut down.) + // You have no control over which thread it is called on. This will + // be called from inside the GC, so it is unsafe to do any VM + // operations which require a Runtime&. Derived classes' dtors + // should also avoid doing anything expensive. Calling the dtor on + // a jsi object is explicitly ok. If you want to do JS operations, + // or any nontrivial work, you should add it to a work queue, and + // manage it externally. + virtual ~HostObject(); + + // When JS wants a property with a given name from the HostObject, + // it will call this method. If it throws an exception, the call + // will throw a JS \c Error object. By default this returns undefined. + // \return the value for the property. + virtual Value get(Runtime&, const PropNameID& name); + + // When JS wants to set a property with a given name on the HostObject, + // it will call this method. If it throws an exception, the call will + // throw a JS \c Error object. By default this throws a type error exception + // mimicking the behavior of a frozen object in strict mode. + virtual void set(Runtime&, const PropNameID& name, const Value& value); + + // When JS wants a list of property names for the HostObject, it will + // call this method. If it throws an exception, the call will throw a + // JS \c Error object. The default implementation returns empty vector. + virtual std::vector getPropertyNames(Runtime& rt); +}; + +/// Represents a JS runtime. Movable, but not copyable. Note that +/// this object may not be thread-aware, but cannot be used safely from +/// multiple threads at once. The application is responsible for +/// ensuring that it is used safely. This could mean using the +/// Runtime from a single thread, using a mutex, doing all work on a +/// serial queue, etc. This restriction applies to the methods of +/// this class, and any method in the API which take a Runtime& as an +/// argument. Destructors (all but ~Scope), operators, or other methods +/// which do not take Runtime& as an argument are safe to call from any +/// thread, but it is still forbidden to make write operations on a single +/// instance of any class from more than one thread. In addition, to +/// make shutdown safe, destruction of objects associated with the Runtime +/// must be destroyed before the Runtime is destroyed, or from the +/// destructor of a managed HostObject or HostFunction. Informally, this +/// means that the main source of unsafe behavior is to hold a jsi object +/// in a non-Runtime-managed object, and not clean it up before the Runtime +/// is shut down. If your lifecycle is such that avoiding this is hard, +/// you will probably need to do use your own locks. +class JSI_EXPORT Runtime { + public: + virtual ~Runtime(); + + /// Evaluates the given JavaScript \c buffer. \c sourceURL is used + /// to annotate the stack trace if there is an exception. The + /// contents may be utf8-encoded JS source code, or binary bytecode + /// whose format is specific to the implementation. If the input + /// format is unknown, or evaluation causes an error, a JSIException + /// will be thrown. + /// Note this function should ONLY be used when there isn't another means + /// through the JSI API. For example, it will be much slower to use this to + /// call a global function than using the JSI APIs to read the function + /// property from the global object and then calling it explicitly. + virtual Value evaluateJavaScript( + const std::shared_ptr& buffer, + const std::string& sourceURL) = 0; + + /// Prepares to evaluate the given JavaScript \c buffer by processing it into + /// a form optimized for execution. This may include pre-parsing, compiling, + /// etc. If the input is invalid (for example, cannot be parsed), a + /// JSIException will be thrown. The resulting object is tied to the + /// particular concrete type of Runtime from which it was created. It may be + /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete + /// type. + /// The PreparedJavaScript object may be passed to multiple VM instances, so + /// they can all share and benefit from the prepared script. + /// As with evaluateJavaScript(), using JavaScript code should be avoided + /// when the JSI API is sufficient. + virtual std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) = 0; + + /// Evaluates a PreparedJavaScript. If evaluation causes an error, a + /// JSIException will be thrown. + /// As with evaluateJavaScript(), using JavaScript code should be avoided + /// when the JSI API is sufficient. + virtual Value evaluatePreparedJavaScript( + const std::shared_ptr& js) = 0; + + /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue. + /// + /// \param maxMicrotasksHint a hint to tell an implementation that it should + /// make a best effort not execute more than the given number. It's default + /// to -1 for infinity (unbounded execution). + /// \return true if the queue is drained or false if there is more work to do. + /// + /// When there were exceptions thrown from the execution of microtasks, + /// implementations shall discard the exceptional jobs. An implementation may + /// \throw a \c JSError object to signal the hosts to handle. In that case, an + /// implementation may or may not suspend the draining. + /// + /// Hosts may call this function again to resume the draining if it was + /// suspended due to either exceptions or the \p maxMicrotasksHint bound. + /// E.g. a host may repetitively invoke this function until the queue is + /// drained to implement the "microtask checkpoint" defined in WHATWG HTML + /// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint. + /// + /// Note that error propagation is only a concern if a host needs to implement + /// `queueMicrotask`, a recent API that allows enqueueing arbitrary functions + /// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are + /// handled internally to VMs and are never propagated to hosts. + /// + /// This API offers some queue management to hosts at its best effort due to + /// different behaviors and limitations imposed by different VMs and APIs. By + /// the time this is written, An implementation may swallow exceptions (JSC), + /// may not pause (V8), and may not support bounded executions. + virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; + + /// \return the global object + virtual Object global() = 0; + + /// \return a short printable description of the instance. It should + /// at least include some human-readable indication of the runtime + /// implementation. This should only be used by logging, debugging, + /// and other developer-facing callers. + virtual std::string description() = 0; + + /// \return whether or not the underlying runtime supports debugging via the + /// Chrome remote debugging protocol. + /// + /// NOTE: the API for determining whether a runtime is debuggable and + /// registering a runtime with the debugger is still in flux, so please don't + /// use this API unless you know what you're doing. + virtual bool isInspectable() = 0; + + /// \return an interface to extract metrics from this \c Runtime. The default + /// implementation of this function returns an \c Instrumentation instance + /// which returns no metrics. + virtual Instrumentation& instrumentation(); + + protected: + friend class Pointer; + friend class PropNameID; + friend class Symbol; + friend class String; + friend class Object; + friend class WeakObject; + friend class Array; + friend class ArrayBuffer; + friend class Function; + friend class Value; + friend class Scope; + friend class JSError; + + // Potential optimization: avoid the cloneFoo() virtual dispatch, + // and instead just fix the number of fields, and copy them, since + // in practice they are trivially copyable. Sufficient use of + // rvalue arguments/methods would also reduce the number of clones. + + struct PointerValue { + virtual void invalidate() = 0; + + protected: + virtual ~PointerValue() = default; + }; + + virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0; + + virtual PropNameID createPropNameIDFromAscii( + const char* str, + size_t length) = 0; + virtual PropNameID createPropNameIDFromUtf8( + const uint8_t* utf8, + size_t length) = 0; + virtual PropNameID createPropNameIDFromString(const String& str) = 0; + virtual std::string utf8(const PropNameID&) = 0; + virtual bool compare(const PropNameID&, const PropNameID&) = 0; + + virtual std::string symbolToString(const Symbol&) = 0; + + virtual String createStringFromAscii(const char* str, size_t length) = 0; + virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; + virtual std::string utf8(const String&) = 0; + + // \return a \c Value created from a utf8-encoded JSON string. The default + // implementation creates a \c String and invokes JSON.parse. + virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length); + + virtual Object createObject() = 0; + virtual Object createObject(std::shared_ptr ho) = 0; + virtual std::shared_ptr getHostObject(const jsi::Object&) = 0; + virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0; + + virtual Value getProperty(const Object&, const PropNameID& name) = 0; + virtual Value getProperty(const Object&, const String& name) = 0; + virtual bool hasProperty(const Object&, const PropNameID& name) = 0; + virtual bool hasProperty(const Object&, const String& name) = 0; + virtual void + setPropertyValue(Object&, const PropNameID& name, const Value& value) = 0; + virtual void + setPropertyValue(Object&, const String& name, const Value& value) = 0; + + virtual bool isArray(const Object&) const = 0; + virtual bool isArrayBuffer(const Object&) const = 0; + virtual bool isFunction(const Object&) const = 0; + virtual bool isHostObject(const jsi::Object&) const = 0; + virtual bool isHostFunction(const jsi::Function&) const = 0; + virtual Array getPropertyNames(const Object&) = 0; + + virtual WeakObject createWeakObject(const Object&) = 0; + virtual Value lockWeakObject(WeakObject&) = 0; + + virtual Array createArray(size_t length) = 0; + virtual size_t size(const Array&) = 0; + virtual size_t size(const ArrayBuffer&) = 0; + virtual uint8_t* data(const ArrayBuffer&) = 0; + virtual Value getValueAtIndex(const Array&, size_t i) = 0; + virtual void setValueAtIndexImpl(Array&, size_t i, const Value& value) = 0; + + virtual Function createFunctionFromHostFunction( + const PropNameID& name, + unsigned int paramCount, + HostFunctionType func) = 0; + virtual Value call( + const Function&, + const Value& jsThis, + const Value* args, + size_t count) = 0; + virtual Value + callAsConstructor(const Function&, const Value* args, size_t count) = 0; + + // Private data for managing scopes. + struct ScopeState; + virtual ScopeState* pushScope(); + virtual void popScope(ScopeState*); + + virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0; + virtual bool strictEquals(const String& a, const String& b) const = 0; + virtual bool strictEquals(const Object& a, const Object& b) const = 0; + + virtual bool instanceOf(const Object& o, const Function& f) = 0; + + // These exist so derived classes can access the private parts of + // Value, Symbol, String, and Object, which are all friends of Runtime. + template + static T make(PointerValue* pv); + static PointerValue* getPointerValue(Pointer& pointer); + static const PointerValue* getPointerValue(const Pointer& pointer); + static const PointerValue* getPointerValue(const Value& value); + + friend class ::FBJSRuntime; + template + friend class RuntimeDecorator; +}; + +// Base class for pointer-storing types. +class JSI_EXPORT Pointer { + protected: + explicit Pointer(Pointer&& other) : ptr_(other.ptr_) { + other.ptr_ = nullptr; + } + + ~Pointer() { + if (ptr_) { + ptr_->invalidate(); + } + } + + Pointer& operator=(Pointer&& other); + + friend class Runtime; + friend class Value; + + explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {} + + typename Runtime::PointerValue* ptr_; +}; + +/// Represents something that can be a JS property key. Movable, not copyable. +class JSI_EXPORT PropNameID : public Pointer { + public: + using Pointer::Pointer; + + PropNameID(Runtime& runtime, const PropNameID& other) + : Pointer(runtime.clonePropNameID(other.ptr_)) {} + + PropNameID(PropNameID&& other) = default; + PropNameID& operator=(PropNameID&& other) = default; + + /// Create a JS property name id from ascii values. The data is + /// copied. + static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) { + return runtime.createPropNameIDFromAscii(str, length); + } + + /// Create a property name id from a nul-terminated C ascii name. The data is + /// copied. + static PropNameID forAscii(Runtime& runtime, const char* str) { + return forAscii(runtime, str, strlen(str)); + } + + /// Create a PropNameID from a C++ string. The string is copied. + static PropNameID forAscii(Runtime& runtime, const std::string& str) { + return forAscii(runtime, str.c_str(), str.size()); + } + + /// Create a PropNameID from utf8 values. The data is copied. + /// Results are undefined if \p utf8 contains invalid code points. + static PropNameID + forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + return runtime.createPropNameIDFromUtf8(utf8, length); + } + + /// Create a PropNameID from utf8-encoded octets stored in a + /// std::string. The string data is transformed and copied. + /// Results are undefined if \p utf8 contains invalid code points. + static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) { + return runtime.createPropNameIDFromUtf8( + reinterpret_cast(utf8.data()), utf8.size()); + } + + /// Create a PropNameID from a JS string. + static PropNameID forString(Runtime& runtime, const jsi::String& str) { + return runtime.createPropNameIDFromString(str); + } + + // Creates a vector of PropNameIDs constructed from given arguments. + template + static std::vector names(Runtime& runtime, Args&&... args); + + // Creates a vector of given PropNameIDs. + template + static std::vector names(PropNameID(&&propertyNames)[N]); + + /// Copies the data in a PropNameID as utf8 into a C++ string. + std::string utf8(Runtime& runtime) const { + return runtime.utf8(*this); + } + + static bool compare( + Runtime& runtime, + const jsi::PropNameID& a, + const jsi::PropNameID& b) { + return runtime.compare(a, b); + } + + friend class Runtime; + friend class Value; +}; + +/// Represents a JS Symbol (es6). Movable, not copyable. +/// TODO T40778724: this is a limited implementation sufficient for +/// the debugger not to crash when a Symbol is a property in an Object +/// or element in an array. Complete support for creating will come +/// later. +class JSI_EXPORT Symbol : public Pointer { + public: + using Pointer::Pointer; + + Symbol(Symbol&& other) = default; + Symbol& operator=(Symbol&& other) = default; + + /// \return whether a and b refer to the same symbol. + static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) { + return runtime.strictEquals(a, b); + } + + /// Converts a Symbol into a C++ string as JS .toString would. The output + /// will look like \c Symbol(description) . + std::string toString(Runtime& runtime) const { + return runtime.symbolToString(*this); + } + + friend class Runtime; + friend class Value; +}; + +/// Represents a JS String. Movable, not copyable. +class JSI_EXPORT String : public Pointer { + public: + using Pointer::Pointer; + + String(String&& other) = default; + String& operator=(String&& other) = default; + + /// Create a JS string from ascii values. The string data is + /// copied. + static String + createFromAscii(Runtime& runtime, const char* str, size_t length) { + return runtime.createStringFromAscii(str, length); + } + + /// Create a JS string from a nul-terminated C ascii string. The + /// string data is copied. + static String createFromAscii(Runtime& runtime, const char* str) { + return createFromAscii(runtime, str, strlen(str)); + } + + /// Create a JS string from a C++ string. The string data is + /// copied. + static String createFromAscii(Runtime& runtime, const std::string& str) { + return createFromAscii(runtime, str.c_str(), str.size()); + } + + /// Create a JS string from utf8-encoded octets. The string data is + /// transformed and copied. Results are undefined if \p utf8 contains invalid + /// code points. + static String + createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + return runtime.createStringFromUtf8(utf8, length); + } + + /// Create a JS string from utf8-encoded octets stored in a + /// std::string. The string data is transformed and copied. Results are + /// undefined if \p utf8 contains invalid code points. + static String createFromUtf8(Runtime& runtime, const std::string& utf8) { + return runtime.createStringFromUtf8( + reinterpret_cast(utf8.data()), utf8.length()); + } + + /// \return whether a and b contain the same characters. + static bool strictEquals(Runtime& runtime, const String& a, const String& b) { + return runtime.strictEquals(a, b); + } + + /// Copies the data in a JS string as utf8 into a C++ string. + std::string utf8(Runtime& runtime) const { + return runtime.utf8(*this); + } + + friend class Runtime; + friend class Value; +}; + +class Array; +class Function; + +/// Represents a JS Object. Movable, not copyable. +class JSI_EXPORT Object : public Pointer { + public: + using Pointer::Pointer; + + Object(Object&& other) = default; + Object& operator=(Object&& other) = default; + + /// Creates a new Object instance, like '{}' in JS. + Object(Runtime& runtime) : Object(runtime.createObject()) {} + + static Object createFromHostObject( + Runtime& runtime, + std::shared_ptr ho) { + return runtime.createObject(ho); + } + + /// \return whether this and \c obj are the same JSObject or not. + static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) { + return runtime.strictEquals(a, b); + } + + /// \return the result of `this instanceOf ctor` in JS. + bool instanceOf(Runtime& rt, const Function& ctor) { + return rt.instanceOf(*this, ctor); + } + + /// \return the property of the object with the given ascii name. + /// If the name isn't a property on the object, returns the + /// undefined value. + Value getProperty(Runtime& runtime, const char* name) const; + + /// \return the property of the object with the String name. + /// If the name isn't a property on the object, returns the + /// undefined value. + Value getProperty(Runtime& runtime, const String& name) const; + + /// \return the property of the object with the given JS PropNameID + /// name. If the name isn't a property on the object, returns the + /// undefined value. + Value getProperty(Runtime& runtime, const PropNameID& name) const; + + /// \return true if and only if the object has a property with the + /// given ascii name. + bool hasProperty(Runtime& runtime, const char* name) const; + + /// \return true if and only if the object has a property with the + /// given String name. + bool hasProperty(Runtime& runtime, const String& name) const; + + /// \return true if and only if the object has a property with the + /// given PropNameID name. + bool hasProperty(Runtime& runtime, const PropNameID& name) const; + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. + template + void setProperty(Runtime& runtime, const char* name, T&& value); + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. + template + void setProperty(Runtime& runtime, const String& name, T&& value); + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. + template + void setProperty(Runtime& runtime, const PropNameID& name, T&& value); + + /// \return true iff JS \c Array.isArray() would return \c true. If + /// so, then \c getArray() will succeed. + bool isArray(Runtime& runtime) const { + return runtime.isArray(*this); + } + + /// \return true iff the Object is an ArrayBuffer. If so, then \c + /// getArrayBuffer() will succeed. + bool isArrayBuffer(Runtime& runtime) const { + return runtime.isArrayBuffer(*this); + } + + /// \return true iff the Object is callable. If so, then \c + /// getFunction will succeed. + bool isFunction(Runtime& runtime) const { + return runtime.isFunction(*this); + } + + /// \return true iff the Object was initialized with \c createFromHostObject + /// and the HostObject passed is of type \c T. If returns \c true then + /// \c getHostObject will succeed. + template + bool isHostObject(Runtime& runtime) const; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will assert. + Array getArray(Runtime& runtime) const&; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will assert. + Array getArray(Runtime& runtime) &&; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will throw + /// JSIException. + Array asArray(Runtime& runtime) const&; + + /// \return an Array instance which refers to the same underlying + /// object. If \c isArray() would return false, this will throw + /// JSIException. + Array asArray(Runtime& runtime) &&; + + /// \return an ArrayBuffer instance which refers to the same underlying + /// object. If \c isArrayBuffer() would return false, this will assert. + ArrayBuffer getArrayBuffer(Runtime& runtime) const&; + + /// \return an ArrayBuffer instance which refers to the same underlying + /// object. If \c isArrayBuffer() would return false, this will assert. + ArrayBuffer getArrayBuffer(Runtime& runtime) &&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will assert. + Function getFunction(Runtime& runtime) const&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will assert. + Function getFunction(Runtime& runtime) &&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will throw + /// JSIException. + Function asFunction(Runtime& runtime) const&; + + /// \return a Function instance which refers to the same underlying + /// object. If \c isFunction() would return false, this will throw + /// JSIException. + Function asFunction(Runtime& runtime) &&; + + /// \return a shared_ptr which refers to the same underlying + /// \c HostObject that was used to create this object. If \c isHostObject + /// is false, this will assert. Note that this does a type check and will + /// assert if the underlying HostObject isn't of type \c T + template + std::shared_ptr getHostObject(Runtime& runtime) const; + + /// \return a shared_ptr which refers to the same underlying + /// \c HostObject that was used to create this object. If \c isHostObject + /// is false, this will throw. + template + std::shared_ptr asHostObject(Runtime& runtime) const; + + /// \return same as \c getProperty(name).asObject(), except with + /// a better exception message. + Object getPropertyAsObject(Runtime& runtime, const char* name) const; + + /// \return similar to \c + /// getProperty(name).getObject().getFunction(), except it will + /// throw JSIException instead of asserting if the property is + /// not an object, or the object is not callable. + Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + + /// \return an Array consisting of all enumerable property names in + /// the object and its prototype chain. All values in the return + /// will be isString(). (This is probably not optimal, but it + /// works. I only need it in one place.) + Array getPropertyNames(Runtime& runtime) const; + + protected: + void + setPropertyValue(Runtime& runtime, const String& name, const Value& value) { + return runtime.setPropertyValue(*this, name, value); + } + + void setPropertyValue( + Runtime& runtime, + const PropNameID& name, + const Value& value) { + return runtime.setPropertyValue(*this, name, value); + } + + friend class Runtime; + friend class Value; +}; + +/// Represents a weak reference to a JS Object. If the only reference +/// to an Object are these, the object is eligible for GC. Method +/// names are inspired by C++ weak_ptr. Movable, not copyable. +class JSI_EXPORT WeakObject : public Pointer { + public: + using Pointer::Pointer; + + WeakObject(WeakObject&& other) = default; + WeakObject& operator=(WeakObject&& other) = default; + + /// Create a WeakObject from an Object. + WeakObject(Runtime& runtime, const Object& o) + : WeakObject(runtime.createWeakObject(o)) {} + + /// \return a Value representing the underlying Object if it is still valid; + /// otherwise returns \c undefined. Note that this method has nothing to do + /// with threads or concurrency. The name is based on std::weak_ptr::lock() + /// which serves a similar purpose. + Value lock(Runtime& runtime); + + friend class Runtime; +}; + +/// Represents a JS Object which can be efficiently used as an array +/// with integral indices. +class JSI_EXPORT Array : public Object { + public: + Array(Array&&) = default; + /// Creates a new Array instance, with \c length undefined elements. + Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {} + + Array& operator=(Array&&) = default; + + /// \return the size of the Array, according to its length property. + /// (C++ naming convention) + size_t size(Runtime& runtime) const { + return runtime.size(*this); + } + + /// \return the size of the Array, according to its length property. + /// (JS naming convention) + size_t length(Runtime& runtime) const { + return size(runtime); + } + + /// \return the property of the array at index \c i. If there is no + /// such property, returns the undefined value. If \c i is out of + /// range [ 0..\c length ] throws a JSIException. + Value getValueAtIndex(Runtime& runtime, size_t i) const; + + /// Sets the property of the array at index \c i. The argument + /// value behaves as with Object::setProperty(). If \c i is out of + /// range [ 0..\c length ] throws a JSIException. + template + void setValueAtIndex(Runtime& runtime, size_t i, T&& value); + + /// There is no current API for changing the size of an array once + /// created. We'll probably need that eventually. + + /// Creates a new Array instance from provided values + template + static Array createWithElements(Runtime&, Args&&... args); + + /// Creates a new Array instance from initializer list. + static Array createWithElements( + Runtime& runtime, + std::initializer_list elements); + + private: + friend class Object; + friend class Value; + + void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) { + return runtime.setValueAtIndexImpl(*this, i, value); + } + + Array(Runtime::PointerValue* value) : Object(value) {} +}; + +/// Represents a JSArrayBuffer +class JSI_EXPORT ArrayBuffer : public Object { + public: + ArrayBuffer(ArrayBuffer&&) = default; + ArrayBuffer& operator=(ArrayBuffer&&) = default; + + /// \return the size of the ArrayBuffer, according to its byteLength property. + /// (C++ naming convention) + size_t size(Runtime& runtime) const { + return runtime.size(*this); + } + + size_t length(Runtime& runtime) const { + return runtime.size(*this); + } + + uint8_t* data(Runtime& runtime) { + return runtime.data(*this); + } + + private: + friend class Object; + friend class Value; + + ArrayBuffer(Runtime::PointerValue* value) : Object(value) {} +}; + +/// Represents a JS Object which is guaranteed to be Callable. +class JSI_EXPORT Function : public Object { + public: + Function(Function&&) = default; + Function& operator=(Function&&) = default; + + /// Create a function which, when invoked, calls C++ code. If the + /// function throws an exception, a JS Error will be created and + /// thrown. + /// \param name the name property for the function. + /// \param paramCount the length property for the function, which + /// may not be the number of arguments the function is passed. + static Function createFromHostFunction( + Runtime& runtime, + const jsi::PropNameID& name, + unsigned int paramCount, + jsi::HostFunctionType func); + + /// Calls the function with \c count \c args. The \c this value of the JS + /// function will not be set by the C++ caller, similar to calling + /// Function.prototype.apply(undefined, args) in JS. + /// \b Note: as with Function.prototype.apply, \c this may not always be + /// \c undefined in the function itself. If the function is non-strict, + /// \c this will be set to the global object. + Value call(Runtime& runtime, const Value* args, size_t count) const; + + /// Calls the function with a \c std::initializer_list of Value + /// arguments. The \c this value of the JS function will not be set by the + /// C++ caller, similar to calling Function.prototype.apply(undefined, args) + /// in JS. + /// \b Note: as with Function.prototype.apply, \c this may not always be + /// \c undefined in the function itself. If the function is non-strict, + /// \c this will be set to the global object. + Value call(Runtime& runtime, std::initializer_list args) const; + + /// Calls the function with any number of arguments similarly to + /// Object::setProperty(). The \c this value of the JS function will not be + /// set by the C++ caller, similar to calling + /// Function.prototype.call(undefined, ...args) in JS. + /// \b Note: as with Function.prototype.call, \c this may not always be + /// \c undefined in the function itself. If the function is non-strict, + /// \c this will be set to the global object. + template + Value call(Runtime& runtime, Args&&... args) const; + + /// Calls the function with \c count \c args and \c jsThis value passed + /// as the \c this value. + Value callWithThis( + Runtime& Runtime, + const Object& jsThis, + const Value* args, + size_t count) const; + + /// Calls the function with a \c std::initializer_list of Value + /// arguments and \c jsThis passed as the \c this value. + Value callWithThis( + Runtime& runtime, + const Object& jsThis, + std::initializer_list args) const; + + /// Calls the function with any number of arguments similarly to + /// Object::setProperty(), and with \c jsThis passed as the \c this value. + template + Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args) + const; + + /// Calls the function as a constructor with \c count \c args. Equivalent + /// to calling `new Func` where `Func` is the js function reqresented by + /// this. + Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) + const; + + /// Same as above `callAsConstructor`, except use an initializer_list to + /// supply the arguments. + Value callAsConstructor(Runtime& runtime, std::initializer_list args) + const; + + /// Same as above `callAsConstructor`, but automatically converts/wraps + /// any argument with a jsi Value. + template + Value callAsConstructor(Runtime& runtime, Args&&... args) const; + + /// Returns whether this was created with Function::createFromHostFunction. + /// If true then you can use getHostFunction to get the underlying + /// HostFunctionType. + bool isHostFunction(Runtime& runtime) const { + return runtime.isHostFunction(*this); + } + + /// Returns the underlying HostFunctionType iff isHostFunction returns true + /// and asserts otherwise. You can use this to use std::function<>::target + /// to get the object that was passed to create the HostFunctionType. + /// + /// Note: The reference returned is borrowed from the JS object underlying + /// \c this, and thus only lasts as long as the object underlying + /// \c this does. + HostFunctionType& getHostFunction(Runtime& runtime) const { + assert(isHostFunction(runtime)); + return runtime.getHostFunction(*this); + } + + private: + friend class Object; + friend class Value; + + Function(Runtime::PointerValue* value) : Object(value) {} +}; + +/// Represents any JS Value (undefined, null, boolean, number, symbol, +/// string, or object). Movable, or explicitly copyable (has no copy +/// ctor). +class JSI_EXPORT Value { + public: + /// Default ctor creates an \c undefined JS value. + Value() : Value(UndefinedKind) {} + + /// Creates a \c null JS value. + /* implicit */ Value(std::nullptr_t) : kind_(NullKind) {} + + /// Creates a boolean JS value. + /* implicit */ Value(bool b) : Value(BooleanKind) { + data_.boolean = b; + } + + /// Creates a number JS value. + /* implicit */ Value(double d) : Value(NumberKind) { + data_.number = d; + } + + /// Creates a number JS value. + /* implicit */ Value(int i) : Value(NumberKind) { + data_.number = i; + } + + /// Moves a Symbol, String, or Object rvalue into a new JS value. + template + /* implicit */ Value(T&& other) : Value(kindOf(other)) { + static_assert( + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value, + "Value cannot be implicitly move-constructed from this type"); + new (&data_.pointer) T(std::move(other)); + } + + /// Value("foo") will treat foo as a bool. This makes doing that a + /// compile error. + template + Value(const char*) { + static_assert( + !std::is_same::value, + "Value cannot be constructed directly from const char*"); + } + + Value(Value&& value); + + /// Copies a Symbol lvalue into a new JS value. + Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) { + new (&data_.pointer) String(runtime.cloneSymbol(sym.ptr_)); + } + + /// Copies a String lvalue into a new JS value. + Value(Runtime& runtime, const String& str) : Value(StringKind) { + new (&data_.pointer) String(runtime.cloneString(str.ptr_)); + } + + /// Copies a Object lvalue into a new JS value. + Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) { + new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_)); + } + + /// Creates a JS value from another Value lvalue. + Value(Runtime& runtime, const Value& value); + + /// Value(rt, "foo") will treat foo as a bool. This makes doing + /// that a compile error. + template + Value(Runtime&, const char*) { + static_assert( + !std::is_same::value, + "Value cannot be constructed directly from const char*"); + } + + ~Value(); + // \return the undefined \c Value. + static Value undefined() { + return Value(); + } + + // \return the null \c Value. + static Value null() { + return Value(nullptr); + } + + // \return a \c Value created from a utf8-encoded JSON string. + static Value + createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length) { + return runtime.createValueFromJsonUtf8(json, length); + } + + /// \return according to the Strict Equality Comparison algorithm, see: + /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison + static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); + + Value& operator=(Value&& other) { + this->~Value(); + new (this) Value(std::move(other)); + return *this; + } + + bool isUndefined() const { + return kind_ == UndefinedKind; + } + + bool isNull() const { + return kind_ == NullKind; + } + + bool isBool() const { + return kind_ == BooleanKind; + } + + bool isNumber() const { + return kind_ == NumberKind; + } + + bool isString() const { + return kind_ == StringKind; + } + + bool isSymbol() const { + return kind_ == SymbolKind; + } + + bool isObject() const { + return kind_ == ObjectKind; + } + + /// \return the boolean value, or asserts if not a boolean. + bool getBool() const { + assert(isBool()); + return data_.boolean; + } + + /// \return the number value, or asserts if not a number. + double getNumber() const { + assert(isNumber()); + return data_.number; + } + + /// \return the number value, or throws JSIException if not a + /// number. + double asNumber() const; + + /// \return the Symbol value, or asserts if not a symbol. + Symbol getSymbol(Runtime& runtime) const& { + assert(isSymbol()); + return Symbol(runtime.cloneSymbol(data_.pointer.ptr_)); + } + + /// \return the Symbol value, or asserts if not a symbol. + /// Can be used on rvalue references to avoid cloning more symbols. + Symbol getSymbol(Runtime&) && { + assert(isSymbol()); + auto ptr = data_.pointer.ptr_; + data_.pointer.ptr_ = nullptr; + return static_cast(ptr); + } + + /// \return the Symbol value, or throws JSIException if not a + /// symbol + Symbol asSymbol(Runtime& runtime) const&; + Symbol asSymbol(Runtime& runtime) &&; + + /// \return the String value, or asserts if not a string. + String getString(Runtime& runtime) const& { + assert(isString()); + return String(runtime.cloneString(data_.pointer.ptr_)); + } + + /// \return the String value, or asserts if not a string. + /// Can be used on rvalue references to avoid cloning more strings. + String getString(Runtime&) && { + assert(isString()); + auto ptr = data_.pointer.ptr_; + data_.pointer.ptr_ = nullptr; + return static_cast(ptr); + } + + /// \return the String value, or throws JSIException if not a + /// string. + String asString(Runtime& runtime) const&; + String asString(Runtime& runtime) &&; + + /// \return the Object value, or asserts if not an object. + Object getObject(Runtime& runtime) const& { + assert(isObject()); + return Object(runtime.cloneObject(data_.pointer.ptr_)); + } + + /// \return the Object value, or asserts if not an object. + /// Can be used on rvalue references to avoid cloning more objects. + Object getObject(Runtime&) && { + assert(isObject()); + auto ptr = data_.pointer.ptr_; + data_.pointer.ptr_ = nullptr; + return static_cast(ptr); + } + + /// \return the Object value, or throws JSIException if not an + /// object. + Object asObject(Runtime& runtime) const&; + Object asObject(Runtime& runtime) &&; + + // \return a String like JS .toString() would do. + String toString(Runtime& runtime) const; + + private: + friend class Runtime; + + enum ValueKind { + UndefinedKind, + NullKind, + BooleanKind, + NumberKind, + SymbolKind, + StringKind, + ObjectKind, + PointerKind = SymbolKind, + }; + + union Data { + // Value's ctor and dtor will manage the lifecycle of the contained Data. + Data() { + static_assert( + sizeof(Data) == sizeof(uint64_t), + "Value data should fit in a 64-bit register"); + } + ~Data() {} + + // scalars + bool boolean; + double number; + // pointers + Pointer pointer; // Symbol, String, Object, Array, Function + }; + + Value(ValueKind kind) : kind_(kind) {} + + constexpr static ValueKind kindOf(const Symbol&) { + return SymbolKind; + } + constexpr static ValueKind kindOf(const String&) { + return StringKind; + } + constexpr static ValueKind kindOf(const Object&) { + return ObjectKind; + } + + ValueKind kind_; + Data data_; + + // In the future: Value becomes NaN-boxed. See T40538354. +}; + +/// Not movable and not copyable RAII marker advising the underlying +/// JavaScript VM to track resources allocated since creation until +/// destruction so that they can be recycled eagerly when the Scope +/// goes out of scope instead of floating in the air until the next +/// garbage collection or any other delayed release occurs. +/// +/// This API should be treated only as advice, implementations can +/// choose to ignore the fact that Scopes are created or destroyed. +/// +/// This class is an exception to the rule allowing destructors to be +/// called without proper synchronization (see Runtime documentation). +/// The whole point of this class is to enable all sorts of clean ups +/// when the destructor is called and this proper synchronization is +/// required at that time. +/// +/// Instances of this class are intended to be created as automatic stack +/// variables in which case destructor calls don't require any additional +/// locking, provided that the lock (if any) is managed with RAII helpers. +class JSI_EXPORT Scope { + public: + explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {} + ~Scope() { + rt_.popScope(prv_); + }; + + Scope(const Scope&) = delete; + Scope(Scope&&) = delete; + + Scope& operator=(const Scope&) = delete; + Scope& operator=(Scope&&) = delete; + + template + static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) { + Scope s(rt); + return f(); + } + + private: + Runtime& rt_; + Runtime::ScopeState* prv_; +}; + +/// Base class for jsi exceptions +class JSI_EXPORT JSIException : public std::exception { + protected: + JSIException(){}; + JSIException(std::string what) : what_(std::move(what)){}; + + public: + virtual const char* what() const noexcept override { + return what_.c_str(); + } + + virtual ~JSIException(); + + protected: + std::string what_; +}; + +/// This exception will be thrown by API functions on errors not related to +/// JavaScript execution. +class JSI_EXPORT JSINativeException : public JSIException { + public: + JSINativeException(std::string what) : JSIException(std::move(what)) {} + + virtual ~JSINativeException(); +}; + +/// This exception will be thrown by API functions whenever a JS +/// operation causes an exception as described by the spec, or as +/// otherwise described. +class JSI_EXPORT JSError : public JSIException { + public: + /// Creates a JSError referring to provided \c value + JSError(Runtime& r, Value&& value); + + /// Creates a JSError referring to new \c Error instance capturing current + /// JavaScript stack. The error message property is set to given \c message. + JSError(Runtime& rt, std::string message); + + /// Creates a JSError referring to new \c Error instance capturing current + /// JavaScript stack. The error message property is set to given \c message. + JSError(Runtime& rt, const char* message) + : JSError(rt, std::string(message)){}; + + /// Creates a JSError referring to a JavaScript Object having message and + /// stack properties set to provided values. + JSError(Runtime& rt, std::string message, std::string stack); + + /// Creates a JSError referring to provided value and what string + /// set to provided message. This argument order is a bit weird, + /// but necessary to avoid ambiguity with the above. + JSError(std::string what, Runtime& rt, Value&& value); + + virtual ~JSError(); + + const std::string& getStack() const { + return stack_; + } + + const std::string& getMessage() const { + return message_; + } + + const jsi::Value& value() const { + assert(value_); + return *value_; + } + + private: + // This initializes the value_ member and does some other + // validation, so it must be called by every branch through the + // constructors. + void setValue(Runtime& rt, Value&& value); + + // This needs to be on the heap, because throw requires the object + // be copyable, and Value is not. + std::shared_ptr value_; + std::string message_; + std::string stack_; +}; + +} // namespace jsi +} // namespace facebook + +#include diff --git a/vnext/Shared/JSI/ChakraRuntime.cpp b/vnext/Shared/JSI/ChakraRuntime.cpp index d7a0ba0b75b..82170d5aefc 100644 --- a/vnext/Shared/JSI/ChakraRuntime.cpp +++ b/vnext/Shared/JSI/ChakraRuntime.cpp @@ -312,14 +312,6 @@ facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromString(const facebo return MakePointer(propertyId); } -facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym) { - // TODO: Support for symbols through the native API in JSC is very limited. - // While we could construct a PropNameID here, we would not be able to get a - // symbol property through the C++ API. - UNREFERENCED_PARAMETER(sym); - throw; -} - std::string ChakraRuntime::utf8(const facebook::jsi::PropNameID &id) { return Common::Unicode::Utf16ToUtf8(GetPropertyNameFromId(GetJsRef(id))); } diff --git a/vnext/Shared/JSI/ChakraRuntime.h b/vnext/Shared/JSI/ChakraRuntime.h index df5d179159b..4f21438c4dd 100644 --- a/vnext/Shared/JSI/ChakraRuntime.h +++ b/vnext/Shared/JSI/ChakraRuntime.h @@ -63,7 +63,6 @@ class ChakraRuntime : public facebook::jsi::Runtime, public ChakraApi, ChakraApi facebook::jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override; facebook::jsi::PropNameID createPropNameIDFromString(const facebook::jsi::String &str) override; - facebook::jsi::PropNameID createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym); std::string utf8(const facebook::jsi::PropNameID &id) override; bool compare(const facebook::jsi::PropNameID &lhs, const facebook::jsi::PropNameID &rhs) override; diff --git a/vnext/overrides.json b/vnext/overrides.json index eed5bdf63f5..68d05a39a62 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -37,6 +37,27 @@ "baseHash": "010970b09aebda9348dfed9dd7b8fefb365bcc9b", "issue": 7821 }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp", + "baseFile": "ReactCommon/jsi/JSCRuntime.cpp", + "baseHash": "da73ed017a05d0fdc4302bd0794a343dc5ac935a", + "issue": 111 + }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h", + "baseFile": "ReactCommon/jsi/jsi/decorator.h", + "baseHash": "c93e4d10173716218503599610eb48f40bd2a47c", + "issue": 111 + }, + { + "type": "patch", + "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h", + "baseFile": "ReactCommon/jsi/jsi/jsi.h", + "baseHash": "6ef3f675e8af4060ebd43ba75136d58932b02eb4", + "issue": 111 + }, { "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/RawPropsParser.cpp", From d73f6aa5f5637cf1739bdaa86fa06d7677c6bd45 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Tue, 15 Mar 2022 11:10:57 -0700 Subject: [PATCH 10/10] adds issue number to override --- vnext/overrides.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vnext/overrides.json b/vnext/overrides.json index 68d05a39a62..b100820c08d 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -42,21 +42,21 @@ "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp", "baseFile": "ReactCommon/jsi/JSCRuntime.cpp", "baseHash": "da73ed017a05d0fdc4302bd0794a343dc5ac935a", - "issue": 111 + "issue": 9703 }, { "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h", "baseFile": "ReactCommon/jsi/jsi/decorator.h", "baseHash": "c93e4d10173716218503599610eb48f40bd2a47c", - "issue": 111 + "issue": 9703 }, { "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h", "baseFile": "ReactCommon/jsi/jsi/jsi.h", "baseHash": "6ef3f675e8af4060ebd43ba75136d58932b02eb4", - "issue": 111 + "issue": 9703 }, { "type": "patch",