From fcc3bfaca2e01d934a915e92ed4f5c6a0aeab480 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Wed, 19 Nov 2025 14:11:35 -0800 Subject: [PATCH 1/3] Add modern inspector support --- ...-2c423284-4814-4644-8f7e-b88ab9acf84e.json | 7 + docs/modern-inspector-details.md | 70 ++ .../packages.chakra.lock.json | 6 +- .../AutomationChannel/packages.lock.json | 6 +- .../packages.newarch.lock.json | 6 +- .../packages.lock.json | 8 +- .../RNTesterApp-Fabric/packages.lock.json | 8 +- .../RNTesterApp/packages.chakra.lock.json | 6 +- .../windows/RNTesterApp/packages.lock.json | 8 +- .../packages.chakra.lock.json | 6 +- .../InteropTestModuleCS/packages.lock.json | 6 +- .../integrationtest/packages.chakra.lock.json | 6 +- .../integrationtest/packages.lock.json | 8 +- .../packages.lock.json | 6 +- .../packages.experimentalwinui3.lock.json | 8 +- .../packages.lock.json | 8 +- .../packages.experimentalwinui3.lock.json | 8 +- .../playground-composition/packages.lock.json | 8 +- .../windows/playground/packages.lock.json | 8 +- .../packages.lock.json | 8 +- .../SampleAppFabric/packages.lock.json | 8 +- .../windows/SampleAppCPP/packages.lock.json | 8 +- .../windows/SampleAppCS/packages.lock.json | 8 +- .../SampleLibraryCPP/packages.lock.json | 6 +- .../SampleLibraryCS/packages.lock.json | 6 +- .../packages.experimentalwinui3.lock.json | 6 +- .../SampleCustomComponent/packages.lock.json | 6 +- .../packages.experimentalwinui3.lock.json | 8 +- vnext/Desktop.ABITests/packages.lock.json | 8 +- .../packages.experimentalwinui3.lock.json | 8 +- vnext/Desktop.DLL/packages.lock.json | 8 +- .../packages.experimentalwinui3.lock.json | 8 +- .../packages.lock.json | 8 +- .../packages.experimentalwinui3.lock.json | 6 +- vnext/Desktop.UnitTests/packages.lock.json | 6 +- .../packages.experimentalwinui3.lock.json | 6 +- vnext/Desktop/packages.lock.json | 6 +- vnext/Directory.Build.props | 2 +- .../packages.experimentalwinui3.lock.json | 6 +- .../packages.lock.json | 6 +- .../Microsoft.ReactNative.Cxx.vcxitems | 2 +- .../packages.experimentalwinui3.lock.json | 6 +- .../packages.lock.json | 6 +- ...kages.newarch.experimentalwinui3.lock.json | 6 +- .../packages.newarch.lock.json | 6 +- .../packages.lock.json | 6 +- .../packages.lock.json | 6 +- .../packages.lock.json | 6 +- .../Fabric/Composition/DebuggerUIIsland.cpp | 169 +++++ .../Fabric/Composition/DebuggerUIIsland.h | 42 ++ .../DebuggingOverlayComponentView.cpp | 2 +- .../Fabric/Composition/ReactNativeIsland.cpp | 69 +- .../Fabric/Composition/ReactNativeIsland.h | 9 + .../Fabric/FabricUIManagerModule.cpp | 7 +- .../react/threading/TaskDispatchThread.cpp | 99 ++- .../react/threading/TaskDispatchThread.h | 29 +- vnext/Microsoft.ReactNative/JsiApi.cpp | 2 +- .../Microsoft.ReactNative.vcxproj | 1 + .../Microsoft.ReactNative.vcxproj.filters | 3 + .../ReactHost/DebuggerNotifications.h | 54 ++ vnext/Microsoft.ReactNative/ReactHost/React.h | 15 +- .../ReactHost/ReactHost.cpp | 195 ++++- .../ReactHost/ReactHost.h | 26 +- .../ReactHost/ReactInstanceWin.cpp | 29 +- .../ReactHost/ReactInstanceWin.h | 2 +- vnext/Microsoft.ReactNative/ReactRootView.cpp | 108 +++ vnext/Microsoft.ReactNative/ReactRootView.h | 6 + vnext/Microsoft.ReactNative/Views/DevMenu.cpp | 2 +- .../packages.chakra.lock.json | 6 +- .../packages.experimentalwinui3.lock.json | 6 +- .../Microsoft.ReactNative/packages.lock.json | 6 +- ...kages.newarch.experimentalwinui3.lock.json | 6 +- .../packages.newarch.lock.json | 6 +- vnext/PropertySheets/JSEngine.props | 2 +- vnext/PropertySheets/React.Cpp.props | 4 +- .../packages.experimentalwinui3.lock.json | 6 +- .../ReactCommon.UnitTests/packages.lock.json | 6 +- vnext/ReactCommon/ReactCommon.vcxproj | 20 +- .../cxxreact/JSExecutor.cpp | 75 -- .../cxxreact/JSExecutor.h | 196 ------ .../cxxreact/NativeToJsBridge.cpp | 351 --------- .../jserrorhandler/JsErrorHandler.cpp | 429 ----------- .../jsi/jsi/test/testlib.cpp | 8 +- .../jsinspector-modern/NetworkIOAgent.cpp | 12 +- .../react/runtime/JSRuntimeFactory.cpp | 45 -- .../react/runtime/JSRuntimeFactory.h | 91 --- .../react/runtime/ReactInstance.cpp | 665 ------------------ vnext/ReactCommon/cgmanifest.json | 2 +- vnext/Scripts/Tfs/Layout-MSRN-Headers.ps1 | 2 + vnext/Shared/DevServerHelper.h | 16 +- vnext/Shared/DevSettings.h | 7 + vnext/Shared/DevSupportManager.cpp | 99 ++- vnext/Shared/DevSupportManager.h | 26 +- .../Hermes/HermesRuntimeAgentDelegate.cpp | 99 +++ .../Hermes/HermesRuntimeAgentDelegate.h | 81 +++ .../Hermes/HermesRuntimeTargetDelegate.cpp | 264 +++++++ .../Hermes/HermesRuntimeTargetDelegate.h | 77 ++ .../{ => Hermes}/HermesSamplingProfiler.cpp | 0 .../{ => Hermes}/HermesSamplingProfiler.h | 0 vnext/Shared/HermesRuntimeHolder.cpp | 140 +--- vnext/Shared/HermesRuntimeHolder.h | 246 ++++++- vnext/Shared/IDevSupportManager.h | 7 +- ...actInspectorPackagerConnectionDelegate.cpp | 108 +++ ...ReactInspectorPackagerConnectionDelegate.h | 19 + vnext/Shared/Inspector/ReactInspectorThread.h | 18 + vnext/Shared/InspectorPackagerConnection.cpp | 232 ------ vnext/Shared/InspectorPackagerConnection.h | 61 -- vnext/Shared/JSI/RuntimeHolder.h | 7 +- vnext/Shared/OInstance.cpp | 71 +- vnext/Shared/Shared.vcxitems | 44 +- vnext/Shared/Shared.vcxitems.filters | 48 +- vnext/overrides.json | 42 -- 112 files changed, 2187 insertions(+), 2721 deletions(-) create mode 100644 change/react-native-windows-2c423284-4814-4644-8f7e-b88ab9acf84e.json create mode 100644 docs/modern-inspector-details.md create mode 100644 vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.cpp create mode 100644 vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.h create mode 100644 vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.cpp delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.h delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/NativeToJsBridge.cpp delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jserrorhandler/JsErrorHandler.cpp delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h delete mode 100644 vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp create mode 100644 vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp create mode 100644 vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h create mode 100644 vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp create mode 100644 vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h rename vnext/Shared/{ => Hermes}/HermesSamplingProfiler.cpp (100%) rename vnext/Shared/{ => Hermes}/HermesSamplingProfiler.h (100%) create mode 100644 vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp create mode 100644 vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h create mode 100644 vnext/Shared/Inspector/ReactInspectorThread.h delete mode 100644 vnext/Shared/InspectorPackagerConnection.cpp delete mode 100644 vnext/Shared/InspectorPackagerConnection.h diff --git a/change/react-native-windows-2c423284-4814-4644-8f7e-b88ab9acf84e.json b/change/react-native-windows-2c423284-4814-4644-8f7e-b88ab9acf84e.json new file mode 100644 index 00000000000..ae486d0eef9 --- /dev/null +++ b/change/react-native-windows-2c423284-4814-4644-8f7e-b88ab9acf84e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Add support for modern inspector", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/docs/modern-inspector-details.md b/docs/modern-inspector-details.md new file mode 100644 index 00000000000..bf55e0611d8 --- /dev/null +++ b/docs/modern-inspector-details.md @@ -0,0 +1,70 @@ +# Modern Inspector Support in React Native for Windows + +## Overview +The modern inspector is the Chrome DevTools–based debugging experience that ships with the latest +versions of React Native. This experience now works end-to-end for React Native for Windows (RNW) +applications, enabling parity with Android and iOS. The integration provides a unified way to inspect +JavaScript execution, evaluate console expressions, profile CPU and memory usage, and visualize the +component hierarchy for both the Paper and Composition UI stacks. + +## Modern Inspector Building Blocks +- **Host Target** – One per `ReactHost`; surfaces metadata, reload requests, pause overlays, and + implements the CDP-facing delegate (`ReactInspectorHostTargetDelegate`). +- **Instance Target** – Created for each React instance; registers runtime targets, tracks instance + lifecycle, and unregisters cleanly on reload. +- **Runtime Target & Agent** – Runtime targets map to JavaScript VMs; agents are per-session objects + that translate Chrome DevTools Protocol (CDP) messages into engine calls. RNW mirrors upstream + lifetimes so reloads tear everything down deterministically. +- **Frontend Channel** – Delivers JSON CDP payloads between the RNW host and DevTools. +- **Inspector Thread** – A single `ReactInspectorThread` ensures CDP work is serialized away from + the UI and JS queues. (iOS and Andrtoid use UI thread.) +- **Debugger Notifications** – `DebuggerNotifications.h` broadcasts pause/resume events so view + hosts can react (e.g., showing overlays or resuming when the debugger continues). + +## Windows Integration Points +- **ReactHost & ReactOptions** – `ReactHost` creates the `HostTarget`, exposes it through + `ReactOptions::InspectorHostTarget`, and implements reload/pause hooks. This is the jump-off point + for all inspector traffic. The inspector supported only if the `UseDirectDebugger` is true. +- **ReactInstanceWin / OInstance** – Register and unregister instance/runtime targets around runtime + creation, keeping the inspector aligned with bridgeless and legacy bridge lifecycles. +- **DevSupportManager & Packager** – `DevSupportManager` now spins up + `ReactInspectorPackagerConnectionDelegate`, allowing Metro to broker modern inspector connections + and reuse the existing websocket infrastructure. +- **Dev Console Shortcut** – Metro’s `J` shortcut launches the inspector for Windows apps, matching + upstream behavior. + +## UI Overlays +- **Composition** – `DebuggerUIIsland` renders pause overlays, focus chrome, and selection adorners + whenever the runtime is paused. +- **Paper** – `ReactRootView` updates provide the same pause/selection affordances. + +## Hermes Runtime Integration +- `HermesRuntimeTargetDelegate` and `HermesRuntimeAgentDelegate` wrap the hermes-windows C debug API + so we can re-use upstream modern inspector code. +- `RuntimeHolder`/`HermesRuntimeHolder` surface a `createRuntimeTargetDelegate` hook that instantiates + delegates only when the inspector is enabled, and safely tears them down during reloads. + +## Packager & Console Integration +- `ReactInspectorPackagerConnectionDelegate` maps the Metro websocket APIs to the modern inspector + handshake. +- Console output, CPU sampling, and memory profiling are forwarded through the Hermes inspector + plumbing automatically once a session is active. + +## Using the Modern Inspector with RNW +1. Start your Metro bundler (`npx react-native start` or `yarn start`). +2. Launch your RNW app (Paper or Composition). +3. In the Metro console, press `J` to open the modern inspector URL in a browser. +4. Chrome DevTools will connect to the Hermes runtime. Pause execution, explore the component tree, + and capture profiles as needed. +5. When execution is paused, the corresponding overlay is rendered in the app window; resume to clear + the overlay. + +## Known Limitations & Follow-Up Work +- **Network profiling** – The `NetworkIOAgent` is not wired up yet for Windows. The integration point + is the `ReactInspectorHostTargetDelegate` in + `vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp`; override `loadNetworkResource` there to + forward requests through a Windows HTTP helper (similar to `GetJavaScriptFromServerAsync`) and + stream results back via the provided `NetworkRequestListener`. Until this happens, the Network tab + in DevTools stays empty. +- **Legacy Chakra runtime** – Modern inspector support currently targets Hermes. Chakra-based apps + continue to rely on legacy debugging flows. diff --git a/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.chakra.lock.json b/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.chakra.lock.json index 091352742c4..006baa72de2 100644 --- a/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.chakra.lock.json +++ b/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.chakra.lock.json @@ -29,8 +29,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -72,7 +72,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.lock.json b/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.lock.json index 091352742c4..006baa72de2 100644 --- a/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.lock.json +++ b/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.lock.json @@ -29,8 +29,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -72,7 +72,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.newarch.lock.json b/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.newarch.lock.json index 88a18eb0b26..4e71702ddc6 100644 --- a/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.newarch.lock.json +++ b/packages/@react-native-windows/automation-channel/windows/AutomationChannel/packages.newarch.lock.json @@ -37,8 +37,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -85,7 +85,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric.Package/packages.lock.json b/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric.Package/packages.lock.json index e75dd28fcc9..9ba83dc5121 100644 --- a/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric.Package/packages.lock.json +++ b/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric.Package/packages.lock.json @@ -14,8 +14,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -85,7 +85,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", @@ -103,7 +103,7 @@ "type": "Project", "dependencies": { "AutomationChannel": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.ReactNative": "[1.0.0, )", "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", diff --git a/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/packages.lock.json b/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/packages.lock.json index 72ce10d311b..43da89c8432 100644 --- a/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/packages.lock.json +++ b/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.VCRTForwarders.140": { "type": "Direct", @@ -95,7 +95,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/e2e-test-app/windows/RNTesterApp/packages.chakra.lock.json b/packages/e2e-test-app/windows/RNTesterApp/packages.chakra.lock.json index 1298ee91e66..8f5b69d29b1 100644 --- a/packages/e2e-test-app/windows/RNTesterApp/packages.chakra.lock.json +++ b/packages/e2e-test-app/windows/RNTesterApp/packages.chakra.lock.json @@ -46,8 +46,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -197,7 +197,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json b/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json index 4dac7070481..9526d7f5461 100644 --- a/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json +++ b/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json @@ -4,9 +4,9 @@ "UAP,Version=v10.0.17763": { "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.NETCore.UniversalWindowsPlatform": { "type": "Direct", @@ -198,7 +198,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/integration-test-app/windows/InteropTestModuleCS/packages.chakra.lock.json b/packages/integration-test-app/windows/InteropTestModuleCS/packages.chakra.lock.json index 98062fb9eed..1d3d9a8eb72 100644 --- a/packages/integration-test-app/windows/InteropTestModuleCS/packages.chakra.lock.json +++ b/packages/integration-test-app/windows/InteropTestModuleCS/packages.chakra.lock.json @@ -26,8 +26,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -178,7 +178,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/integration-test-app/windows/InteropTestModuleCS/packages.lock.json b/packages/integration-test-app/windows/InteropTestModuleCS/packages.lock.json index 98062fb9eed..1d3d9a8eb72 100644 --- a/packages/integration-test-app/windows/InteropTestModuleCS/packages.lock.json +++ b/packages/integration-test-app/windows/InteropTestModuleCS/packages.lock.json @@ -26,8 +26,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -178,7 +178,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/integration-test-app/windows/integrationtest/packages.chakra.lock.json b/packages/integration-test-app/windows/integrationtest/packages.chakra.lock.json index 828eabed2c8..24399a4161c 100644 --- a/packages/integration-test-app/windows/integrationtest/packages.chakra.lock.json +++ b/packages/integration-test-app/windows/integrationtest/packages.chakra.lock.json @@ -29,8 +29,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -199,7 +199,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/integration-test-app/windows/integrationtest/packages.lock.json b/packages/integration-test-app/windows/integrationtest/packages.lock.json index c885f8e587a..58b00ed3a30 100644 --- a/packages/integration-test-app/windows/integrationtest/packages.lock.json +++ b/packages/integration-test-app/windows/integrationtest/packages.lock.json @@ -4,9 +4,9 @@ "native,Version=v0.0": { "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.UI.Xaml": { "type": "Direct", @@ -200,7 +200,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/playground/windows/PlaygroundNativeModules/packages.lock.json b/packages/playground/windows/PlaygroundNativeModules/packages.lock.json index 091352742c4..006baa72de2 100644 --- a/packages/playground/windows/PlaygroundNativeModules/packages.lock.json +++ b/packages/playground/windows/PlaygroundNativeModules/packages.lock.json @@ -29,8 +29,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -72,7 +72,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/playground/windows/playground-composition.Package/packages.experimentalwinui3.lock.json b/packages/playground/windows/playground-composition.Package/packages.experimentalwinui3.lock.json index 54a47aafc49..8472064b28f 100644 --- a/packages/playground/windows/playground-composition.Package/packages.experimentalwinui3.lock.json +++ b/packages/playground/windows/playground-composition.Package/packages.experimentalwinui3.lock.json @@ -14,8 +14,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -76,7 +76,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", "ReactCommon": "[1.0.0, )", @@ -86,7 +86,7 @@ "playground-composition": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.ReactNative": "[1.0.0, )", "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", diff --git a/packages/playground/windows/playground-composition.Package/packages.lock.json b/packages/playground/windows/playground-composition.Package/packages.lock.json index a23f348d7fa..ea87593a461 100644 --- a/packages/playground/windows/playground-composition.Package/packages.lock.json +++ b/packages/playground/windows/playground-composition.Package/packages.lock.json @@ -14,8 +14,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -76,7 +76,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", @@ -86,7 +86,7 @@ "playground-composition": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.ReactNative": "[1.0.0, )", "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", diff --git a/packages/playground/windows/playground-composition/packages.experimentalwinui3.lock.json b/packages/playground/windows/playground-composition/packages.experimentalwinui3.lock.json index 143b98b49da..dc0e73813b3 100644 --- a/packages/playground/windows/playground-composition/packages.experimentalwinui3.lock.json +++ b/packages/playground/windows/playground-composition/packages.experimentalwinui3.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.VCRTForwarders.140": { "type": "Direct", @@ -86,7 +86,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/playground/windows/playground-composition/packages.lock.json b/packages/playground/windows/playground-composition/packages.lock.json index df9b70406dd..8fb2da00945 100644 --- a/packages/playground/windows/playground-composition/packages.lock.json +++ b/packages/playground/windows/playground-composition/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.VCRTForwarders.140": { "type": "Direct", @@ -86,7 +86,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/playground/windows/playground/packages.lock.json b/packages/playground/windows/playground/packages.lock.json index 5c69020f59f..9da2e1f071b 100644 --- a/packages/playground/windows/playground/packages.lock.json +++ b/packages/playground/windows/playground/packages.lock.json @@ -4,9 +4,9 @@ "native,Version=v0.0": { "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.UI.Xaml": { "type": "Direct", @@ -73,7 +73,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-app-fabric/windows/SampleAppFabric.Package/packages.lock.json b/packages/sample-app-fabric/windows/SampleAppFabric.Package/packages.lock.json index 7d84a365b4f..d64940b51e4 100644 --- a/packages/sample-app-fabric/windows/SampleAppFabric.Package/packages.lock.json +++ b/packages/sample-app-fabric/windows/SampleAppFabric.Package/packages.lock.json @@ -14,8 +14,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -76,7 +76,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", @@ -93,7 +93,7 @@ "sampleappfabric": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.ReactNative": "[1.0.0, )", "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", diff --git a/packages/sample-app-fabric/windows/SampleAppFabric/packages.lock.json b/packages/sample-app-fabric/windows/SampleAppFabric/packages.lock.json index 48426cb9ce0..83d8e870d2a 100644 --- a/packages/sample-app-fabric/windows/SampleAppFabric/packages.lock.json +++ b/packages/sample-app-fabric/windows/SampleAppFabric/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.VCRTForwarders.140": { "type": "Direct", @@ -86,7 +86,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-apps/windows/SampleAppCPP/packages.lock.json b/packages/sample-apps/windows/SampleAppCPP/packages.lock.json index b630317fbe8..2d62f83e9ef 100644 --- a/packages/sample-apps/windows/SampleAppCPP/packages.lock.json +++ b/packages/sample-apps/windows/SampleAppCPP/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.UI.Xaml": { "type": "Direct", @@ -186,7 +186,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-apps/windows/SampleAppCS/packages.lock.json b/packages/sample-apps/windows/SampleAppCS/packages.lock.json index 556fb03525f..7208c87546d 100644 --- a/packages/sample-apps/windows/SampleAppCS/packages.lock.json +++ b/packages/sample-apps/windows/SampleAppCS/packages.lock.json @@ -4,9 +4,9 @@ "UAP,Version=v10.0.17763": { "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.NETCore.UniversalWindowsPlatform": { "type": "Direct", @@ -180,7 +180,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-apps/windows/SampleLibraryCPP/packages.lock.json b/packages/sample-apps/windows/SampleLibraryCPP/packages.lock.json index 091352742c4..006baa72de2 100644 --- a/packages/sample-apps/windows/SampleLibraryCPP/packages.lock.json +++ b/packages/sample-apps/windows/SampleLibraryCPP/packages.lock.json @@ -29,8 +29,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -72,7 +72,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-apps/windows/SampleLibraryCS/packages.lock.json b/packages/sample-apps/windows/SampleLibraryCS/packages.lock.json index 98062fb9eed..1d3d9a8eb72 100644 --- a/packages/sample-apps/windows/SampleLibraryCS/packages.lock.json +++ b/packages/sample-apps/windows/SampleLibraryCS/packages.lock.json @@ -26,8 +26,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -178,7 +178,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/packages.experimentalwinui3.lock.json b/packages/sample-custom-component/windows/SampleCustomComponent/packages.experimentalwinui3.lock.json index b868a531010..c765f63ba80 100644 --- a/packages/sample-custom-component/windows/SampleCustomComponent/packages.experimentalwinui3.lock.json +++ b/packages/sample-custom-component/windows/SampleCustomComponent/packages.experimentalwinui3.lock.json @@ -37,8 +37,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -85,7 +85,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", "ReactCommon": "[1.0.0, )", diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json b/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json index 29fbc28aeda..957ffef6cca 100644 --- a/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json +++ b/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json @@ -37,8 +37,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -85,7 +85,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Desktop.ABITests/packages.experimentalwinui3.lock.json b/vnext/Desktop.ABITests/packages.experimentalwinui3.lock.json index e0c1749a3d9..05edf1a7a58 100644 --- a/vnext/Desktop.ABITests/packages.experimentalwinui3.lock.json +++ b/vnext/Desktop.ABITests/packages.experimentalwinui3.lock.json @@ -32,8 +32,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -96,7 +96,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", @@ -108,7 +108,7 @@ "react.windows.desktop.dll": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "React.Windows.Desktop": "[1.0.0, )", "ReactNative.V8Jsi.Windows": "[0.71.8, )", diff --git a/vnext/Desktop.ABITests/packages.lock.json b/vnext/Desktop.ABITests/packages.lock.json index 60d4d8a65eb..fb08ef502b5 100644 --- a/vnext/Desktop.ABITests/packages.lock.json +++ b/vnext/Desktop.ABITests/packages.lock.json @@ -32,8 +32,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -96,7 +96,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", @@ -108,7 +108,7 @@ "react.windows.desktop.dll": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "React.Windows.Desktop": "[1.0.0, )", "ReactNative.V8Jsi.Windows": "[0.71.8, )", diff --git a/vnext/Desktop.DLL/packages.experimentalwinui3.lock.json b/vnext/Desktop.DLL/packages.experimentalwinui3.lock.json index 0d149f18d17..9c5d8fc6c14 100644 --- a/vnext/Desktop.DLL/packages.experimentalwinui3.lock.json +++ b/vnext/Desktop.DLL/packages.experimentalwinui3.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -87,7 +87,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", diff --git a/vnext/Desktop.DLL/packages.lock.json b/vnext/Desktop.DLL/packages.lock.json index cf3655879d1..23d5b6288df 100644 --- a/vnext/Desktop.DLL/packages.lock.json +++ b/vnext/Desktop.DLL/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -87,7 +87,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", diff --git a/vnext/Desktop.IntegrationTests/packages.experimentalwinui3.lock.json b/vnext/Desktop.IntegrationTests/packages.experimentalwinui3.lock.json index 28010434691..595e7c3eefc 100644 --- a/vnext/Desktop.IntegrationTests/packages.experimentalwinui3.lock.json +++ b/vnext/Desktop.IntegrationTests/packages.experimentalwinui3.lock.json @@ -39,8 +39,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -98,7 +98,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", @@ -110,7 +110,7 @@ "react.windows.desktop.dll": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "React.Windows.Desktop": "[1.0.0, )", "ReactNative.V8Jsi.Windows": "[0.71.8, )", diff --git a/vnext/Desktop.IntegrationTests/packages.lock.json b/vnext/Desktop.IntegrationTests/packages.lock.json index 8da4fb3d4d8..63475edf6d8 100644 --- a/vnext/Desktop.IntegrationTests/packages.lock.json +++ b/vnext/Desktop.IntegrationTests/packages.lock.json @@ -39,8 +39,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -98,7 +98,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", @@ -110,7 +110,7 @@ "react.windows.desktop.dll": { "type": "Project", "dependencies": { - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "React.Windows.Desktop": "[1.0.0, )", "ReactNative.V8Jsi.Windows": "[0.71.8, )", diff --git a/vnext/Desktop.UnitTests/packages.experimentalwinui3.lock.json b/vnext/Desktop.UnitTests/packages.experimentalwinui3.lock.json index c0f2dc1e163..0adab5b3a4b 100644 --- a/vnext/Desktop.UnitTests/packages.experimentalwinui3.lock.json +++ b/vnext/Desktop.UnitTests/packages.experimentalwinui3.lock.json @@ -27,8 +27,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -96,7 +96,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", diff --git a/vnext/Desktop.UnitTests/packages.lock.json b/vnext/Desktop.UnitTests/packages.lock.json index 62ab93c2809..d25873e3339 100644 --- a/vnext/Desktop.UnitTests/packages.lock.json +++ b/vnext/Desktop.UnitTests/packages.lock.json @@ -27,8 +27,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -96,7 +96,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", diff --git a/vnext/Desktop/packages.experimentalwinui3.lock.json b/vnext/Desktop/packages.experimentalwinui3.lock.json index 489cd101365..53e2a97e58e 100644 --- a/vnext/Desktop/packages.experimentalwinui3.lock.json +++ b/vnext/Desktop/packages.experimentalwinui3.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/Desktop/packages.lock.json b/vnext/Desktop/packages.lock.json index 55d4a1196bd..70e16869c0f 100644 --- a/vnext/Desktop/packages.lock.json +++ b/vnext/Desktop/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/Directory.Build.props b/vnext/Directory.Build.props index 52be548f0b3..2061ed37e58 100644 --- a/vnext/Directory.Build.props +++ b/vnext/Directory.Build.props @@ -28,7 +28,7 @@ 10.1.0 ca2e3685b160617d3d95fcd9e789c4e06ca88 - 980cb60d7911237d0f647fc566543ef627adac70 + 21b47f08b762b21b1d4d970940ab23f59f43249c $(MSBuildThisFileDirectory)stubs - 980cb60d7911237d0f647fc566543ef627adac70 + 21b47f08b762b21b1d4d970940ab23f59f43249c true $(MSBuildThisFileDirectory) $(ReactNativeDir)\..\..\node_modules\.node-api-jsi\node-api-jsi-$(NodeApiJsiCommitHash)\ diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/packages.experimentalwinui3.lock.json b/vnext/Microsoft.ReactNative.IntegrationTests/packages.experimentalwinui3.lock.json index 88e3b3b8302..29f2cb8342e 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/packages.experimentalwinui3.lock.json +++ b/vnext/Microsoft.ReactNative.IntegrationTests/packages.experimentalwinui3.lock.json @@ -33,8 +33,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -84,7 +84,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/packages.lock.json b/vnext/Microsoft.ReactNative.IntegrationTests/packages.lock.json index 88e3b3b8302..29f2cb8342e 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.IntegrationTests/packages.lock.json @@ -33,8 +33,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -84,7 +84,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.experimentalwinui3.lock.json b/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.experimentalwinui3.lock.json index ada7ad87276..018fcfd93b5 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.experimentalwinui3.lock.json +++ b/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.experimentalwinui3.lock.json @@ -43,8 +43,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -91,7 +91,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.lock.json b/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.lock.json index 599f7fe352e..e9634c96aad 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.lock.json +++ b/vnext/Microsoft.ReactNative.IntegrationTests/packages.newarch.lock.json @@ -43,8 +43,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -91,7 +91,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative.Managed.IntegrationTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.IntegrationTests/packages.lock.json index e9032e277e4..afc979a07a8 100644 --- a/vnext/Microsoft.ReactNative.Managed.IntegrationTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.IntegrationTests/packages.lock.json @@ -56,8 +56,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -332,7 +332,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json index e9032e277e4..afc979a07a8 100644 --- a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json @@ -56,8 +56,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -332,7 +332,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json index 97fba5a88fa..202c208c15e 100644 --- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json @@ -36,8 +36,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", @@ -179,7 +179,7 @@ "dependencies": { "Common": "[1.0.0, )", "Folly": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.UI.Xaml": "[2.8.0, )", "ReactCommon": "[1.0.0, )", diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.cpp new file mode 100644 index 00000000000..eb23179c9ad --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.cpp @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "DebuggerUIIsland.h" + +#include +#include +#include +#include +#include +#include +#include "CompositionContextHelper.h" +#include "TextDrawing.h" + +namespace winrt::Microsoft::ReactNative::implementation { + +constexpr float debuggerUIFontSize = 10.0f; +constexpr float debuggerTextMargin = 4.0f; + +DebuggerUIIsland::DebuggerUIIsland( + const winrt::Microsoft::UI::Composition::Compositor &compositor, + winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, + winrt::Microsoft::ReactNative::Composition::Theme theme) noexcept + : m_compositor(compositor), m_compContext(compContext), m_theme(theme) { + m_backgroundVisual = m_compositor.CreateSpriteVisual(); + m_backgroundVisual.RelativeSizeAdjustment({1.0f, 1.0f}); + + auto backgroundBrush = m_compositor.CreateColorBrush({100, 0, 0, 0}); + m_backgroundVisual.Brush(backgroundBrush); + + m_TextVisual = m_compositor.CreateSpriteVisual(); + m_TextVisual.IsPixelSnappingEnabled(true); + + m_backgroundVisual.Children().InsertAtTop(m_TextVisual); +} + +DebuggerUIIsland::~DebuggerUIIsland() noexcept { + m_island.StateChanged(m_islandStateChangedToken); +} + +void DebuggerUIIsland::Redraw() noexcept { + if (!m_island) + return; + + if (m_island.ActualSize().x == 0 || m_island.ActualSize().y == 0) + return; + + auto scaleFactor = m_island.Environment().DisplayScale(); + + auto attributedString = facebook::react::AttributedString{}; + auto fragment = facebook::react::AttributedString::Fragment{}; + fragment.string = m_message; + fragment.textAttributes.fontSize = debuggerUIFontSize; + attributedString.appendFragment(std::move(fragment)); + + // Resume Icon + auto iconFragment = facebook::react::AttributedString::Fragment{}; + iconFragment.string = " \uF08F"; + iconFragment.textAttributes.fontFamily = "Segoe Fluent Icons"; + iconFragment.textAttributes.fontSize = debuggerUIFontSize; + attributedString.appendFragment(std::move(iconFragment)); + + auto attributedStringBox = facebook::react::AttributedStringBox{attributedString}; + + facebook::react::LayoutConstraints constraints; + constraints.maximumSize.width = std::max(0.0f, m_island.ActualSize().x - debuggerTextMargin * 2 * scaleFactor); + constraints.maximumSize.height = std::max(0.0f, m_island.ActualSize().y - debuggerTextMargin * 2 * scaleFactor); + + auto textAttributes = facebook::react::TextAttributes{}; + textAttributes.foregroundColor = facebook::react::blackColor(); + + winrt::com_ptr<::IDWriteTextLayout> textLayout; + facebook::react::WindowsTextLayoutManager::GetTextLayout(attributedStringBox, {}, constraints, textLayout); + + DWRITE_TEXT_METRICS tm; + textLayout->GetMetrics(&tm); + + winrt::Windows::Foundation::Size surfaceSize = { + std::ceilf(std::min(constraints.maximumSize.width, tm.width + debuggerTextMargin * 2 * scaleFactor)), + std::ceilf(std::min(constraints.maximumSize.height, tm.height + debuggerTextMargin * 2 * scaleFactor))}; + auto drawingSurface = m_compContext.CreateDrawingSurfaceBrush( + surfaceSize, + winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, + winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); + + POINT offset; + { + ::Microsoft::ReactNative::Composition::AutoDrawDrawingSurface autoDraw(drawingSurface, scaleFactor, &offset); + if (auto d2dDeviceContext = autoDraw.GetRenderTarget()) { + d2dDeviceContext->Clear(D2D1::ColorF{1.0f, 1.0f, 0.76f, 1.0f}); + + auto theme = winrt::get_self(m_theme); + + Composition::RenderText( + *d2dDeviceContext, + *textLayout, + attributedStringBox.getValue(), + textAttributes, + {static_cast(offset.x + std::floorf(debuggerTextMargin * scaleFactor)), + static_cast(offset.y + std::floorf(debuggerTextMargin * scaleFactor))}, + scaleFactor, + *theme); + } + + drawingSurface.HorizontalAlignmentRatio(0.0f); + drawingSurface.Stretch(winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::None); + + m_TextVisual.Brush(winrt::Microsoft::ReactNative::Composition::Experimental::implementation:: + MicrosoftCompositionContextHelper::InnerBrush(drawingSurface)); + m_TextVisual.Size({surfaceSize.Width, surfaceSize.Height}); + + m_debuggerHitRect = { + m_island.ActualSize().x / 2 - tm.width / 2 + debuggerTextMargin * scaleFactor, + debuggerTextMargin * scaleFactor, + surfaceSize.Width, + surfaceSize.Height}; + + m_TextVisual.Offset({m_debuggerHitRect.X, m_debuggerHitRect.Y, 0.0f}); + } +} + +void DebuggerUIIsland::Message(std::string &&value) noexcept { + m_message = value; + Redraw(); +} + +winrt::Microsoft::UI::Content::ContentIsland DebuggerUIIsland::Island() noexcept { + if (!m_island) { + m_island = winrt::Microsoft::UI::Content::ContentIsland::Create(m_backgroundVisual); + + m_islandStateChangedToken = + m_island.StateChanged([weakThis = weak_from_this()]( + winrt::Microsoft::UI::Content::ContentIsland const &island, + winrt::Microsoft::UI::Content::ContentIslandStateChangedEventArgs const &args) { + if (auto pThis = weakThis.lock()) { + if (args.DidRasterizationScaleChange() || args.DidActualSizeChange()) { + pThis->Redraw(); + } + } + }); + + auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(m_island); + + m_islandPointerUpToken = + pointerSource.PointerReleased([weakThis = weak_from_this()]( + winrt::Microsoft::UI::Input::InputPointerSource const &, + winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + if (auto pThis = weakThis.lock()) { + auto position = args.CurrentPoint().Position(); + if (position.X >= pThis->m_debuggerHitRect.X && position.Y >= pThis->m_debuggerHitRect.Y && + position.X <= pThis->m_debuggerHitRect.X + pThis->m_debuggerHitRect.Width && + position.Y <= pThis->m_debuggerHitRect.Y + pThis->m_debuggerHitRect.Height) { + pThis->m_resumedEvent(nullptr, nullptr); + } + } + }); + } + return m_island; +} + +winrt::event_token DebuggerUIIsland::Resumed( + winrt::Windows::Foundation::EventHandler const &handler) noexcept { + return m_resumedEvent.add(handler); +} +void DebuggerUIIsland::Resumed(winrt::event_token const &token) noexcept { + m_resumedEvent.remove(token); +} + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.h b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.h new file mode 100644 index 00000000000..9fc719ad0d1 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggerUIIsland.h @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +#include +#include + +namespace winrt::Microsoft::ReactNative::implementation { + +struct DebuggerUIIsland : std::enable_shared_from_this { + DebuggerUIIsland( + const winrt::Microsoft::UI::Composition::Compositor &compositor, + winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, + winrt::Microsoft::ReactNative::Composition::Theme theme) noexcept; + ~DebuggerUIIsland() noexcept; + winrt::Microsoft::UI::Content::ContentIsland Island() noexcept; + + void Message(std::string &&value) noexcept; + + winrt::event_token Resumed( + winrt::Windows::Foundation::EventHandler const &handler) noexcept; + void Resumed(winrt::event_token const &token) noexcept; + + private: + void Redraw() noexcept; + + winrt::event_token m_islandStateChangedToken; + winrt::event_token m_islandPointerUpToken; + + winrt::Microsoft::UI::Composition::SpriteVisual m_backgroundVisual{nullptr}; + winrt::Microsoft::UI::Composition::SpriteVisual m_TextVisual{nullptr}; + winrt::Windows::Foundation::Rect m_debuggerHitRect{0, 0, 0, 0}; + winrt::Microsoft::ReactNative::Composition::Theme m_theme{nullptr}; + std::string m_message; + + winrt::event> m_resumedEvent; + winrt::Microsoft::UI::Composition::Compositor m_compositor{nullptr}; + winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext{nullptr}; + winrt::Microsoft::UI::Content::ContentIsland m_island{nullptr}; +}; + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp index fd84303f8e1..84452bf67aa 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp @@ -103,9 +103,9 @@ void DebuggingOverlayComponentView::HandleCommand( auto rootVisual = root->OuterVisual(); while (m_activeOverlays != 0) { + --m_activeOverlays; auto visual = rootVisual.GetAt(root->overlayIndex() + m_activeOverlays); rootVisual.Remove(visual); - --m_activeOverlays; } } return; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp index 63829c6adab..c094cdc8c9b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp @@ -276,6 +276,7 @@ void ReactNativeIsland::UpdateRootVisualSize() noexcept { m_rootVisual.Size({m_size.Width * m_scaleFactor, m_size.Height * m_scaleFactor}); UpdateLoadingVisualSize(); + UpdateDebuggerVisualSize(); } void ReactNativeIsland::UpdateLoadingVisualSize() noexcept { @@ -292,6 +293,13 @@ void ReactNativeIsland::UpdateLoadingVisualSize() noexcept { } } +void ReactNativeIsland::UpdateDebuggerVisualSize() noexcept { + if (!m_debuggerChildSiteLink) + return; + + m_debuggerChildSiteLink.ActualSize(m_size); +} + float ReactNativeIsland::ScaleFactor() noexcept { return m_scaleFactor; } @@ -476,6 +484,20 @@ void ReactNativeIsland::InitRootView( m_CompositionEventHandler = std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this); m_CompositionEventHandler->Initialize(); + ::Microsoft::ReactNative::DebuggerNotifications::SubscribeShowDebuggerPausedOverlay( + m_context.Notifications().Handle(), + m_context.UIDispatcher().Handle(), + [weakThis = get_weak()](std::string message, std::function onResume) { + if (auto strongThis = weakThis.get()) { + strongThis->ShowDebuggerUI(message, onResume); + } + }, + [weakThis = get_weak()]() { + if (auto strongThis = weakThis.get()) { + strongThis->HideDebuggerUI(); + } + }); + UpdateRootViewInternal(); m_isInitialized = true; @@ -738,7 +760,10 @@ void ReactNativeIsland::ShowInstanceLoading() noexcept { NotifySizeChanged(); UpdateLoadingVisualSize(); - InternalRootVisual().InsertAt(m_loadingVisual, m_hasRenderedVisual ? 1 : 0); + // ShowDebuggerUI(); // TEMP + + InternalRootVisual().InsertAt( + m_loadingVisual, m_hasRenderedVisual ? (m_debuggerVisual ? 2 : 1) : (m_debuggerVisual ? 1 : 0)); } void ReactNativeIsland::InitTextScaleMultiplier() noexcept { @@ -759,6 +784,48 @@ void ReactNativeIsland::InitTextScaleMultiplier() noexcept { }); } +void ReactNativeIsland::ShowDebuggerUI(std::string message, const std::function &onResume) noexcept { + if (!m_debuggerVisual) { + auto compContext = + winrt::Microsoft::ReactNative::Composition::implementation::CompositionUIService::GetCompositionContext( + m_context.Properties().Handle()); + m_debuggerVisual = compContext.CreateSpriteVisual(); + + m_debuggerChildSiteLink = winrt::Microsoft::UI::Content::ChildSiteLink::Create( + Island(), + winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( + m_debuggerVisual) + .as()); + + m_debuggerUIIsland = std::make_shared(m_compositor, compContext, Theme()); + m_debuggerUIIsland->Message(std::string(message)); + m_debuggerVisual.RelativeSizeWithOffset({0.0f, 0.0f}, {1.0f, 1.0f}); + + m_debuggerUIIsland->Resumed( + [wkThis = get_weak(), onResume]( + const winrt::Windows::Foundation::IInspectable &, const winrt::Windows::Foundation::IInspectable &) { + if (auto pThis = wkThis.get()) { + // pThis->HideDebuggerUI(); + onResume(); + } + }); + + InternalRootVisual().InsertAt(m_debuggerVisual, m_hasRenderedVisual ? 1 : 0); + m_debuggerUIIsland->Island().IsHitTestVisibleWhenTransparent(false); + m_debuggerChildSiteLink.Connect(m_debuggerUIIsland->Island()); + } + + m_debuggerVisual.IsVisible(true); + + UpdateRootVisualSize(); +} + +void ReactNativeIsland::HideDebuggerUI() noexcept { + if (m_debuggerVisual) { + m_debuggerVisual.IsVisible(false); + } +} + winrt::Windows::Foundation::Size ReactNativeIsland::Measure( const winrt::Microsoft::ReactNative::LayoutConstraints &layoutConstraints, const winrt::Windows::Foundation::Point &viewportOffset) const { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h index 0a306125890..f0f132138f0 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h @@ -7,11 +7,14 @@ #include #include +#include #include #include #include +#include #include #include "CompositionEventHandler.h" +#include "DebuggerUIIsland.h" #include "PortalComponentView.h" #include "ReactHost/React.h" @@ -178,6 +181,9 @@ struct ReactNativeIsland std::shared_ptr<::Microsoft::ReactNative::CompositionEventHandler> m_CompositionEventHandler; winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_rootVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual m_loadingVisual{nullptr}; + winrt::Microsoft::UI::Content::ChildSiteLink m_debuggerChildSiteLink{nullptr}; + std::shared_ptr m_debuggerUIIsland; + winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual m_debuggerVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::Experimental::IActivityVisual m_loadingActivityVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader m_resources{nullptr}; winrt::Microsoft::ReactNative::Composition::Theme m_theme{nullptr}; @@ -193,8 +199,11 @@ struct ReactNativeIsland void ShowInstanceLoaded() noexcept; void ShowInstanceError() noexcept; void ShowInstanceLoading() noexcept; + void ShowDebuggerUI(std::string message, const std::function &onResume) noexcept; + void HideDebuggerUI() noexcept; void UpdateRootVisualSize() noexcept; void UpdateLoadingVisualSize() noexcept; + void UpdateDebuggerVisualSize() noexcept; Composition::Experimental::IDrawingSurfaceBrush CreateLoadingVisualBrush() noexcept; void ApplyConstraints( const winrt::Microsoft::ReactNative::LayoutConstraints &layoutConstraintsIn, diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp index f530668a7e1..a853f712d43 100644 --- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp @@ -165,6 +165,9 @@ void FabricUIManager::setProps(facebook::react::SurfaceId surfaceId, const folly } void FabricUIManager::stopSurface(facebook::react::SurfaceId surfaceId) noexcept { + if (surfaceId == -1) { + return; + } visit(surfaceId, [&](const facebook::react::SurfaceHandler &surfaceHandler) { surfaceHandler.stop(); m_scheduler->unregisterSurface(surfaceHandler); @@ -174,7 +177,9 @@ void FabricUIManager::stopSurface(facebook::react::SurfaceId surfaceId) noexcept std::unique_lock lock(m_handlerMutex); auto iterator = m_handlerRegistry.find(surfaceId); - m_handlerRegistry.erase(iterator); + if (iterator != m_handlerRegistry.end()) { + m_handlerRegistry.erase(iterator); + } } auto &rootDescriptor = m_registry.componentViewDescriptorWithTag(surfaceId); diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.cpp b/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.cpp index 9c7318103bd..945d28ee97e 100644 --- a/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.cpp @@ -12,8 +12,11 @@ #include #include -#include +#include #include +#include +#include +#include #include #include @@ -25,40 +28,88 @@ namespace facebook::react { -TaskDispatchThread::TaskDispatchThread(std::string threadName, int priorityOffset) noexcept - : threadName_(std::move(threadName)) { -#ifdef ANDROID - // Attaches the thread to JVM just in case anything calls out to Java - thread_ = std::thread([&]() { - facebook::jni::ThreadScope::WithClassLoader([&]() { - int result = setpriority(PRIO_PROCESS, static_cast(::syscall(SYS_gettid)), priorityOffset); +class TaskDispatchThread::Impl : public std::enable_shared_from_this { + public: + Impl(std::string &&threadName) noexcept; + ~Impl() noexcept; + + void start() noexcept; + bool isOnThread() noexcept; + bool isRunning() noexcept; + void runAsync(TaskFn &&task, std::chrono::milliseconds delayMs = std::chrono::milliseconds::zero()) noexcept; + void runSync(TaskFn &&task) noexcept; + void quit() noexcept; + void loop() noexcept; + + private: + struct Task { + TimePoint dispatchTime; + TaskFn fn; + + Task(TimePoint dispatchTime, TaskFn &&fn) : dispatchTime(dispatchTime), fn(std::move(fn)) {} + + bool operator<(const Task &other) const { + // Have the earliest tasks be at the front of the queue. + return dispatchTime > other.dispatchTime; + } + }; + + std::mutex queueLock_; + std::condition_variable loopCv_; + std::priority_queue queue_; + std::atomic running_{true}; + std::string threadName_; + std::thread thread_; +}; + +TaskDispatchThread::TaskDispatchThread(std::string threadName, int /*priorityOffset*/) noexcept + : impl_(std::make_shared(std::move(threadName))) { + impl_->start(); +} - if (result != 0) { - LOG(INFO) << " setCurrentThreadPriority failed with pri errno: " << errno; - } +TaskDispatchThread::~TaskDispatchThread() noexcept { + impl_->quit(); +} - loop(); - }); - }); +bool TaskDispatchThread::isOnThread() noexcept { + return impl_->isOnThread(); +} -#else - thread_ = std::thread(&TaskDispatchThread::loop, this); -#endif +bool TaskDispatchThread::isRunning() noexcept { + return impl_->isRunning(); } -TaskDispatchThread::~TaskDispatchThread() noexcept { +void TaskDispatchThread::runAsync(TaskFn &&task, std::chrono::milliseconds delayMs) noexcept { + impl_->runAsync(std::move(task), delayMs); +} + +void TaskDispatchThread::runSync(TaskFn &&task) noexcept { + impl_->runSync(std::move(task)); +} + +void TaskDispatchThread::quit() noexcept { + impl_->quit(); +} + +TaskDispatchThread::Impl::Impl(std::string &&threadName) noexcept : threadName_(std::move(threadName)) {} + +TaskDispatchThread::Impl::~Impl() noexcept { quit(); } -bool TaskDispatchThread::isOnThread() noexcept { +void TaskDispatchThread::Impl::start() noexcept { + thread_ = std::thread([self = shared_from_this()]() { self->loop(); }); +} + +bool TaskDispatchThread::Impl::isOnThread() noexcept { return std::this_thread::get_id() == thread_.get_id(); } -bool TaskDispatchThread::isRunning() noexcept { +bool TaskDispatchThread::Impl::isRunning() noexcept { return running_; } -void TaskDispatchThread::runAsync(TaskFn &&task, std::chrono::milliseconds delayMs) noexcept { +void TaskDispatchThread::Impl::runAsync(TaskFn &&task, std::chrono::milliseconds delayMs) noexcept { if (!running_) { return; } @@ -68,7 +119,7 @@ void TaskDispatchThread::runAsync(TaskFn &&task, std::chrono::milliseconds delay loopCv_.notify_one(); } -void TaskDispatchThread::runSync(TaskFn &&task) noexcept { +void TaskDispatchThread::Impl::runSync(TaskFn &&task) noexcept { std::promise promise; runAsync([&]() { if (running_) { @@ -79,7 +130,7 @@ void TaskDispatchThread::runSync(TaskFn &&task) noexcept { promise.get_future().wait(); } -void TaskDispatchThread::quit() noexcept { +void TaskDispatchThread::Impl::quit() noexcept { if (!running_) { return; } @@ -94,7 +145,7 @@ void TaskDispatchThread::quit() noexcept { } } -void TaskDispatchThread::loop() noexcept { +void TaskDispatchThread::Impl::loop() noexcept { if (!threadName_.empty()) { folly::setThreadName(threadName_); } diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.h b/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.h index 1f161ca6997..38fd14aca49 100644 --- a/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.h +++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.h @@ -11,11 +11,8 @@ #pragma once #include -#include #include -#include -#include -#include +#include namespace facebook::react { @@ -47,27 +44,9 @@ class TaskDispatchThread { /** Shut down and clean up the thread. */ void quit() noexcept; - protected: - struct Task { - TimePoint dispatchTime; - TaskFn fn; - - Task(TimePoint dispatchTime, TaskFn &&fn) : dispatchTime(dispatchTime), fn(std::move(fn)) {} - - bool operator<(const Task &other) const { - // Have the earliest tasks be at the front of the queue. - return dispatchTime > other.dispatchTime; - } - }; - - void loop() noexcept; - - std::mutex queueLock_; - std::condition_variable loopCv_; - std::priority_queue queue_; - std::atomic running_{true}; - std::string threadName_; - std::thread thread_; + private: + class Impl; + std::shared_ptr impl_; }; } // namespace facebook::react diff --git a/vnext/Microsoft.ReactNative/JsiApi.cpp b/vnext/Microsoft.ReactNative/JsiApi.cpp index d074fff39f7..3408bd28163 100644 --- a/vnext/Microsoft.ReactNative/JsiApi.cpp +++ b/vnext/Microsoft.ReactNative/JsiApi.cpp @@ -479,7 +479,7 @@ facebook::jsi::JSError const &jsError) { \ }(); )JS"); // TODO: consider implementing this script as a resource file and loading it with the resource URL. - jsiRuntime->evaluateJavaScript(jsiPalBuffer, "Form_JSI_API_not_a_real_file"); + jsiRuntime->evaluateJavaScript(jsiPalBuffer, "jsi-internal://host-function-manager.js"); ReactNative::JsiRuntime abiJsiResult{make(Mso::Copy(jsiRuntimeHolder), Mso::Copy(jsiRuntime))}; std::scoped_lock lock{s_mutex}; auto it = s_jsiRuntimeMap.try_emplace(reinterpret_cast(jsiRuntime.get()), abiJsiResult); diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index b50fab3cadb..f8097e89ed4 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -268,6 +268,7 @@ Code + diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index ad0c2b22282..e2480686490 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -313,6 +313,9 @@ ReactHost + + ReactHost + ReactHost diff --git a/vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h b/vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h new file mode 100644 index 00000000000..6a083140a34 --- /dev/null +++ b/vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +namespace Microsoft::ReactNative { + +struct DebuggerNotifications { + static winrt::Microsoft::ReactNative::IReactPropertyName ShowDebuggerPausedOverlayEventName() noexcept { + static winrt::Microsoft::ReactNative::IReactPropertyName propertyName{ + winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetName( + winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetNamespace(L"ReactNative.Debugger"), + L"ShowDebuggerPausedOverlay")}; + return propertyName; + } + + static void OnShowDebuggerPausedOverlay( + winrt::Microsoft::ReactNative::IReactNotificationService const &service, + std::string message, + std::function onResume) { + const winrt::Microsoft::ReactNative::ReactNonAbiValue>> nonAbiValue{ + std::in_place, std::tie(message, onResume)}; + service.SendNotification(ShowDebuggerPausedOverlayEventName(), nullptr, nonAbiValue); + } + + static void OnHideDebuggerPausedOverlay(winrt::Microsoft::ReactNative::IReactNotificationService const &service) { + service.SendNotification(ShowDebuggerPausedOverlayEventName(), nullptr, nullptr); + } + + static winrt::Microsoft::ReactNative::IReactNotificationSubscription SubscribeShowDebuggerPausedOverlay( + winrt::Microsoft::ReactNative::IReactNotificationService const &service, + winrt::Microsoft::ReactNative::IReactDispatcher const &dispatcher, + std::function)> showCallback, + std::function hideCallback) { + return service.Subscribe( + ShowDebuggerPausedOverlayEventName(), + dispatcher, + [showCallback, hideCallback](auto &&, winrt::Microsoft::ReactNative::IReactNotificationArgs const &args) { + if (args.Data()) { + const auto [message, onResume] = args.Data() + .as>>>() + .Value(); + showCallback(message, onResume); + } else { + hideCallback(); + } + }); + } +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/ReactHost/React.h b/vnext/Microsoft.ReactNative/ReactHost/React.h index dc7867f39b8..c7b8f8a15f1 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/React.h +++ b/vnext/Microsoft.ReactNative/ReactHost/React.h @@ -33,6 +33,10 @@ #include #endif +namespace facebook::react::jsinspector_modern { +class HostTarget; +} // namespace facebook::react::jsinspector_modern + namespace Mso::React { // Forward declarations @@ -216,9 +220,9 @@ struct ReactOptions { //! Base path of the SDX. The absolute path of the SDX can be constructed from this and the Identity. std::string BundleRootPath; - //! Javascript Bundles - //! This List includes both Platform and User Javascript Bundles - //! Bundles are loaded into Javascript engine in the same order + //! JavaScript Bundles + //! This List includes both Platform and User JavaScript Bundles + //! Bundles are loaded into JavaScript engine in the same order //! as they are specified in this list. std::vector> JSBundles; @@ -237,7 +241,7 @@ struct ReactOptions { //! during development to report JavaScript errors to users std::shared_ptr RedBoxHandler; - //! Flag to suggest sdx owner's preference on enabling Bytecode caching in Javascript Engine for corresponding SDX. + //! Flag to suggest sdx owner's preference on enabling Bytecode caching in JavaScript Engine for corresponding SDX. bool EnableBytecode{true}; //! Flag controlling whether the JavaScript engine uses JIT compilation. @@ -347,6 +351,9 @@ struct ReactOptions { //! The callback is called when IReactInstance is destroyed and must not be used anymore. //! It is called from the native queue. OnReactInstanceDestroyedCallback OnInstanceDestroyed; + + //! The HostTarget instance for modern inspector integration. + facebook::react::jsinspector_modern::HostTarget *InspectorHostTarget; }; //! IReactHost manages a ReactNative instance. diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp index 56ce0f97635..cb7737ae6ed 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp @@ -8,9 +8,16 @@ #include +#include +#include #include #include +#include "Inspector/ReactInspectorThread.h" +#include "ReactHost/DebuggerNotifications.h" + +using namespace facebook::react; + namespace Mso::React { //============================================================================================= @@ -282,6 +289,10 @@ bool ReactOptions::EnableDefaultCrashHandler() const noexcept { return winrt::unbox_value_or(properties.Get(EnableDefaultCrashHandlerProperty()), false); } +//============================================================================================= +// ReactNativeWindowsFeatureFlags implementation +//============================================================================================= + class ReactNativeWindowsFeatureFlags : public facebook::react::ReactNativeFeatureFlagsDefaults { public: bool enableBridgelessArchitecture() override { @@ -295,9 +306,59 @@ class ReactNativeWindowsFeatureFlags : public facebook::react::ReactNativeFeatur bool enableCppPropsIteratorSetter() override { return true; } + + bool fuseboxEnabledRelease() override { + return true; // Enable Fusebox (modern CDP backend) by default for React Native Windows + } + + bool fuseboxNetworkInspectionEnabled() override { + return true; // Enable network inspection support in Fusebox + } +}; + +//============================================================================================= +// ReactInspectorHostTargetDelegate implementation +//============================================================================================= + +class ReactInspectorHostTargetDelegate : public jsinspector_modern::HostTargetDelegate, + public std::enable_shared_from_this { + public: + ReactInspectorHostTargetDelegate(Mso::WeakPtr &&reactHost) noexcept : m_reactHost(std::move(reactHost)) {} + + jsinspector_modern::HostTargetMetadata getMetadata() override { + // TODO: (vmoroz) provide more info + return { + .integrationName = "React Native Windows (Host)", + }; + } + + void onReload(jsinspector_modern::HostTargetDelegate::PageReloadRequest const &request) override { + if (Mso::CntPtr reactHost = m_reactHost.GetStrongPtr()) { + reactHost->ReloadInstance(); + } + } + + void onSetPausedInDebuggerMessage( + jsinspector_modern::HostTargetDelegate::OverlaySetPausedInDebuggerMessageRequest const &request) override { + if (Mso::CntPtr reactHost = m_reactHost.GetStrongPtr()) { + auto notifications = reactHost->Options().Notifications; + if (request.message.has_value()) { + ::Microsoft::ReactNative::DebuggerNotifications::OnShowDebuggerPausedOverlay( + notifications, request.message.value(), [weakReactHost = m_reactHost]() { + if (Mso::CntPtr strongReactHost = weakReactHost.GetStrongPtr()) { + strongReactHost->OnDebuggerResume(); + } + }); + } else { + ::Microsoft::ReactNative::DebuggerNotifications::OnHideDebuggerPausedOverlay(notifications); + } + } + } + + private: + Mso::WeakPtr m_reactHost; }; -std::once_flag g_FlagInitFeatureFlags; //============================================================================================= // ReactHost implementation //============================================================================================= @@ -305,9 +366,16 @@ std::once_flag g_FlagInitFeatureFlags; ReactHost::ReactHost(Mso::DispatchQueue const &queue) noexcept : Super{EnsureSerialQueue(queue)}, m_options{Queue(), m_mutex}, - m_notifyWhenClosed{ReactHostRegistry::Register(*this), Queue(), m_mutex} { - std::call_once(g_FlagInitFeatureFlags, []() noexcept { - facebook::react::ReactNativeFeatureFlags::override(std::make_unique()); + m_notifyWhenClosed{ReactHostRegistry::Register(*this), Queue(), m_mutex}, + m_inspectorHostTargetDelegate{std::make_shared(this)}, + m_inspectorHostTarget{ + jsinspector_modern::HostTarget::create(*m_inspectorHostTargetDelegate, [](std::function &&callback) { + ::Microsoft::ReactNative::ReactInspectorThread::Instance().Post( + [callback = std::move(callback)]() { callback(); }); + })} { + static std::once_flag initFeatureFlagsOnce; + std::call_once(initFeatureFlagsOnce, []() noexcept { + ReactNativeFeatureFlags::override(std::make_unique()); }); } @@ -319,15 +387,16 @@ void ReactHost::Finalize() noexcept { // Since each AsyncAction has a strong ref count to ReactHost, the AsyncActionQueue must be empty. // Thus, we only need to call UnloadInQueue to unload ReactInstance if the ReactHost is not closed yet. if (Mso::Promise notifyWhenClosed = m_notifyWhenClosed.Exchange(nullptr)) { - UnloadInQueue(0).Then( - [notifyWhenClosed = std::move(notifyWhenClosed)]() noexcept { notifyWhenClosed.TrySetValue(); }); + UnloadInQueue(UnloadReason::CloseHost, 0) + .Then( + [notifyWhenClosed = std::move(notifyWhenClosed)]() noexcept { notifyWhenClosed.TrySetValue(); }); } } void ReactHost::Close() noexcept { InvokeInQueue([this]() noexcept { // Put the ReactHost to the closed state, unload ReactInstance, and notify the closing Promise. - auto whenClosed = m_actionQueue.Load()->PostAction(MakeUnloadInstanceAction()); + auto whenClosed = m_actionQueue.Load()->PostAction(MakeUnloadInstanceAction(UnloadReason::CloseHost)); // After we set the m_notifyWhenClosed to null, the ReactHost is considered to be closed. Mso::SetPromiseValue(m_notifyWhenClosed.Exchange(nullptr), std::move(whenClosed)); @@ -379,12 +448,14 @@ Mso::Future ReactHost::ReloadInstance() noexcept { Mso::Future ReactHost::ReloadInstanceWithOptions(ReactOptions &&options) noexcept { return PostInQueue([this, options = std::move(options)]() mutable noexcept { - return m_actionQueue.Load()->PostActions({MakeUnloadInstanceAction(), MakeLoadInstanceAction(std::move(options))}); + return m_actionQueue.Load()->PostActions( + {MakeUnloadInstanceAction(UnloadReason::Unload), MakeLoadInstanceAction(std::move(options))}); }); } Mso::Future ReactHost::UnloadInstance() noexcept { - return PostInQueue([this]() noexcept { return m_actionQueue.Load()->PostAction(MakeUnloadInstanceAction()); }); + return PostInQueue( + [this]() noexcept { return m_actionQueue.Load()->PostAction(MakeUnloadInstanceAction(UnloadReason::Unload)); }); } AsyncAction ReactHost::MakeLoadInstanceAction(ReactOptions &&options) noexcept { @@ -393,11 +464,13 @@ AsyncAction ReactHost::MakeLoadInstanceAction(ReactOptions &&options) noexcept { }; } -AsyncAction ReactHost::MakeUnloadInstanceAction() noexcept { +AsyncAction ReactHost::MakeUnloadInstanceAction(UnloadReason reason) noexcept { Mso::Internal::VerifyIsInQueueElseCrash(Queue()); size_t unloadActionId = ++m_nextUnloadActionId; m_pendingUnloadActionId = unloadActionId; - return [spThis = Mso::CntPtr{this}, unloadActionId]() noexcept { return spThis->UnloadInQueue(unloadActionId); }; + return [spThis = Mso::CntPtr{this}, reason, unloadActionId]() noexcept { + return spThis->UnloadInQueue(reason, unloadActionId); + }; } Mso::CntPtr ReactHost::MakeViewHost(ReactViewOptions &&options) noexcept { @@ -428,6 +501,18 @@ Mso::Future ReactHost::LoadInQueue(ReactOptions &&options) noexcept { return Mso::MakeCanceledFuture(); } + // Start or stop inspector page if needed. + // Make sure to update the both copies of options. + if (IsInspectable()) { + AddInspectorPage(); + options.InspectorHostTarget = m_inspectorHostTarget.get(); + m_options.Load().InspectorHostTarget = m_inspectorHostTarget.get(); + } else { + RemoveInspectorPage(); + options.InspectorHostTarget = nullptr; + m_options.Load().InspectorHostTarget = nullptr; + } + Mso::Promise whenCreated; Mso::Promise whenLoaded; @@ -463,7 +548,7 @@ Mso::Future ReactHost::LoadInQueue(ReactOptions &&options) noexcept { }); } -Mso::Future ReactHost::UnloadInQueue(size_t unloadActionId) noexcept { +Mso::Future ReactHost::UnloadInQueue(UnloadReason reason, size_t unloadActionId) noexcept { Mso::Internal::VerifyIsInQueueElseCrash(Queue()); // If the pending unload action Id does not match, then we have newer unload action, @@ -485,21 +570,25 @@ Mso::Future ReactHost::UnloadInQueue(size_t unloadActionId) noexcept { // We unload ReactInstance after all view instances are unloaded. // It is safe to capture 'this' because the Unload action keeps a strong reference to ReactHost. - return Mso::WhenAllCompleted(unloadCompletionList).Then(m_executor, [this](Mso::Maybe && /*value*/) noexcept { - Mso::Future onUnloaded; - if (auto reactInstance = m_reactInstance.Exchange(nullptr)) { - onUnloaded = reactInstance->Destroy(); - } - - m_isInstanceUnloading.Store(false); - m_lastError.Store({}); - - if (!onUnloaded) { - onUnloaded = Mso::MakeSucceededFuture(); - } - - return onUnloaded; - }); + return Mso::WhenAllCompleted(unloadCompletionList) + .Then(m_executor, [this, reason](Mso::Maybe && /*value*/) noexcept { + Mso::Future onUnloaded; + if (auto reactInstance = m_reactInstance.Exchange(nullptr)) { + onUnloaded = reactInstance->Destroy(); + } + + m_isInstanceUnloading.Store(false); + m_lastError.Store({}); + + if (!onUnloaded) { + onUnloaded = Mso::MakeSucceededFuture(); + } + + if (reason == UnloadReason::CloseHost) { + RemoveInspectorPage(); + } + return onUnloaded; + }); } void ReactHost::ForEachViewHost(const Mso::FunctorRef &action) noexcept { @@ -528,6 +617,54 @@ void ReactHost::DetachViewHost(ReactViewHost &viewHost) noexcept { viewHosts.erase(it); } +bool ReactHost::IsInspectable() noexcept { + ReactOptions &options = m_options.Load(); + return options.JsiEngine() == JSIEngine::Hermes && options.UseDirectDebugger(); +} + +void ReactHost::AddInspectorPage() noexcept { + std::optional &inspectorPageId = m_inspectorPageId.Load(); + if (inspectorPageId.has_value()) + return; + + jsinspector_modern::InspectorTargetCapabilities capabilities; + capabilities.nativePageReloads = true; + capabilities.prefersFuseboxFrontend = true; + // TODO: (vmoroz) improve the page name + inspectorPageId = jsinspector_modern::getInspectorInstance().addPage( + "React Native Windows (Experimental)", + "Hermes", + [weakInspectorHostTarget = + std::weak_ptr(m_inspectorHostTarget)](std::unique_ptr remote) + -> std::unique_ptr { + if (std::shared_ptr inspectorHostTarget = weakInspectorHostTarget.lock()) { + return inspectorHostTarget->connect(std::move(remote)); + } + + // This can happen if we're about to shut down. Reject the connection. + return nullptr; + }, + capabilities); +} + +void ReactHost::RemoveInspectorPage() noexcept { + std::optional &inspectorPageId = m_inspectorPageId.Load(); + if (!inspectorPageId.has_value()) + return; + + jsinspector_modern::getInspectorInstance().removePage(*inspectorPageId); + inspectorPageId.reset(); +} + +void ReactHost::OnDebuggerResume() noexcept { + ::Microsoft::ReactNative::ReactInspectorThread::Instance().Post( + [weakInspectorHostTarget = std::weak_ptr(m_inspectorHostTarget)]() { + if (std::shared_ptr inspectorHostTarget = weakInspectorHostTarget.lock()) { + inspectorHostTarget->sendCommand(jsinspector_modern::HostCommand::DebuggerResume); + } + }); +} + //============================================================================================= // ReactViewHost implementation //============================================================================================= @@ -579,8 +716,8 @@ Mso::Future ReactViewHost::AttachViewInstance(IReactViewInstance &viewInst m_reactHost->AttachViewHost(*this); return InitViewInstanceInQueue(); - //// Schedule the viewInstance load in the action queue since there can be other load actions in the queue that need - //// to be consolidated. + // Schedule the viewInstance load in the action queue since there can be other load actions in the queue that need + // to be consolidated. // return m_actionQueue.Load()->PostAction(MakeInitViewInstanceAction()); }); } diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactHost.h b/vnext/Microsoft.ReactNative/ReactHost/ReactHost.h index 765039d9542..b786772b233 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactHost.h +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactHost.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include "AsyncActionQueue.h" @@ -15,6 +16,7 @@ namespace Mso::React { +class ReactInspectorHostTargetDelegate; class ReactViewHost; //! ReactHost manages lifetime of ReactNative instance. @@ -52,9 +54,6 @@ class ReactHost final : public Mso::ActiveObject { Mso::CntPtr ActionQueue() const noexcept; - Mso::Future LoadInQueue(ReactOptions &&options) noexcept; - Mso::Future UnloadInQueue(size_t unloadActionId) noexcept; - void Close() noexcept; bool IsClosed() const noexcept; @@ -64,6 +63,12 @@ class ReactHost final : public Mso::ActiveObject { template Mso::Future PostInQueue(TCallback &&callback) noexcept; + private: + enum class UnloadReason { + Unload, + CloseHost, + }; + private: friend MakePolicy; ReactHost(Mso::DispatchQueue const &queue) noexcept; @@ -75,9 +80,18 @@ class ReactHost final : public Mso::ActiveObject { void ForEachViewHost(const Mso::FunctorRef &action) noexcept; AsyncAction MakeLoadInstanceAction(ReactOptions &&options) noexcept; - AsyncAction MakeUnloadInstanceAction() noexcept; + AsyncAction MakeUnloadInstanceAction(UnloadReason reason) noexcept; + + Mso::Future LoadInQueue(ReactOptions &&options) noexcept; + Mso::Future UnloadInQueue(UnloadReason reason, size_t unloadActionId) noexcept; + + void OnDebuggerResume() noexcept; + bool IsInspectable() noexcept; + void AddInspectorPage() noexcept; + void RemoveInspectorPage() noexcept; private: + friend class ReactInspectorHostTargetDelegate; mutable std::mutex m_mutex; const Mso::InvokeElsePostExecutor m_executor{Queue()}; const Mso::ActiveReadableField> m_actionQueue{ @@ -92,6 +106,10 @@ class ReactHost final : public Mso::ActiveObject { size_t m_pendingUnloadActionId{0}; size_t m_nextUnloadActionId{0}; const Mso::ActiveField m_isInstanceUnloading{false, Queue()}; + + const std::shared_ptr m_inspectorHostTargetDelegate; + const std::shared_ptr m_inspectorHostTarget; + const Mso::ActiveField> m_inspectorPageId{Queue()}; }; //! Implements a cross-platform host for a React view diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index aefaaa3db63..eb394fb3544 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -66,6 +66,7 @@ #include #include #include +#include "Inspector/ReactInspectorThread.h" #endif #if !defined(CORE_ABI) && !defined(USE_FABRIC) @@ -553,6 +554,8 @@ std::shared_ptr ReactInstanceWin::CreateDevSetting devSettings->useRuntimeScheduler = useRuntimeScheduler; + devSettings->inspectorHostTarget = m_options.InspectorHostTarget; + return devSettings; } @@ -664,16 +667,21 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { }; if (devSettings->useDirectDebugger) { - ::Microsoft::ReactNative::GetSharedDevManager()->EnsureHermesInspector( - devSettings->sourceBundleHost, devSettings->sourceBundlePort); + ::Microsoft::ReactNative::GetSharedDevManager()->EnsureInspectorPackagerConnection( + devSettings->sourceBundleHost, devSettings->sourceBundlePort, devSettings->bundleAppId); } m_jsiRuntimeHolder = std::make_shared( devSettings, jsMessageThread, CreatePreparedScriptStore()); auto jsRuntime = std::make_unique(m_jsiRuntimeHolder); jsRuntime->getRuntime(); - m_bridgelessReactInstance = std::make_unique( - std::move(jsRuntime), jsMessageThread, timerManager, jsErrorHandlingFunc); + + m_bridgelessReactInstance = std::make_shared( + std::move(jsRuntime), + jsMessageThread, + timerManager, + jsErrorHandlingFunc, + m_options.InspectorHostTarget); auto bufferedRuntimeExecutor = m_bridgelessReactInstance->getBufferedRuntimeExecutor(); timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); @@ -695,6 +703,7 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { winrt::make(Mso::Copy(m_reactContext))); facebook::react::ReactInstance::JSRuntimeFlags options; + m_bridgelessReactInstance->initializeRuntime( options, [=, onCreated = m_options.OnInstanceCreated, reactContext = m_reactContext]( @@ -748,7 +757,6 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { LoadJSBundlesBridgeless(devSettings); SetupHMRClient(); - } catch (std::exception &e) { OnErrorWithMessage(e.what()); OnErrorWithMessage("ReactInstanceWin: Failed to create React Instance."); @@ -1092,6 +1100,17 @@ Mso::Future ReactInstanceWin::Destroy() noexcept { if (m_bridgelessReactInstance) { if (auto jsMessageThread = m_jsMessageThread.Exchange(nullptr)) { jsMessageThread->runOnQueueSync([&]() noexcept { + // Unregister from inspector BEFORE shutting down JS thread + if (m_bridgelessReactInstance && m_options.InspectorHostTarget) { + Mso::React::MessageDispatchQueue messageDispatchQueue{ + ::Microsoft::ReactNative::ReactInspectorThread::Instance(), nullptr}; + messageDispatchQueue.runOnQueueSync( + [weakBridgelessReactInstance = std::weak_ptr(m_bridgelessReactInstance)]() { + if (auto bridgelessReactInstance = weakBridgelessReactInstance.lock()) { + bridgelessReactInstance->unregisterFromInspector(); + } + }); + } { // Release the JSI runtime std::scoped_lock lock{m_mutex}; diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h index 36c153df4e9..4d5125bc166 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h @@ -205,7 +205,7 @@ class ReactInstanceWin final : public Mso::ActiveObject #ifdef USE_FABRIC // Bridgeless - std::unique_ptr m_bridgelessReactInstance; + std::shared_ptr m_bridgelessReactInstance; #endif std::atomic m_state{ReactInstanceState::Loading}; diff --git a/vnext/Microsoft.ReactNative/ReactRootView.cpp b/vnext/Microsoft.ReactNative/ReactRootView.cpp index a697f76aa39..e04b0b813c2 100644 --- a/vnext/Microsoft.ReactNative/ReactRootView.cpp +++ b/vnext/Microsoft.ReactNative/ReactRootView.cpp @@ -5,14 +5,19 @@ #include "ReactRootView.g.cpp" #include +#include #include +#include +#include #include #include #include #include #include +#include "InstanceManager.h" #include "ReactNativeHost.h" #include "ReactViewInstance.h" +#include "Utils/KeyboardUtils.h" #include "XamlUtils.h" #include @@ -34,6 +39,7 @@ ReactRootView::ReactRootView() noexcept : m_uiQueue(Mso::DispatchQueue::GetCurre UpdatePerspective(); Loaded([this](auto &&, auto &&) { ::Microsoft::ReactNative::SetCompositor(::Microsoft::ReactNative::GetCompositor(*this)); + SetupDevToolsShortcut(); }); } @@ -45,6 +51,20 @@ void ReactRootView::ReactNativeHost(ReactNative::ReactNativeHost const &value) n if (m_reactNativeHost != value) { ReactViewHost(nullptr); m_reactNativeHost = value; + const auto weakThis = this->get_weak(); + ::Microsoft::ReactNative::DebuggerNotifications::SubscribeShowDebuggerPausedOverlay( + m_reactNativeHost.InstanceSettings().Notifications(), + m_reactNativeHost.InstanceSettings().UIDispatcher(), + [weakThis](std::string message, std::function onResume) { + if (auto strongThis = weakThis.get()) { + strongThis->ShowDebuggerPausedOverlay(message, onResume); + } + }, + [weakThis]() { + if (auto strongThis = weakThis.get()) { + strongThis->HideDebuggerPausedOverlay(); + } + }); ReloadView(); } } @@ -283,6 +303,65 @@ void ReactRootView::EnsureLoadingUI() noexcept { } } +void ReactRootView::HideDebuggerPausedOverlay() noexcept { + m_isDebuggerPausedOverlayOpen = false; + if (m_debuggerPausedFlyout) { + m_debuggerPausedFlyout.Hide(); + m_debuggerPausedFlyout = nullptr; + } +} + +void ReactRootView::ShowDebuggerPausedOverlay( + const std::string &message, + const std::function &onResume) noexcept { + // Initialize content + const xaml::Controls::Grid contentGrid; + xaml::Controls::ColumnDefinition messageColumnDefinition; + xaml::Controls::ColumnDefinition buttonColumnDefinition; + messageColumnDefinition.MinWidth(60); + buttonColumnDefinition.MinWidth(36); + contentGrid.ColumnDefinitions().Append(messageColumnDefinition); + contentGrid.ColumnDefinitions().Append(buttonColumnDefinition); + xaml::Controls::TextBlock messageBlock; + messageBlock.Text(winrt::to_hstring(message)); + messageBlock.FontWeight(winrt::Windows::UI::Text::FontWeights::SemiBold()); + xaml::Controls::FontIcon resumeGlyph; + resumeGlyph.FontFamily(xaml::Media::FontFamily(L"Segoe MDL2 Assets")); + resumeGlyph.Foreground(xaml::Media::SolidColorBrush(winrt::Colors::Green())); + resumeGlyph.Glyph(L"\uF5B0"); + resumeGlyph.HorizontalAlignment(xaml::HorizontalAlignment::Right); + resumeGlyph.PointerReleased([onResume](auto &&...) { onResume(); }); + xaml::Controls::Grid::SetColumn(resumeGlyph, 1); + contentGrid.Children().Append(messageBlock); + contentGrid.Children().Append(resumeGlyph); + + // Configure flyout + m_isDebuggerPausedOverlayOpen = true; + xaml::Style flyoutStyle( + {XAML_NAMESPACE_STR L".Controls.FlyoutPresenter", winrt::Windows::UI::Xaml::Interop::TypeKind::Metadata}); + flyoutStyle.Setters().Append(winrt::Setter( + xaml::Controls::Control::CornerRadiusProperty(), winrt::box_value(xaml::CornerRadius{12, 12, 12, 12}))); + flyoutStyle.Setters().Append(winrt::Setter( + xaml::Controls::Control::BackgroundProperty(), + winrt::box_value(xaml::Media::SolidColorBrush{FromArgb(255, 255, 255, 193)}))); + flyoutStyle.Setters().Append( + winrt::Setter(xaml::FrameworkElement::MarginProperty(), winrt::box_value(xaml::Thickness{0, 12, 0, 0}))); + m_debuggerPausedFlyout = xaml::Controls::Flyout{}; + m_debuggerPausedFlyout.FlyoutPresenterStyle(flyoutStyle); + m_debuggerPausedFlyout.LightDismissOverlayMode(xaml::Controls::LightDismissOverlayMode::On); + m_debuggerPausedFlyout.Content(contentGrid); + + // Disable light dismiss + m_debuggerPausedFlyout.Closing([weakThis = this->get_weak()](auto &&, const auto &args) { + if (auto strongThis = weakThis.get()) { + args.Cancel(strongThis->m_isDebuggerPausedOverlayOpen); + } + }); + + // Show flyout + m_debuggerPausedFlyout.ShowAt(*this); +} + void ReactRootView::ShowInstanceLoaded() noexcept { if (m_xamlRootView) { ClearLoadingUI(); @@ -481,4 +560,33 @@ void ReactRootView::RemoveChildAt(uint32_t index) { Children().RemoveAt(RNIndexToXamlIndex(index)); } +bool IsCtrlShiftI(winrt::Windows::System::VirtualKey key) noexcept { + return ( + key == winrt::Windows::System::VirtualKey::I && + ::Microsoft::ReactNative::IsModifiedKeyPressed( + winrt::CoreWindow::GetForCurrentThread(), winrt::Windows::System::VirtualKey::Shift) && + ::Microsoft::ReactNative::IsModifiedKeyPressed( + winrt::CoreWindow::GetForCurrentThread(), winrt::Windows::System::VirtualKey::Control)); +} + +void ReactRootView::SetupDevToolsShortcut() noexcept { + if (auto xamlRoot = XamlRoot()) { + if (std::find(m_subscribedDebuggerRoots.begin(), m_subscribedDebuggerRoots.end(), xamlRoot) == + m_subscribedDebuggerRoots.end()) { + if (auto rootContent = xamlRoot.Content()) { + m_subscribedDebuggerRoots.push_back(xamlRoot); + rootContent.KeyDown( + [weakThis = this->get_weak()](const auto & /*sender*/, const xaml::Input::KeyRoutedEventArgs &args) { + if (const auto strongThis = weakThis.get()) { + if (IsCtrlShiftI(args.Key())) { + ::Microsoft::ReactNative::GetSharedDevManager()->OpenDevTools( + winrt::to_string(strongThis->m_reactNativeHost.InstanceSettings().BundleAppId())); + } + }; + }); + } + } + } +} + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/ReactRootView.h b/vnext/Microsoft.ReactNative/ReactRootView.h index f35a3c3fd60..876ac7e38cb 100644 --- a/vnext/Microsoft.ReactNative/ReactRootView.h +++ b/vnext/Microsoft.ReactNative/ReactRootView.h @@ -72,6 +72,7 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: bool m_isPerspectiveEnabled{true}; bool m_isInitialized{false}; bool m_isJSViewAttached{false}; + bool m_isDebuggerPausedOverlayOpen{false}; Mso::DispatchQueue m_uiQueue; int64_t m_rootTag{-1}; std::unique_ptr m_reactOptions; @@ -84,9 +85,11 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: std::shared_ptr<::Microsoft::ReactNative::PreviewKeyboardEventHandlerOnRoot> m_previewKeyboardEventHandlerOnRoot; xaml::Controls::ContentControl m_focusSafeHarbor{nullptr}; xaml::Controls::ContentControl::LosingFocus_revoker m_focusSafeHarborLosingFocusRevoker{}; + xaml::Controls::Flyout m_debuggerPausedFlyout{nullptr}; winrt::Grid m_greenBoxGrid{nullptr}; winrt::TextBlock m_waitingTextBlock{nullptr}; winrt::SystemNavigationManager::BackRequested_revoker m_backRequestedRevoker{}; + std::vector m_subscribedDebuggerRoots{}; // Visual tree to support safe harbor // this @@ -102,6 +105,8 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: void UpdateRootViewInternal() noexcept; void ClearLoadingUI() noexcept; void EnsureLoadingUI() noexcept; + void HideDebuggerPausedOverlay() noexcept; + void ShowDebuggerPausedOverlay(const std::string &message, const std::function &onResume) noexcept; void ShowInstanceLoaded() noexcept; void ShowInstanceError() noexcept; void ShowInstanceWaiting() noexcept; @@ -112,6 +117,7 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: bool OnBackRequested() noexcept; Mso::React::IReactViewHost *ReactViewHost() noexcept; void ReactViewHost(Mso::React::IReactViewHost *viewHost) noexcept; + void SetupDevToolsShortcut() noexcept; }; } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Views/DevMenu.cpp b/vnext/Microsoft.ReactNative/Views/DevMenu.cpp index 2d59627e885..38d1f17fcbd 100644 --- a/vnext/Microsoft.ReactNative/Views/DevMenu.cpp +++ b/vnext/Microsoft.ReactNative/Views/DevMenu.cpp @@ -6,7 +6,7 @@ #include "DevMenu.h" #include -#include "HermesSamplingProfiler.h" +#include "Hermes/HermesSamplingProfiler.h" #include "IReactDispatcher.h" #include "Modules/DevSettingsModule.h" diff --git a/vnext/Microsoft.ReactNative/packages.chakra.lock.json b/vnext/Microsoft.ReactNative/packages.chakra.lock.json index bd23518b301..0602b9ec3c3 100644 --- a/vnext/Microsoft.ReactNative/packages.chakra.lock.json +++ b/vnext/Microsoft.ReactNative/packages.chakra.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/Microsoft.ReactNative/packages.experimentalwinui3.lock.json b/vnext/Microsoft.ReactNative/packages.experimentalwinui3.lock.json index bd23518b301..0602b9ec3c3 100644 --- a/vnext/Microsoft.ReactNative/packages.experimentalwinui3.lock.json +++ b/vnext/Microsoft.ReactNative/packages.experimentalwinui3.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/Microsoft.ReactNative/packages.lock.json b/vnext/Microsoft.ReactNative/packages.lock.json index bd23518b301..0602b9ec3c3 100644 --- a/vnext/Microsoft.ReactNative/packages.lock.json +++ b/vnext/Microsoft.ReactNative/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/Microsoft.ReactNative/packages.newarch.experimentalwinui3.lock.json b/vnext/Microsoft.ReactNative/packages.newarch.experimentalwinui3.lock.json index e6e852aff5c..e8c5fb90325 100644 --- a/vnext/Microsoft.ReactNative/packages.newarch.experimentalwinui3.lock.json +++ b/vnext/Microsoft.ReactNative/packages.newarch.experimentalwinui3.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/Microsoft.ReactNative/packages.newarch.lock.json b/vnext/Microsoft.ReactNative/packages.newarch.lock.json index 24cffe8d1be..6e383fda2c0 100644 --- a/vnext/Microsoft.ReactNative/packages.newarch.lock.json +++ b/vnext/Microsoft.ReactNative/packages.newarch.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Direct", - "requested": "[0.0.0-2505.2001-0e4bc3b9, )", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "requested": "[0.0.0-2511.7001-d7ca19b3, )", + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/vnext/PropertySheets/JSEngine.props b/vnext/PropertySheets/JSEngine.props index a784057f813..a7fe1f085fa 100644 --- a/vnext/PropertySheets/JSEngine.props +++ b/vnext/PropertySheets/JSEngine.props @@ -6,7 +6,7 @@ true - 0.0.0-2505.2001-0e4bc3b9 + 0.0.0-2511.7001-d7ca19b3 $(PkgMicrosoft_JavaScript_Hermes) $(NuGetPackageRoot)\Microsoft.JavaScript.Hermes\$(HermesVersion) false diff --git a/vnext/PropertySheets/React.Cpp.props b/vnext/PropertySheets/React.Cpp.props index 1e17a731773..72808b4d59f 100644 --- a/vnext/PropertySheets/React.Cpp.props +++ b/vnext/PropertySheets/React.Cpp.props @@ -61,7 +61,7 @@ - USE_HERMES;%(PreprocessorDefinitions) + USE_HERMES;REACT_NATIVE_DEBUGGER_ENABLED;%(PreprocessorDefinitions) ENABLE_DEVSERVER_HBCBUNDLES;%(PreprocessorDefinitions) USE_V8;%(PreprocessorDefinitions) USE_FABRIC;%(PreprocessorDefinitions) @@ -136,7 +136,7 @@ $(CppStandard) ProgramDatabase - false + true true /utf-8 %(AdditionalOptions) /await Guard diff --git a/vnext/ReactCommon.UnitTests/packages.experimentalwinui3.lock.json b/vnext/ReactCommon.UnitTests/packages.experimentalwinui3.lock.json index 83d625ce1fd..b2ac6286fe2 100644 --- a/vnext/ReactCommon.UnitTests/packages.experimentalwinui3.lock.json +++ b/vnext/ReactCommon.UnitTests/packages.experimentalwinui3.lock.json @@ -27,8 +27,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -91,7 +91,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250127003-experimental3, )", diff --git a/vnext/ReactCommon.UnitTests/packages.lock.json b/vnext/ReactCommon.UnitTests/packages.lock.json index e2b3300fe1e..00a55c89947 100644 --- a/vnext/ReactCommon.UnitTests/packages.lock.json +++ b/vnext/ReactCommon.UnitTests/packages.lock.json @@ -27,8 +27,8 @@ }, "Microsoft.JavaScript.Hermes": { "type": "Transitive", - "resolved": "0.0.0-2505.2001-0e4bc3b9", - "contentHash": "VNSUBgaGzJ/KkK3Br0b9FORkCgKqke54hi48vG42xRACIlxN+uLFMz0hRo+KHogz+Fsn+ltXicGwQsDVpmaCMg==" + "resolved": "0.0.0-2511.7001-d7ca19b3", + "contentHash": "/EGy/gbTWpFZPZ4Z81QxbGQxpZhqiOE3qrnSokZRgXAyHivl15s7zZkRLOy9daDmVyEfanq7YBCOMi0ha58uQA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -91,7 +91,7 @@ "Common": "[1.0.0, )", "Folly": "[1.0.0, )", "FollyWin32": "[1.0.0, )", - "Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2511.7001-d7ca19b3, )", "Microsoft.SourceLink.GitHub": "[1.1.1, )", "Microsoft.Web.WebView2": "[1.0.2903.40, )", "Microsoft.WindowsAppSDK": "[1.7.250401001, )", diff --git a/vnext/ReactCommon/ReactCommon.vcxproj b/vnext/ReactCommon/ReactCommon.vcxproj index 6164251f380..cae2350e85f 100644 --- a/vnext/ReactCommon/ReactCommon.vcxproj +++ b/vnext/ReactCommon/ReactCommon.vcxproj @@ -114,6 +114,13 @@ + + + + + + + @@ -144,10 +151,18 @@ + + + + + + + + - + @@ -194,6 +209,7 @@ + @@ -289,4 +305,4 @@ - \ No newline at end of file + diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.cpp deleted file mode 100644 index 31ec1f84144..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.cpp +++ /dev/null @@ -1,75 +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 "JSExecutor.h" - -#include "RAMBundleRegistry.h" - -#include -#include -#include -#include - -#include - -namespace facebook::react { - -std::string JSExecutor::getSyntheticBundlePath( - uint32_t bundleId, - const std::string& bundlePath) { - if (bundleId == RAMBundleRegistry::MAIN_BUNDLE_ID) { - return bundlePath; - } - return folly::to("seg-", bundleId, ".js"); -} - -double JSExecutor::performanceNow() { - return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now()); -} - -void JSExecutor::addConsoleMessage(jsi::Runtime& runtime, jsinspector_modern::ConsoleMessage message){ - return; -} - -bool JSExecutor::supportsConsole() const { - return false; -} - -std::unique_ptr JSExecutor::captureStackTrace( - facebook::jsi::Runtime &runtime, - size_t framesToSkip) { - return std::make_unique(); -} - -void JSExecutor::enableSamplingProfiler() { - return; // [Windows TODO: stubbed implementation #14700] -} - -void JSExecutor::disableSamplingProfiler() { - return; // [Windows TODO: stubbed implementation #14700] -} - -facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile JSExecutor::collectSamplingProfile() { - return facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile( - "stubbed_impl", {}); // [Windows TODO: stubbed implementation #14700] -} - -std::unique_ptr -JSExecutor::createAgentDelegate( - jsinspector_modern::FrontendChannel frontendChannel, - jsinspector_modern::SessionState& sessionState, - std::unique_ptr, - const jsinspector_modern::ExecutionContextDescription& - executionContextDescription, - RuntimeExecutor runtimeExecutor) { - (void)executionContextDescription; - (void)runtimeExecutor; - return std::make_unique( - std::move(frontendChannel), sessionState, getDescription()); -} - -} // namespace facebook::react \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.h deleted file mode 100644 index f044fca7cbf..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.h +++ /dev/null @@ -1,196 +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 -#include -#include - -#ifndef RN_EXPORT -#define RN_EXPORT __attribute__((visibility("default"))) -#endif - -namespace facebook::react { - -class JSBigString; -class JSExecutor; -class JSModulesUnbundle; -class MessageQueueThread; -class ModuleRegistry; -class RAMBundleRegistry; - -// This interface describes the delegate interface required by -// Executor implementations to call from JS into native code. -class ExecutorDelegate { - public: - virtual ~ExecutorDelegate() {} - - virtual std::shared_ptr getModuleRegistry() = 0; - - virtual void callNativeModules( - JSExecutor& executor, - folly::dynamic&& calls, - bool isEndOfBatch) = 0; - virtual MethodCallResult callSerializableNativeHook( - JSExecutor& executor, - unsigned int moduleId, - unsigned int methodId, - folly::dynamic&& args) = 0; -}; - -class JSExecutorFactory { - public: - virtual std::unique_ptr createJSExecutor( - std::shared_ptr delegate, - std::shared_ptr jsQueue) = 0; - virtual ~JSExecutorFactory() {} -}; - -class RN_EXPORT JSExecutor : public jsinspector_modern::RuntimeTargetDelegate { - public: - /** - * Prepares the JS runtime for React Native by installing global variables. - * Called once before any JS is evaluated. - */ - virtual void initializeRuntime() = 0; - /** - * Execute an application script bundle in the JS context. - */ - virtual void loadBundle( - std::unique_ptr script, - std::string sourceURL) = 0; - - /** - * Add an application "RAM" bundle registry - */ - virtual void setBundleRegistry( - std::unique_ptr bundleRegistry) = 0; - - /** - * Register a file path for an additional "RAM" bundle - */ - virtual void registerBundle( - uint32_t bundleId, - const std::string& bundlePath) = 0; - - /** - * Executes BatchedBridge.callFunctionReturnFlushedQueue with the module ID, - * method ID and optional additional arguments in JS. The executor is - * responsible for using Bridge->callNativeModules to invoke any necessary - * native modules methods. - */ - virtual void callFunction( - const std::string& moduleId, - const std::string& methodId, - const folly::dynamic& arguments) = 0; - - /** - * Executes BatchedBridge.invokeCallbackAndReturnFlushedQueue with the cbID, - * and optional additional arguments in JS and returns the next queue. The - * executor is responsible for using Bridge->callNativeModules to invoke any - * necessary native modules methods. - */ - virtual void invokeCallback( - const double callbackId, - const folly::dynamic& arguments) = 0; - - virtual void setGlobalVariable( - std::string propName, - std::unique_ptr jsonValue) = 0; - - virtual void* getJavaScriptContext() { - return nullptr; - } - - /** - * Returns whether or not the underlying executor supports debugging via the - * Chrome remote debugging protocol. If true, the executor should also - * override the \c createAgentDelegate method. - */ - virtual bool isInspectable() { - return false; - } - - /** - * The description is displayed in the dev menu, if there is one in - * this build. There is a default, but if this method returns a - * non-empty string, it will be used instead. - */ - virtual std::string getDescription() = 0; - - virtual void handleMemoryPressure([[maybe_unused]] int pressureLevel) {} - - virtual void destroy() {} - virtual ~JSExecutor() override {} - - virtual void flush() {} - - static std::string getSyntheticBundlePath( - uint32_t bundleId, - const std::string& bundlePath); - - static double performanceNow(); - - virtual void addConsoleMessage(jsi::Runtime& runtime, jsinspector_modern::ConsoleMessage message); - - virtual bool supportsConsole() const; - - /** - * \returns an opaque representation of a stack trace. This may be passed back - * to the `RuntimeTargetDelegate` as part of `addConsoleMessage` or other APIs - * that report stack traces. - * \param framesToSkip The number of call frames to skip. The first call frame - * is the topmost (current) frame on the Runtime's call stack, which will - * typically be the (native) JSI HostFunction that called this method. - * \note The method is called on the JS thread, and receives a valid reference - * to the current \c jsi::Runtime. The callee MAY use its own intrinsic - * Runtime reference, if it has one, without checking it for equivalence with - * the one provided here. - */ - std::unique_ptr captureStackTrace( - jsi::Runtime& runtime, - size_t framesToSkip = 0) override; - - - /** - * Start sampling profiler. - */ - virtual void enableSamplingProfiler() override; - - /** - * Stop sampling profiler. - */ - virtual void disableSamplingProfiler() override; - - /** - * Return recorded sampling profile for the previous sampling session. - */ - virtual facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile collectSamplingProfile() override; - - /** - * Create a RuntimeAgentDelegate that can be used to debug the JS VM instance. - */ - virtual std::unique_ptr - createAgentDelegate( - jsinspector_modern::FrontendChannel frontendChannel, - jsinspector_modern::SessionState& sessionState, - std::unique_ptr - previouslyExportedState, - const jsinspector_modern::ExecutionContextDescription& - executionContextDescription, - RuntimeExecutor runtimeExecutor) override; -}; - -} // namespace facebook::react \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/NativeToJsBridge.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/NativeToJsBridge.cpp deleted file mode 100644 index bd8d8eef53f..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/NativeToJsBridge.cpp +++ /dev/null @@ -1,351 +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 "NativeToJsBridge.h" - -#include -#include -#include -#include -#include - -#include "ErrorUtils.h" -#include "Instance.h" -#include "JSBigString.h" -#include "MessageQueueThread.h" -#include "MethodCall.h" -#include "ModuleRegistry.h" -#include "MoveWrapper.h" -#include "RAMBundleRegistry.h" -#include "TraceSection.h" - -#include - -#ifdef WITH_FBSYSTRACE -#include - -using fbsystrace::FbSystraceAsyncFlow; -#endif - -namespace facebook::react { - -// This class manages calls from JS to native code. -class JsToNativeBridge : public react::ExecutorDelegate { - public: - JsToNativeBridge( - std::shared_ptr registry, - std::shared_ptr callback) - : m_registry(registry), m_callback(callback) {} - - std::shared_ptr getModuleRegistry() override { - return m_registry; - } - - bool isBatchActive() { - return m_batchHadNativeModuleOrTurboModuleCalls; - } - - void callNativeModules( - [[maybe_unused]] JSExecutor& executor, - folly::dynamic&& calls, - bool isEndOfBatch) override { - CHECK(m_registry || calls.empty()) - << "native module calls cannot be completed with no native modules"; - m_batchHadNativeModuleOrTurboModuleCalls = - m_batchHadNativeModuleOrTurboModuleCalls || !calls.empty(); - - std::vector methodCalls = parseMethodCalls(std::move(calls)); - BridgeNativeModulePerfLogger::asyncMethodCallBatchPreprocessEnd( - (int)methodCalls.size()); - - // An exception anywhere in here stops processing of the batch. This - // was the behavior of the Android bridge, and since exception handling - // terminates the whole bridge, there's not much point in continuing. - for (auto& call : methodCalls) { - m_registry->callNativeMethod( - call.moduleId, call.methodId, std::move(call.arguments), call.callId); - } - if (isEndOfBatch) { - // onBatchComplete will be called on the native (module) queue, but - // decrementPendingJSCalls will be called sync. Be aware that the bridge - // may still be processing native calls when the bridge idle signaler - // fires. - if (m_batchHadNativeModuleOrTurboModuleCalls) { - m_callback->onBatchComplete(); - m_batchHadNativeModuleOrTurboModuleCalls = false; - } - m_callback->decrementPendingJSCalls(); - } - } - - MethodCallResult callSerializableNativeHook( - [[maybe_unused]] JSExecutor& executor, - unsigned int moduleId, - unsigned int methodId, - folly::dynamic&& args) override { - return m_registry->callSerializableNativeHook( - moduleId, methodId, std::move(args)); - } - - void recordTurboModuleAsyncMethodCall() noexcept { - m_batchHadNativeModuleOrTurboModuleCalls = true; - } - - private: - // These methods are always invoked from an Executor. The NativeToJsBridge - // keeps a reference to the executor, and when destroy() is called, the - // executor is destroyed synchronously on its queue. - std::shared_ptr m_registry; - std::shared_ptr m_callback; - std::atomic m_batchHadNativeModuleOrTurboModuleCalls{false}; -}; - -NativeToJsBridge::NativeToJsBridge( - JSExecutorFactory* jsExecutorFactory, - std::shared_ptr registry, - std::shared_ptr jsQueue, - std::shared_ptr callback) - : m_destroyed(std::make_shared(false)), - m_delegate(std::make_shared(registry, callback)), - m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)), - m_executorMessageQueueThread(std::move(jsQueue)), - m_inspectable(m_executor->isInspectable()) {} - -// This must be called on the same thread on which the constructor was called. -NativeToJsBridge::~NativeToJsBridge() { - CHECK(*m_destroyed) - << "NativeToJsBridge::destroy() must be called before deallocating the NativeToJsBridge!"; -} - -void NativeToJsBridge::initializeRuntime() { - runOnExecutorQueue( - [](JSExecutor* executor) mutable { executor->initializeRuntime(); }); -} - -void NativeToJsBridge::loadBundle( - std::unique_ptr bundleRegistry, - std::unique_ptr startupScript, - std::string startupScriptSourceURL) { - runOnExecutorQueue( - [this, - bundleRegistryWrap = makeMoveWrapper(std::move(bundleRegistry)), - startupScript = makeMoveWrapper(std::move(startupScript)), - startupScriptSourceURL = - std::move(startupScriptSourceURL)](JSExecutor* executor) mutable { - auto bundleRegistry = bundleRegistryWrap.move(); - if (bundleRegistry) { - executor->setBundleRegistry(std::move(bundleRegistry)); - } - try { - executor->loadBundle( - std::move(*startupScript), std::move(startupScriptSourceURL)); - } catch (...) { - m_applicationScriptHasFailure = true; - throw; - } - }); -} - -void NativeToJsBridge::loadBundleSync( - std::unique_ptr bundleRegistry, - std::unique_ptr startupScript, - std::string startupScriptSourceURL) { - if (bundleRegistry) { - m_executor->setBundleRegistry(std::move(bundleRegistry)); - } - try { - m_executor->loadBundle( - std::move(startupScript), std::move(startupScriptSourceURL)); - } catch (...) { - m_applicationScriptHasFailure = true; - throw; - } -} - -void NativeToJsBridge::callFunction( - std::string&& module, - std::string&& method, - folly::dynamic&& arguments) { - int systraceCookie = -1; -#ifdef WITH_FBSYSTRACE - systraceCookie = m_systraceCookie++; - FbSystraceAsyncFlow::begin( - TRACE_TAG_REACT_CXX_BRIDGE, "JSCall", systraceCookie); -#endif - - runOnExecutorQueue([this, - module = std::move(module), - method = std::move(method), - arguments = std::move(arguments), - systraceCookie](JSExecutor* executor) { - if (m_applicationScriptHasFailure) { - LOG(ERROR) - << "Attempting to call JS function on a bad application bundle: " - << module.c_str() << "." << method.c_str() << "()"; - throw std::runtime_error( - "Attempting to call JS function on a bad application bundle: " + - module + "." + method + "()"); - } - -#ifdef WITH_FBSYSTRACE - FbSystraceAsyncFlow::end( - TRACE_TAG_REACT_CXX_BRIDGE, "JSCall", systraceCookie); - TraceSection s( - "NativeToJsBridge::callFunction", "module", module, "method", method); -#else - (void)(systraceCookie); -#endif - // This is safe because we are running on the executor's thread: it won't - // destruct until after it's been unregistered (which we check above) and - // that will happen on this thread - executor->callFunction(module, method, arguments); - }); -} - -void NativeToJsBridge::invokeCallback( - double callbackId, - folly::dynamic&& arguments) { - int systraceCookie = -1; -#ifdef WITH_FBSYSTRACE - systraceCookie = m_systraceCookie++; - FbSystraceAsyncFlow::begin( - TRACE_TAG_REACT_CXX_BRIDGE, "", systraceCookie); -#endif - - runOnExecutorQueue( - [this, callbackId, arguments = std::move(arguments), systraceCookie]( - JSExecutor* executor) { - if (m_applicationScriptHasFailure) { - LOG(ERROR) - << "Attempting to call JS callback on a bad application bundle: " - << callbackId; - throw std::runtime_error( - "Attempting to invoke JS callback on a bad application bundle."); - } -#ifdef WITH_FBSYSTRACE - FbSystraceAsyncFlow::end( - TRACE_TAG_REACT_CXX_BRIDGE, "", systraceCookie); - TraceSection s("NativeToJsBridge::invokeCallback"); -#else - (void)(systraceCookie); -#endif - executor->invokeCallback(callbackId, arguments); - }); -} - -void NativeToJsBridge::registerBundle( - uint32_t bundleId, - const std::string& bundlePath) { - runOnExecutorQueue([bundleId, bundlePath](JSExecutor* executor) { - executor->registerBundle(bundleId, bundlePath); - }); -} - -void NativeToJsBridge::setGlobalVariable( - std::string propName, - std::unique_ptr jsonValue) { - runOnExecutorQueue([propName = std::move(propName), - jsonValue = makeMoveWrapper(std::move(jsonValue))]( - JSExecutor* executor) mutable { - executor->setGlobalVariable(propName, jsonValue.move()); - }); -} - -void* NativeToJsBridge::getJavaScriptContext() { - // TODO(cjhopman): this seems unsafe unless we require that it is only called - // on the main js queue. - return m_executor->getJavaScriptContext(); -} - -bool NativeToJsBridge::isInspectable() { - return m_inspectable; -} - -bool NativeToJsBridge::isBatchActive() { - return m_delegate->isBatchActive(); -} - -void NativeToJsBridge::handleMemoryPressure(int pressureLevel) { - runOnExecutorQueue([=](JSExecutor* executor) { - executor->handleMemoryPressure(pressureLevel); - }); -} - -void NativeToJsBridge::destroy() { - // All calls made through runOnExecutorQueue have an early exit if - // m_destroyed is true. Setting this before the runOnQueueSync will cause - // pending work to be cancelled and we won't have to wait for it. - *m_destroyed = true; - m_executorMessageQueueThread->runOnQueueSync([this] { - m_executor->destroy(); - m_executorMessageQueueThread->quitSynchronous(); - m_executor = nullptr; - }); -} - -void NativeToJsBridge::runOnExecutorQueue( - std::function&& task) noexcept { - if (*m_destroyed) { - return; - } - - std::shared_ptr isDestroyed = m_destroyed; - m_executorMessageQueueThread->runOnQueue( - [this, isDestroyed, task = std::move(task)] { - if (*isDestroyed) { - return; - } - - // The executor is guaranteed to be valid for the duration of the task - // because: - // 1. the executor is only destroyed after it is unregistered - // 2. the executor is unregistered on this queue - // 3. we just confirmed that the executor hasn't been unregistered above - task(m_executor.get()); - }); -} - -std::shared_ptr -NativeToJsBridge::getDecoratedNativeMethodCallInvoker( - std::shared_ptr nativeMethodCallInvoker) const { - class NativeMethodCallInvokerImpl : public NativeMethodCallInvoker { - private: - std::weak_ptr m_jsToNativeBridge; - std::shared_ptr m_nativeInvoker; - - public: - NativeMethodCallInvokerImpl( - std::weak_ptr jsToNativeBridge, - std::shared_ptr nativeInvoker) - : m_jsToNativeBridge(std::move(jsToNativeBridge)), - m_nativeInvoker(std::move(nativeInvoker)) {} - - void invokeAsync( - const std::string& methodName, - NativeMethodCallFunc&& func) noexcept override { - if (auto strongJsToNativeBridge = m_jsToNativeBridge.lock()) { - strongJsToNativeBridge->recordTurboModuleAsyncMethodCall(); - } - m_nativeInvoker->invokeAsync(methodName, std::move(func)); - } - - void invokeSync(const std::string& methodName, NativeMethodCallFunc&& func) - override { - m_nativeInvoker->invokeSync(methodName, std::move(func)); - } - }; - - return std::make_shared( - m_delegate, std::move(nativeMethodCallInvoker)); -} - -jsinspector_modern::RuntimeTargetDelegate& -NativeToJsBridge::getInspectorTargetDelegate() { - return *m_executor; -} - -} // namespace facebook::react \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jserrorhandler/JsErrorHandler.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jserrorhandler/JsErrorHandler.cpp deleted file mode 100644 index fc5ff6cfabc..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jserrorhandler/JsErrorHandler.cpp +++ /dev/null @@ -1,429 +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 "JsErrorHandler.h" -#include -#include -#include -#include -#include -#include "StackTraceParser.h" - -using namespace facebook; - -namespace { -std::string quote(const std::string& view) { - return "\"" + view + "\""; -} - -int nextExceptionId() { - static int exceptionId = 0; - return exceptionId++; -} - -bool isLooselyNull(const jsi::Value& value) { - return value.isNull() || value.isUndefined(); -} - -bool isEqualTo( - jsi::Runtime& runtime, - const jsi::Value& value, - const std::string& str) { - return jsi::Value::strictEquals( - runtime, value, jsi::String::createFromUtf8(runtime, str)); -} - -std::string stringifyToCpp(jsi::Runtime& runtime, const jsi::Value& value) { - return value.toString(runtime).utf8(runtime); -} - -bool isTruthy(jsi::Runtime& runtime, const jsi::Value& value) { - auto Boolean = runtime.global().getPropertyAsFunction(runtime, "Boolean"); - return Boolean.call(runtime, value).getBool(); -} - -void objectAssign( - jsi::Runtime& runtime, - jsi::Object& target, - const jsi::Object& value) { - auto Object = runtime.global().getPropertyAsObject(runtime, "Object"); - auto assign = Object.getPropertyAsFunction(runtime, "assign"); - assign.callWithThis(runtime, Object, target, value); -} - -jsi::Object wrapInErrorIfNecessary( - jsi::Runtime& runtime, - const jsi::Value& value) { - auto Error = runtime.global().getPropertyAsFunction(runtime, "Error"); - auto isError = - value.isObject() && value.asObject(runtime).instanceOf(runtime, Error); - auto error = isError - ? value.getObject(runtime) - : Error.callAsConstructor(runtime, value).getObject(runtime); - return error; -} - -class SetFalseOnDestruct { - std::shared_ptr _value; - - public: - SetFalseOnDestruct(const SetFalseOnDestruct&) = delete; - SetFalseOnDestruct& operator=(const SetFalseOnDestruct&) = delete; - SetFalseOnDestruct(SetFalseOnDestruct&&) = delete; - SetFalseOnDestruct& operator=(SetFalseOnDestruct&&) = delete; - explicit SetFalseOnDestruct(std::shared_ptr value) - : _value(std::move(value)) {} - ~SetFalseOnDestruct() { - *_value = false; - } -}; - -void logErrorWhileReporting( - std::string message, - jsi::JSError& error, - jsi::JSError& originalError) { - LOG(ERROR) << "JsErrorHandler::" << message << std::endl - << "Js error message: " << error.getMessage() << std::endl - << "Original js error message: " << originalError.getMessage() - << std::endl; -} - -jsi::Value getBundleMetadata(jsi::Runtime& runtime, jsi::JSError& error) { - auto jsGetBundleMetadataValue = - runtime.global().getProperty(runtime, "__getBundleMetadata"); - - if (!jsGetBundleMetadataValue.isObject() || - !jsGetBundleMetadataValue.asObject(runtime).isFunction(runtime)) { - return jsi::Value::null(); - } - - auto jsGetBundleMetadataValueFn = - jsGetBundleMetadataValue.asObject(runtime).asFunction(runtime); - - try { - auto bundleMetadataValue = jsGetBundleMetadataValueFn.call(runtime); - if (bundleMetadataValue.isObject()) { - return bundleMetadataValue; - } - return bundleMetadataValue; - } catch (jsi::JSError& ex) { - logErrorWhileReporting( - "getBundleMetadata(): Error raised while calling __getBundleMetadata(). Returning null.", - ex, - error); - } - - return jsi::Value::null(); -} -} // namespace - -namespace facebook::react { - -template <> -struct Bridging { - static jsi::Value toJs( - jsi::Runtime& runtime, - const JsErrorHandler::ProcessedError::StackFrame& frame) { - auto stackFrame = jsi::Object(runtime); - auto file = bridging::toJs(runtime, frame.file, nullptr); - auto lineNumber = bridging::toJs(runtime, frame.lineNumber, nullptr); - auto column = bridging::toJs(runtime, frame.column, nullptr); - - stackFrame.setProperty(runtime, "file", file); - stackFrame.setProperty(runtime, "methodName", frame.methodName); - stackFrame.setProperty(runtime, "lineNumber", lineNumber); - stackFrame.setProperty(runtime, "column", column); - return stackFrame; - } -}; - -template <> -struct Bridging { - static jsi::Value toJs( - jsi::Runtime& runtime, - const JsErrorHandler::ProcessedError& error) { - auto data = jsi::Object(runtime); - data.setProperty(runtime, "message", error.message); - data.setProperty( - runtime, - "originalMessage", - bridging::toJs(runtime, error.originalMessage, nullptr)); - data.setProperty( - runtime, "name", bridging::toJs(runtime, error.name, nullptr)); - data.setProperty( - runtime, - "componentStack", - bridging::toJs(runtime, error.componentStack, nullptr)); - - auto stack = jsi::Array(runtime, error.stack.size()); - for (size_t i = 0; i < error.stack.size(); i++) { - auto& frame = error.stack[i]; - stack.setValueAtIndex(runtime, i, bridging::toJs(runtime, frame)); - } - - data.setProperty(runtime, "stack", stack); - data.setProperty(runtime, "id", error.id); - data.setProperty(runtime, "isFatal", error.isFatal); - data.setProperty(runtime, "extraData", error.extraData); - return data; - } -}; - -std::ostream& operator<<( - std::ostream& os, - const JsErrorHandler::ProcessedError::StackFrame& frame) { - auto file = frame.file ? quote(*frame.file) : "nil"; - auto methodName = quote(frame.methodName); - auto lineNumber = - frame.lineNumber ? std::to_string(*frame.lineNumber) : "nil"; - auto column = frame.column ? std::to_string(*frame.column) : "nil"; - - os << "StackFrame { .file = " << file << ", .methodName = " << methodName - << ", .lineNumber = " << lineNumber << ", .column = " << column << " }"; - return os; -} -std::ostream& operator<<( - std::ostream& os, - const JsErrorHandler::ProcessedError& error) { - auto message = quote(error.message); - auto originalMessage = - error.originalMessage ? quote(*error.originalMessage) : "nil"; - auto name = error.name ? quote(*error.name) : "nil"; - auto componentStack = - error.componentStack ? quote(*error.componentStack) : "nil"; - auto id = std::to_string(error.id); - auto isFatal = std::to_string(static_cast(error.isFatal)); - auto extraData = "jsi::Object{ } "; - - os << "ProcessedError {\n" - << " .message = " << message << "\n" - << " .originalMessage = " << originalMessage << "\n" - << " .name = " << name << "\n" - << " .componentStack = " << componentStack << "\n" - << " .stack = [\n"; - - for (const auto& frame : error.stack) { - os << " " << frame << ", \n"; - } - os << " ]\n" - << " .id = " << id << "\n" - << " .isFatal " << isFatal << "\n" - << " .extraData = " << extraData << "\n" - << "}"; - return os; -} - -JsErrorHandler::JsErrorHandler(JsErrorHandler::OnJsError onJsError) - : _onJsError(std::move(onJsError)), - _inErrorHandler(std::make_shared(false)){ - - }; - -JsErrorHandler::~JsErrorHandler() {} - -void JsErrorHandler::handleError( - jsi::Runtime& runtime, - jsi::JSError& error, - bool isFatal, - bool logToConsole) { - // TODO: Current error parsing works and is stable. Can investigate using - // REGEX_HERMES to get additional Hermes data, though it requires JS setup - - if (!ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling() && - _isRuntimeReady) { - try { - handleJSError(runtime, error, isFatal); - return; - } catch (jsi::JSError& ex) { - logErrorWhileReporting( - "handleError(): Error raised while reporting using js pipeline. Using c++ pipeline instead.", - ex, - error); - - // Re-try reporting using the c++ pipeline - _hasHandledFatalError = false; - } - } - - handleErrorWithCppPipeline(runtime, error, isFatal, logToConsole); -} - -void JsErrorHandler::handleErrorWithCppPipeline( - jsi::Runtime& runtime, - jsi::JSError& error, - bool isFatal, - bool logToConsole) { - *_inErrorHandler = true; - SetFalseOnDestruct temp{_inErrorHandler}; - - auto message = error.getMessage(); - auto errorObj = wrapInErrorIfNecessary(runtime, error.value()); - auto componentStackValue = errorObj.getProperty(runtime, "componentStack"); - if (!isLooselyNull(componentStackValue)) { - message += "\n" + stringifyToCpp(runtime, componentStackValue); - } - - auto nameValue = errorObj.getProperty(runtime, "name"); - auto name = (isLooselyNull(nameValue) || isEqualTo(runtime, nameValue, "")) - ? std::nullopt - : std::optional(stringifyToCpp(runtime, nameValue)); - - if (name && !message.starts_with(*name + ": ")) { - message = *name + ": " + message; - } - - auto jsEngineValue = errorObj.getProperty(runtime, "jsEngine"); - - if (!isLooselyNull(jsEngineValue)) { - message += ", js engine: " + stringifyToCpp(runtime, jsEngineValue); - } - - auto extraDataKey = jsi::PropNameID::forUtf8(runtime, "RN$ErrorExtraDataKey"); - auto extraDataValue = errorObj.getProperty(runtime, extraDataKey); - - auto extraData = jsi::Object(runtime); - if (extraDataValue.isObject()) { - objectAssign(runtime, extraData, extraDataValue.asObject(runtime)); - } - - auto isDEV = - isTruthy(runtime, runtime.global().getProperty(runtime, "__DEV__")); - - extraData.setProperty(runtime, "jsEngine", jsEngineValue); - extraData.setProperty(runtime, "rawStack", error.getStack()); - extraData.setProperty(runtime, "__DEV__", isDEV); - extraData.setProperty( - runtime, "bundleMetadata", getBundleMetadata(runtime, error)); - - auto cause = errorObj.getProperty(runtime, "cause"); - if (cause.isObject()) { - auto causeObj = cause.asObject(runtime); - // TODO: Consider just forwarding all properties. For now, just forward the - // stack properties to maintain symmetry with js pipeline - auto stackSymbols = causeObj.getProperty(runtime, "stackSymbols"); - extraData.setProperty(runtime, "stackSymbols", stackSymbols); - - auto stackReturnAddresses = - causeObj.getProperty(runtime, "stackReturnAddresses"); - extraData.setProperty( - runtime, "stackReturnAddresses", stackReturnAddresses); - - auto stackElements = causeObj.getProperty(runtime, "stackElements"); - extraData.setProperty(runtime, "stackElements", stackElements); - } - - auto originalMessage = message == error.getMessage() - ? std::nullopt - : std::optional(error.getMessage()); - - auto componentStack = !componentStackValue.isString() - ? std::nullopt - : std::optional(componentStackValue.asString(runtime).utf8(runtime)); - - auto isHermes = runtime.global().hasProperty(runtime, "HermesInternal"); - auto stackFrames = StackTraceParser::parse(isHermes, error.getStack()); - - auto id = nextExceptionId(); - - ProcessedError processedError = { - .message = - _isRuntimeReady ? message : ("[runtime not ready]: " + message), - .originalMessage = originalMessage, - .name = name, - .componentStack = componentStack, - .stack = stackFrames, - .id = id, - .isFatal = isFatal, - .extraData = std::move(extraData), - }; - - auto data = bridging::toJs(runtime, processedError).asObject(runtime); - - auto isComponentError = - isTruthy(runtime, errorObj.getProperty(runtime, "isComponentError")); - data.setProperty(runtime, "isComponentError", isComponentError); - - if (logToConsole && runtime.global().hasProperty(runtime, "console")) { // [Windows] Added hasProperty check - auto console = runtime.global().getPropertyAsObject(runtime, "console"); - auto errorFn = console.getPropertyAsFunction(runtime, "error"); - auto finalMessage = - jsi::String::createFromUtf8(runtime, processedError.message); - errorFn.callWithThis(runtime, console, finalMessage); - } - - std::shared_ptr shouldPreventDefault = std::make_shared(false); - auto preventDefault = jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "preventDefault"), - 0, - [shouldPreventDefault]( - jsi::Runtime& /*rt*/, - const jsi::Value& /*thisVal*/, - const jsi::Value* /*args*/, - size_t /*count*/) { - *shouldPreventDefault = true; - return jsi::Value::undefined(); - }); - - data.setProperty(runtime, "preventDefault", preventDefault); - - for (auto& errorListener : _errorListeners) { - try { - errorListener(runtime, jsi::Value(runtime, data)); - } catch (jsi::JSError& ex) { - logErrorWhileReporting( - "handleErrorWithCppPipeline(): Error raised inside an error listener. Executing next listener.", - ex, - error); - } - } - - if (*shouldPreventDefault) { - return; - } - - auto errorType = errorObj.getProperty(runtime, "type"); - auto isWarn = isEqualTo(runtime, errorType, "warn"); - - if (isFatal || !isWarn) { - if (isFatal) { - if (_hasHandledFatalError) { - return; - } - _hasHandledFatalError = true; - } - - _onJsError(runtime, processedError); - } -} - -void JsErrorHandler::registerErrorListener( - const std::function& errorListener) { - _errorListeners.push_back(errorListener); -} - -bool JsErrorHandler::hasHandledFatalError() { - return _hasHandledFatalError; -} - -void JsErrorHandler::setRuntimeReady() { - _isRuntimeReady = true; -} - -bool JsErrorHandler::isRuntimeReady() { - return _isRuntimeReady; -} - -void JsErrorHandler::notifyOfFatalError() { - _hasHandledFatalError = true; -} - -bool JsErrorHandler::inErrorHandler() { - return *_inErrorHandler; -} - -} // namespace facebook::react diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/test/testlib.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/test/testlib.cpp index f3a2cfa63dc..8a49c9229ed 100644 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/test/testlib.cpp +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/test/testlib.cpp @@ -1173,7 +1173,7 @@ TEST_P(JSITest, DecoratorTest) { class CountRuntime final : public WithRuntimeDecorator { public: - explicit CountRuntime(std::unique_ptr rt) + explicit CountRuntime(std::shared_ptr rt) : WithRuntimeDecorator(*rt, count_), rt_(std::move(rt)), count_(kInit) {} @@ -1183,7 +1183,7 @@ TEST_P(JSITest, DecoratorTest) { } private: - std::unique_ptr rt_; + std::shared_ptr rt_; Count count_; }; @@ -1222,7 +1222,7 @@ TEST_P(JSITest, MultiDecoratorTest) { class MultiRuntime final : public WithRuntimeDecorator> { public: - explicit MultiRuntime(std::unique_ptr rt) + explicit MultiRuntime(std::shared_ptr rt) : WithRuntimeDecorator>(*rt, tuple_), rt_(std::move(rt)) {} @@ -1234,7 +1234,7 @@ TEST_P(JSITest, MultiDecoratorTest) { } private: - std::unique_ptr rt_; + std::shared_ptr rt_; std::tuple tuple_; }; diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp index 344efffb526..f6c82b5f771 100644 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp @@ -27,7 +27,7 @@ static constexpr std::array kTextMIMETypePrefixes{ "application/javascript" // Not in Chromium but emitted by Metro }; -// namespace { [Windows #13587] +namespace { struct InitStreamResult { uint32_t httpStatusCode; @@ -41,6 +41,8 @@ using StreamInitCallback = using IOReadCallback = std::function)>; +} // namespace [Windows #13587] + /** * Private class owning state and implementing the listener for a particular * request @@ -54,7 +56,7 @@ class Stream : public NetworkRequestListener, Stream(const Stream& other) = delete; Stream& operator=(const Stream& other) = delete; Stream(Stream&& other) = default; - Stream& operator=(Stream&& other) = default; + Stream& operator=(Stream&& other) noexcept = default; /** * Factory method to create a Stream with a callback for the initial result @@ -67,9 +69,9 @@ class Stream : public NetworkRequestListener, */ static std::shared_ptr create( VoidExecutor executor, - StreamInitCallback initCb) { + const StreamInitCallback& initCb) { std::shared_ptr stream{new Stream(initCb)}; - stream->setExecutor(executor); + stream->setExecutor(std::move(executor)); return stream; } @@ -382,7 +384,7 @@ void NetworkIOAgent::handleIoRead(const cdp::PreparsedRequest& req) { "Invalid params: handle is missing or not a string.")); return; } - std::optional size = std::nullopt; // [Windows #13587] + std::optional size = std::nullopt; if ((req.params.count("size") != 0u) && req.params.at("size").isInt()) { size = req.params.at("size").asInt(); } diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp deleted file mode 100644 index c75d6d10787..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp +++ /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. - */ - -#include "JSRuntimeFactory.h" -#include -#include - -namespace facebook::react { - -jsi::Runtime& JSIRuntimeHolder::getRuntime() noexcept { - return *runtime_; -} - -JSIRuntimeHolder::JSIRuntimeHolder(std::unique_ptr runtime) - : runtime_(std::move(runtime)) { - assert(runtime_ != nullptr); -} - -void JSIRuntimeHolder::addConsoleMessage(jsi::Runtime& runtime, jsinspector_modern::ConsoleMessage message) { - return; -} - -bool JSIRuntimeHolder::supportsConsole() const{ - return false; -} - -std::unique_ptr -JSIRuntimeHolder::createAgentDelegate( - jsinspector_modern::FrontendChannel frontendChannel, - jsinspector_modern::SessionState& sessionState, - std::unique_ptr, - const jsinspector_modern::ExecutionContextDescription& - executionContextDescription, - RuntimeExecutor runtimeExecutor) { - (void)executionContextDescription; - (void)runtimeExecutor; - return std::make_unique( - std::move(frontendChannel), sessionState, runtime_->description()); -} - -} // namespace facebook::react \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h deleted file mode 100644 index 4f87670497c..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h +++ /dev/null @@ -1,91 +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 - -#ifdef __cplusplus - -#include -#include -#include -#include -#include - - -namespace facebook::react { - -/** - * An interface that represents an instance of a JS VM - */ -class JSRuntime : public jsinspector_modern::RuntimeTargetDelegate { - public: - virtual jsi::Runtime& getRuntime() noexcept = 0; - - virtual ~JSRuntime() = default; - - /** - * Get a reference to the \c RuntimeTargetDelegate owned (or implemented) by - * this JSRuntime. This reference must remain valid for the duration of the - * JSRuntime's lifetime. - */ - // virtual jsinspector_modern::RuntimeTargetDelegate& getRuntimeTargetDelegate(); - - /** - * Run initialize work that must happen on the runtime's JS thread. Used for - * initializing TLS and registering profiling. - * - * TODO T194671568 Move the runtime constructor to the JsThread - */ - virtual void unstable_initializeOnJsThread() {} - - private: - /** - * Initialized by \c getRuntimeTargetDelegate if not overridden, and then - * never changes. - */ - std::optional - runtimeTargetDelegate_; -}; - -/** - * Interface for a class that creates instances of a JS VM - */ -class JSRuntimeFactory { - public: - virtual std::unique_ptr createJSRuntime( - std::shared_ptr msgQueueThread) noexcept = 0; - - virtual ~JSRuntimeFactory() = default; -}; - -/** - * Utility class for creating a JSRuntime from a uniquely owned jsi::Runtime. - */ -class JSIRuntimeHolder : public JSRuntime { - public: - jsi::Runtime& getRuntime() noexcept override; - void addConsoleMessage(jsi::Runtime& runtime, jsinspector_modern::ConsoleMessage message) override; - bool supportsConsole() const override; - - std::unique_ptr createAgentDelegate( - jsinspector_modern::FrontendChannel frontendChannel, - jsinspector_modern::SessionState& sessionState, - std::unique_ptr - previouslyExportedState, - const jsinspector_modern::ExecutionContextDescription& - executionContextDescription, - RuntimeExecutor runtimeExecutor) override; - - explicit JSIRuntimeHolder(std::unique_ptr runtime); - - private: - std::unique_ptr runtime_; -}; - -} // namespace facebook::react - -#endif // __cplusplus diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp deleted file mode 100644 index 78876d42a7a..00000000000 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp +++ /dev/null @@ -1,665 +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 "ReactInstance.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace facebook::react { - -namespace { - -std::shared_ptr createRuntimeScheduler( - RuntimeExecutor runtimeExecutor, - RuntimeSchedulerTaskErrorHandler taskErrorHandler) { - std::shared_ptr scheduler = - std::make_shared( - runtimeExecutor, RuntimeSchedulerClock::now, taskErrorHandler); - scheduler->setPerformanceEntryReporter( - // FIXME: Move creation of PerformanceEntryReporter to here and - // guarantee that its lifetime is the same as the runtime. - PerformanceEntryReporter::getInstance().get()); - - return scheduler; -} - -} // namespace - -ReactInstance::ReactInstance( - std::unique_ptr runtime, - std::shared_ptr jsMessageQueueThread, - std::shared_ptr timerManager, - JsErrorHandler::OnJsError onJsError, - jsinspector_modern::HostTarget* parentInspectorTarget) - : runtime_(std::move(runtime)), - jsMessageQueueThread_(jsMessageQueueThread), - timerManager_(std::move(timerManager)), - jsErrorHandler_(std::make_shared(std::move(onJsError))), - parentInspectorTarget_(parentInspectorTarget) { - RuntimeExecutor runtimeExecutor = - [weakRuntime = std::weak_ptr(runtime_), - weakTimerManager = std::weak_ptr(timerManager_), - weakJsThread = std::weak_ptr(jsMessageQueueThread_), - jsErrorHandler = jsErrorHandler_](auto callback) { - if (weakRuntime.expired()) { - return; - } - - if (auto jsThread = weakJsThread.lock()) { - jsThread->runOnQueue([jsErrorHandler, - weakRuntime, - weakTimerManager, - callback = std::move(callback)]() { - auto runtime = weakRuntime.lock(); - if (!runtime) { - return; - } - - jsi::Runtime& jsiRuntime = runtime->getRuntime(); - TraceSection s("ReactInstance::_runtimeExecutor[Callback]"); - try { - ShadowNode::setUseRuntimeShadowNodeReferenceUpdateOnThread(true); - callback(jsiRuntime); - } catch (jsi::JSError& originalError) { - jsErrorHandler->handleError(jsiRuntime, originalError, true); - } catch (std::exception& ex) { - jsi::JSError error( - jsiRuntime, std::string("Non-js exception: ") + ex.what()); - jsErrorHandler->handleError(jsiRuntime, error, true); - } - }); - } - }; - - if (parentInspectorTarget_) { - auto executor = parentInspectorTarget_->executorFromThis(); - - auto bufferedRuntimeExecutorThatWaitsForInspectorSetup = - std::make_shared(runtimeExecutor); - auto runtimeExecutorThatExecutesAfterInspectorSetup = - [bufferedRuntimeExecutorThatWaitsForInspectorSetup]( - std::function&& callback) { - bufferedRuntimeExecutorThatWaitsForInspectorSetup->execute( - std::move(callback)); - }; - - runtimeScheduler_ = createRuntimeScheduler( - runtimeExecutorThatExecutesAfterInspectorSetup, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& runtime, jsi::JSError& error) { - jsErrorHandler->handleError(runtime, error, true); - }); - - auto runtimeExecutorThatGoesThroughRuntimeScheduler = - [runtimeScheduler = runtimeScheduler_.get()]( - std::function&& callback) { - runtimeScheduler->scheduleWork(std::move(callback)); - }; - - // This code can execute from any thread, so we need to make sure we set up - // the inspector logic in the right one. The callback executes immediately - // if we are already in the right thread. - executor([this, - runtimeExecutorThatGoesThroughRuntimeScheduler, - bufferedRuntimeExecutorThatWaitsForInspectorSetup]( - jsinspector_modern::HostTarget& hostTarget) { - // Callbacks scheduled through the page target executor are generally - // not guaranteed to run (e.g.: if the page target is destroyed) - // but in this case it is because the page target cannot be destroyed - // before the instance finishes its setup: - // * On iOS it's because we do the setup synchronously. - // * On Android it's because we explicitly wait for the instance - // creation task to finish before starting the destruction. - inspectorTarget_ = &hostTarget.registerInstance(*this); - runtimeInspectorTarget_ = - &inspectorTarget_->registerRuntime(*runtime_, runtimeExecutorThatGoesThroughRuntimeScheduler); // [Windows #13172] - bufferedRuntimeExecutorThatWaitsForInspectorSetup->flush(); - }); - } else { - runtimeScheduler_ = createRuntimeScheduler( - runtimeExecutor, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& runtime, jsi::JSError& error) { - jsErrorHandler->handleError(runtime, error, true); - }); - } - - bufferedRuntimeExecutor_ = std::make_shared( - [runtimeScheduler = runtimeScheduler_.get()]( - std::function&& callback) { - runtimeScheduler->scheduleWork(std::move(callback)); - }); -} - -void ReactInstance::unregisterFromInspector() { - if (inspectorTarget_) { - assert(runtimeInspectorTarget_); - inspectorTarget_->unregisterRuntime(*runtimeInspectorTarget_); - - assert(parentInspectorTarget_); - parentInspectorTarget_->unregisterInstance(*inspectorTarget_); - - inspectorTarget_ = nullptr; - } -} - -RuntimeExecutor ReactInstance::getUnbufferedRuntimeExecutor() noexcept { - return [runtimeScheduler = runtimeScheduler_.get()]( - std::function&& callback) { - runtimeScheduler->scheduleWork(std::move(callback)); - }; -} - -// This BufferedRuntimeExecutor ensures that the main JS bundle finished -// execution before any JS queued into it from C++ are executed. Use -// getUnbufferedRuntimeExecutor() instead if you do not need the main JS bundle -// to have finished. e.g. setting global variables into JS runtime. -RuntimeExecutor ReactInstance::getBufferedRuntimeExecutor() noexcept { - return [weakBufferedRuntimeExecutor_ = - std::weak_ptr(bufferedRuntimeExecutor_)]( - std::function&& callback) { - if (auto strongBufferedRuntimeExecutor_ = - weakBufferedRuntimeExecutor_.lock()) { - strongBufferedRuntimeExecutor_->execute(std::move(callback)); - } - }; -} - -// TODO(T184010230): Should the RuntimeScheduler returned from this method be -// buffered? -std::shared_ptr -ReactInstance::getRuntimeScheduler() noexcept { - return runtimeScheduler_; -} - -namespace { - -// Copied from JSIExecutor.cpp -// basename_r isn't in all iOS SDKs, so use this simple version instead. -std::string simpleBasename(const std::string& path) { - size_t pos = path.rfind("/"); - return (pos != std::string::npos) ? path.substr(pos) : path; -} - -} // namespace - -/** - * Load the JS bundle and flush buffered JS calls, future JS calls won't be - * buffered after calling this. - * Note that this method is asynchronous. However, a completion callback - * isn't needed because all calls into JS should be dispatched to the JSThread, - * preferably via the runtimeExecutor_. - */ -void ReactInstance::loadScript( - std::unique_ptr script, - const std::string& sourceURL, - std::function&& completion) { - auto buffer = std::make_shared(std::move(script)); - std::string scriptName = simpleBasename(sourceURL); - - runtimeScheduler_->scheduleWork([this, - scriptName, - sourceURL, - buffer = std::move(buffer), - weakBufferedRuntimeExecuter = - std::weak_ptr( - bufferedRuntimeExecutor_), - completion](jsi::Runtime& runtime) { - TraceSection s("ReactInstance::loadScript"); - bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl); - if (hasLogger) { - ReactMarker::logTaggedMarkerBridgeless( - ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str()); - } - - runtime.evaluateJavaScript(buffer, sourceURL); - - /** - * TODO(T183610671): We need a safe/reliable way to enable the js - * pipeline from javascript. Remove this after we figure that out, or - * after we just remove the js pipeline. - */ - if (!jsErrorHandler_->hasHandledFatalError()) { - jsErrorHandler_->setRuntimeReady(); - } - - if (hasLogger) { - ReactMarker::logTaggedMarkerBridgeless( - ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str()); - ReactMarker::logMarkerBridgeless(ReactMarker::INIT_REACT_RUNTIME_STOP); - ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP); - } - if (auto strongBufferedRuntimeExecuter = - weakBufferedRuntimeExecuter.lock()) { - strongBufferedRuntimeExecuter->flush(); - } - if (completion) { - completion(runtime); - } - }); -} - -/* - * Calls a method on a JS module that has been registered with - * `registerCallableModule`. Used to invoke a JS function from platform code. - */ -void ReactInstance::callFunctionOnModule( - const std::string& moduleName, - const std::string& methodName, - folly::dynamic&& args) { - if (bufferedRuntimeExecutor_ == nullptr) { - LOG(ERROR) - << "Calling callFunctionOnModule with null BufferedRuntimeExecutor"; - return; - } - - bufferedRuntimeExecutor_->execute([this, - moduleName = moduleName, - methodName = methodName, - args = std::move(args)]( - jsi::Runtime& runtime) { - TraceSection s( - "ReactInstance::callFunctionOnModule", - "moduleName", - moduleName, - "methodName", - methodName); - auto it = callableModules_.find(moduleName); - if (it == callableModules_.end()) { - std::ostringstream knownModules; - int i = 0; - for (it = callableModules_.begin(); it != callableModules_.end(); - it++, i++) { - const char* space = (i > 0 ? ", " : " "); - knownModules << space << it->first; - } - throw jsi::JSError( - runtime, - "Failed to call into JavaScript module method " + moduleName + "." + - methodName + - "(). Module has not been registered as callable. Registered callable JavaScript modules (n = " + - std::to_string(callableModules_.size()) + - "):" + knownModules.str() + - ". Did you forget to call `registerCallableModule`?"); - } - - if (std::holds_alternative(it->second)) { - auto module = - std::get(it->second).call(runtime).asObject(runtime); - it->second = std::move(module); - } - - auto& module = std::get(it->second); - auto method = module.getPropertyAsFunction(runtime, methodName.c_str()); - - std::vector jsArgs; - for (auto& arg : args) { - jsArgs.push_back(jsi::valueFromDynamic(runtime, arg)); - } - method.callWithThis( - runtime, module, (const jsi::Value*)jsArgs.data(), jsArgs.size()); - }); -} - -void ReactInstance::registerSegment( - uint32_t segmentId, - const std::string& segmentPath) { - LOG(WARNING) << "Starting to run ReactInstance::registerSegment with segment " - << segmentId; - runtimeScheduler_->scheduleWork([=](jsi::Runtime& runtime) { - TraceSection s("ReactInstance::registerSegment"); - const auto tag = folly::to(segmentId); - auto script = JSBigFileString::fromPath(segmentPath); - if (script->size() == 0) { - throw std::invalid_argument( - "Empty segment registered with ID " + tag + " from " + segmentPath); - } - auto buffer = std::make_shared(std::move(script)); - - bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl); - if (hasLogger) { - ReactMarker::logTaggedMarkerBridgeless( - ReactMarker::REGISTER_JS_SEGMENT_START, tag.c_str()); - } - LOG(WARNING) << "Starting to evaluate segment " << segmentId - << " in ReactInstance::registerSegment"; - runtime.evaluateJavaScript( - buffer, JSExecutor::getSyntheticBundlePath(segmentId, segmentPath)); - LOG(WARNING) << "Finished evaluating segment " << segmentId - << " in ReactInstance::registerSegment"; - if (hasLogger) { - ReactMarker::logTaggedMarkerBridgeless( - ReactMarker::REGISTER_JS_SEGMENT_STOP, tag.c_str()); - } - }); -} - -namespace { -void defineReactInstanceFlags( - jsi::Runtime& runtime, - ReactInstance::JSRuntimeFlags options) noexcept { - defineReadOnlyGlobal(runtime, "RN$Bridgeless", jsi::Value(true)); - - if (options.isProfiling) { - defineReadOnlyGlobal(runtime, "__RCTProfileIsProfiling", jsi::Value(true)); - } - - if (options.runtimeDiagnosticFlags.length() > 0) { - defineReadOnlyGlobal( - runtime, - "RN$DiagnosticFlags", - jsi::String::createFromUtf8(runtime, options.runtimeDiagnosticFlags)); - } -} - -bool isTruthy(jsi::Runtime& runtime, const jsi::Value& value) { - auto Boolean = runtime.global().getPropertyAsFunction(runtime, "Boolean"); - return Boolean.call(runtime, value).getBool(); -} - -} // namespace - -void ReactInstance::initializeRuntime( - JSRuntimeFlags options, - BindingsInstallFunc bindingsInstallFunc) noexcept { - runtimeScheduler_->scheduleWork([this, options, bindingsInstallFunc]( - jsi::Runtime& runtime) { - TraceSection s("ReactInstance::initializeRuntime"); - - bindNativePerformanceNow(runtime); - - RuntimeSchedulerBinding::createAndInstallIfNeeded( - runtime, runtimeScheduler_); - - runtime_->unstable_initializeOnJsThread(); - - defineReactInstanceFlags(runtime, options); - - defineReadOnlyGlobal( - runtime, - "RN$useAlwaysAvailableJSErrorHandling", - jsi::Value( - ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling())); - - defineReadOnlyGlobal( - runtime, - "RN$isRuntimeReady", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "isRuntimeReady"), - 0, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& /*runtime*/, - const jsi::Value& /*unused*/, - const jsi::Value* /*args*/, - size_t /*count*/) { - return jsErrorHandler->isRuntimeReady(); - })); - - defineReadOnlyGlobal( - runtime, - "RN$hasHandledFatalException", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "hasHandledFatalException"), - 0, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& /*runtime*/, - const jsi::Value& /*unused*/, - const jsi::Value* /*args*/, - size_t /*count*/) { - return jsErrorHandler->hasHandledFatalError(); - })); - - defineReadOnlyGlobal( - runtime, - "RN$notifyOfFatalException", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "notifyOfFatalException"), - 0, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& /*runtime*/, - const jsi::Value& /*unused*/, - const jsi::Value* /*args*/, - size_t /*count*/) { - jsErrorHandler->notifyOfFatalError(); - return jsi::Value::undefined(); - })); - - defineReadOnlyGlobal( - runtime, - "RN$inExceptionHandler", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "inExceptionHandler"), - 0, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& /*runtime*/, - const jsi::Value& /*unused*/, - const jsi::Value* /*args*/, - size_t /*count*/) { - return jsErrorHandler->inErrorHandler(); - })); - - // TODO(T196834299): We should really use a C++ turbomodule for this - defineReadOnlyGlobal( - runtime, - "RN$handleException", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "handleException"), - 3, - [jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& runtime, - const jsi::Value& /*unused*/, - const jsi::Value* args, - size_t count) { - if (count < 2) { - throw jsi::JSError( - runtime, - "handleException requires 3 arguments: error, isFatal, logToConsole (optional)"); - } - - auto isFatal = isTruthy(runtime, args[1]); - - if (!ReactNativeFeatureFlags:: - useAlwaysAvailableJSErrorHandling()) { - if (jsErrorHandler->isRuntimeReady()) { - return jsi::Value(false); - } - } - - auto jsError = - jsi::JSError(runtime, jsi::Value(runtime, args[0])); - - if (count == 2) { - jsErrorHandler->handleError(runtime, jsError, isFatal); - } else { - auto logToConsole = isTruthy(runtime, args[2]); - jsErrorHandler->handleError( - runtime, jsError, isFatal, logToConsole); - } - - return jsi::Value(true); - })); - - defineReadOnlyGlobal( - runtime, - "RN$registerExceptionListener", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "registerExceptionListener"), - 1, - [errorListeners = std::vector>(), - jsErrorHandler = jsErrorHandler_]( - jsi::Runtime& runtime, - const jsi::Value& /*unused*/, - const jsi::Value* args, - size_t count) mutable { - if (count < 1) { - throw jsi::JSError( - runtime, - "registerExceptionListener: requires 1 argument: fn"); - } - - if (!args[0].isObject() || - !args[0].getObject(runtime).isFunction(runtime)) { - throw jsi::JSError( - runtime, - "registerExceptionListener: The first argument must be a function"); - } - - auto errorListener = std::make_shared( - args[0].getObject(runtime).getFunction(runtime)); - errorListeners.emplace_back(errorListener); - - jsErrorHandler->registerErrorListener( - [weakErrorListener = std::weak_ptr( - errorListener)](jsi::Runtime& runtime, jsi::Value data) { - if (auto strongErrorListener = weakErrorListener.lock()) { - strongErrorListener->call(runtime, data); - } - }); - - return jsi::Value::undefined(); - })); - - defineReadOnlyGlobal( - runtime, - "RN$registerCallableModule", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "registerCallableModule"), - 2, - [this]( - jsi::Runtime& runtime, - const jsi::Value& /*unused*/, - const jsi::Value* args, - size_t count) { - if (count != 2) { - throw jsi::JSError( - runtime, - "registerCallableModule requires exactly 2 arguments"); - } - if (!args[0].isString()) { - throw jsi::JSError( - runtime, - "The first argument to registerCallableModule must be a string (the name of the JS module)."); - } - auto name = args[0].asString(runtime).utf8(runtime); - if (!args[1].isObject() || - !args[1].getObject(runtime).isFunction(runtime)) { - throw jsi::JSError( - runtime, - "The second argument to registerCallableModule must be a function that returns the JS module."); - } - callableModules_.emplace( - std::move(name), - args[1].getObject(runtime).getFunction(runtime)); - return jsi::Value::undefined(); - })); - - timerManager_->attachGlobals(runtime); - - bindingsInstallFunc(runtime); - }); -} - -void ReactInstance::handleMemoryPressureJs(int pressureLevel) { - // The level is an enum value passed by the Android OS to an onTrimMemory - // event callback. Defined in ComponentCallbacks2. - enum AndroidMemoryPressure { - TRIM_MEMORY_BACKGROUND = 40, - TRIM_MEMORY_COMPLETE = 80, - TRIM_MEMORY_MODERATE = 60, - TRIM_MEMORY_RUNNING_CRITICAL = 15, - TRIM_MEMORY_RUNNING_LOW = 10, - TRIM_MEMORY_RUNNING_MODERATE = 5, - TRIM_MEMORY_UI_HIDDEN = 20, - }; - const char* levelName; - switch (pressureLevel) { - case TRIM_MEMORY_BACKGROUND: - levelName = "TRIM_MEMORY_BACKGROUND"; - break; - case TRIM_MEMORY_COMPLETE: - levelName = "TRIM_MEMORY_COMPLETE"; - break; - case TRIM_MEMORY_MODERATE: - levelName = "TRIM_MEMORY_MODERATE"; - break; - case TRIM_MEMORY_RUNNING_CRITICAL: - levelName = "TRIM_MEMORY_RUNNING_CRITICAL"; - break; - case TRIM_MEMORY_RUNNING_LOW: - levelName = "TRIM_MEMORY_RUNNING_LOW"; - break; - case TRIM_MEMORY_RUNNING_MODERATE: - levelName = "TRIM_MEMORY_RUNNING_MODERATE"; - break; - case TRIM_MEMORY_UI_HIDDEN: - levelName = "TRIM_MEMORY_UI_HIDDEN"; - break; - default: - levelName = "UNKNOWN"; - break; - } - - switch (pressureLevel) { - case TRIM_MEMORY_RUNNING_LOW: - case TRIM_MEMORY_RUNNING_MODERATE: - case TRIM_MEMORY_UI_HIDDEN: - // For non-severe memory trims, do nothing. - LOG(INFO) << "Memory warning (pressure level: " << levelName - << ") received by JS VM, ignoring because it's non-severe"; - break; - case TRIM_MEMORY_BACKGROUND: - case TRIM_MEMORY_COMPLETE: - case TRIM_MEMORY_MODERATE: - case TRIM_MEMORY_RUNNING_CRITICAL: - // For now, pressureLevel is unused by collectGarbage. - // This may change in the future if the JS GC has different styles of - // collections. - LOG(INFO) << "Memory warning (pressure level: " << levelName - << ") received by JS VM, running a GC"; - runtimeScheduler_->scheduleWork([=](jsi::Runtime& runtime) { - TraceSection s("ReactInstance::handleMemoryPressure"); - runtime.instrumentation().collectGarbage(levelName); - }); - break; - default: - // Use the raw number instead of the name here since the name is - // meaningless. - LOG(WARNING) << "Memory warning (pressure level: " << pressureLevel - << ") received by JS VM, unrecognized pressure level"; - break; - } -} - -void* ReactInstance::getJavaScriptContext() { - return &runtime_->getRuntime(); -} - -} // namespace facebook::react diff --git a/vnext/ReactCommon/cgmanifest.json b/vnext/ReactCommon/cgmanifest.json index 7644e64a4d1..0a1ad2798df 100644 --- a/vnext/ReactCommon/cgmanifest.json +++ b/vnext/ReactCommon/cgmanifest.json @@ -6,7 +6,7 @@ "Type": "git", "Git": { "RepositoryUrl": "https://github.com/microsoft/node-api-jsi", - "CommitHash": "980cb60d7911237d0f647fc566543ef627adac70" + "CommitHash": "21b47f08b762b21b1d4d970940ab23f59f43249c" } }, "DevelopmentDependency": false diff --git a/vnext/Scripts/Tfs/Layout-MSRN-Headers.ps1 b/vnext/Scripts/Tfs/Layout-MSRN-Headers.ps1 index 7f1c54d2583..77f5a34a58c 100644 --- a/vnext/Scripts/Tfs/Layout-MSRN-Headers.ps1 +++ b/vnext/Scripts/Tfs/Layout-MSRN-Headers.ps1 @@ -78,6 +78,8 @@ New-Item $MSRNCxxTargetRoot\node-api -ItemType Directory -Force Copy-Item -Force -Path $NodeApiJsiRoot\node-api\js_native_api.h -Destination $MSRNCxxTargetRoot\node-api\ Copy-Item -Force -Path $NodeApiJsiRoot\node-api\js_native_api_types.h -Destination $MSRNCxxTargetRoot\node-api\ Copy-Item -Force -Path $NodeApiJsiRoot\node-api\js_runtime_api.h -Destination $MSRNCxxTargetRoot\node-api\ +Copy-Item -Force -Path $NodeApiJsiRoot\node-api\node_api.h -Destination $MSRNCxxTargetRoot\node-api\ +Copy-Item -Force -Path $NodeApiJsiRoot\node-api\node_api_types.h -Destination $MSRNCxxTargetRoot\node-api\ # Microsoft.ReactNative.CXX project Node-API JSI files New-Item $MSRNCxxTargetRoot\ApiLoaders -ItemType Directory -Force diff --git a/vnext/Shared/DevServerHelper.h b/vnext/Shared/DevServerHelper.h index 384a7619810..9fc4cbcf4d9 100644 --- a/vnext/Shared/DevServerHelper.h +++ b/vnext/Shared/DevServerHelper.h @@ -76,12 +76,20 @@ class DevServerHelper { const std::string &packagerHost, const uint16_t packagerPort, const std::string &deviceName, - const std::string &packageName) { + const std::string &packageName, + const std::string &deviceId) { return string_format( InspectorDeviceUrlFormat, GetDeviceLocalHost(packagerHost, packagerPort).c_str(), deviceName.c_str(), - packageName.c_str()); + packageName.c_str(), + deviceId.c_str()); + } + + static std::string + get_OpenDebuggerUrl(const std::string &packagerHost, const uint16_t packagerPort, const std::string &deviceId) { + return string_format( + OpenDebuggerUrlFormat, GetDeviceLocalHost(packagerHost, packagerPort).c_str(), deviceId.c_str()); } static constexpr const char DefaultPackagerHost[] = "localhost"; @@ -105,11 +113,13 @@ class DevServerHelper { static constexpr const char PackagerConnectionUrlFormat[] = "ws://%s/message"; static constexpr const char PackagerStatusUrlFormat[] = "http://%s/status"; static constexpr const char PackagerOpenStackFrameUrlFormat[] = "https://%s/open-stack-frame"; - static constexpr const char InspectorDeviceUrlFormat[] = "ws://%s/inspector/device?name=%s&app=%s"; + static constexpr const char InspectorDeviceUrlFormat[] = "ws://%s/inspector/device?name=%s&app=%s&device=%s"; + static constexpr const char OpenDebuggerUrlFormat[] = "http://%s/open-debugger?device=%s"; static constexpr const char PackagerOkStatus[] = "packager-status:running"; const int LongPollFailureDelayMs = 5000; + // TODO: [vmoroz] avoid using vaiadic args for the format and move it to a utility class. template static std::string string_format(const char *format, Args... args) { size_t size = snprintf(nullptr, 0, format, args...) + 1; diff --git a/vnext/Shared/DevSettings.h b/vnext/Shared/DevSettings.h index b6ddcb0745e..0591f9f404d 100644 --- a/vnext/Shared/DevSettings.h +++ b/vnext/Shared/DevSettings.h @@ -23,6 +23,10 @@ struct RuntimeHolderLazyInit; namespace facebook { namespace react { +namespace jsinspector_modern { +class HostTarget; +} // namespace jsinspector_modern + enum class JSIEngineOverride : int32_t { Default = 0, // No JSI, will use the legacy ExecutorFactory Chakra = 1, // Use the JSIExecutorFactory with ChakraRuntime @@ -117,6 +121,9 @@ struct DevSettings { // If true, then use only Turbo Modules instead of CxxModules. bool useTurboModulesOnly{false}; + + // The HostTarget instance for Fusebox + facebook::react::jsinspector_modern::HostTarget *inspectorHostTarget; }; } // namespace react diff --git a/vnext/Shared/DevSupportManager.cpp b/vnext/Shared/DevSupportManager.cpp index a6935bb8aa9..70d3a2657ce 100644 --- a/vnext/Shared/DevSupportManager.cpp +++ b/vnext/Shared/DevSupportManager.cpp @@ -9,14 +9,19 @@ #include #include +#include "Inspector/ReactInspectorPackagerConnectionDelegate.h" #include "PackagerConnection.h" #include "Unicode.h" #include "Utilities.h" #include +#include #include +#include +#include #include +#include #include #include #include @@ -171,6 +176,49 @@ bool IsIgnorablePollHResult(HRESULT hr) { return hr == WININET_E_INVALID_SERVER_RESPONSE; } +std::string GetDeviceId(const std::string &packageName) { + const auto hash = winrt::Windows::Security::Cryptography::Core::HashAlgorithmProvider::OpenAlgorithm( + winrt::Windows::Security::Cryptography::Core::HashAlgorithmNames::Sha256()) + .CreateHash(); + hash.Append(winrt::Windows::System::Profile::SystemIdentification::GetSystemIdForPublisher().Id()); + winrt::Windows::Storage::Streams::InMemoryRandomAccessStream stream; + winrt::Windows::Storage::Streams::DataWriter writer; + // If an app ID is provided, we will allow reconnection to DevTools. + // Apps must supply a unique app ID to each ReactNativeHost instance settings for this to behave correctly. + if (!packageName.empty()) { + const auto packageNameBuffer = winrt::Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary( + winrt::to_hstring(packageName), winrt::Windows::Security::Cryptography::BinaryStringEncoding::Utf16BE); + hash.Append(packageNameBuffer); + } else { + const auto processId = GetCurrentProcessId(); + std::vector processIdBytes( + reinterpret_cast(&processId), reinterpret_cast(&processId + 1)); + winrt::array_view processIdByteArray(processIdBytes); + const auto processIdBuffer = + winrt::Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(processIdByteArray); + hash.Append(processIdBuffer); + } + const auto hashBuffer = hash.GetValueAndReset(); + const auto hashString = winrt::Windows::Security::Cryptography::CryptographicBuffer::EncodeToHexString(hashBuffer); + return winrt::to_string(hashString); +} + +std::string GetPackageName(const std::string &bundleAppId) { + if (!bundleAppId.empty()) { + return bundleAppId; + } + + std::string packageName{"RNW"}; + wchar_t fullName[PACKAGE_FULL_NAME_MAX_LENGTH]{}; + uint32_t size = ARRAYSIZE(fullName); + if (SUCCEEDED(GetCurrentPackageFullName(&size, fullName))) { + // we are in an unpackaged app + packageName = winrt::to_string(fullName); + } + + return packageName; +} + std::future PollForLiveReload(const std::string &url) { winrt::Windows::Web::Http::HttpClient httpClient; winrt::Windows::Foundation::Uri uri(Microsoft::Common::Unicode::Utf8ToUtf16(url)); @@ -238,37 +286,48 @@ void DevSupportManager::StopPollingLiveReload() { m_cancellation_token = true; } -void DevSupportManager::EnsureHermesInspector( +// TODO: (vmoroz) Use or delete this function +void DevSupportManager::OpenDevTools(const std::string &bundleAppId) { + winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter; + filter.CacheControl().ReadBehavior(winrt::Windows::Web::Http::Filters::HttpCacheReadBehavior::NoCache); + winrt::Windows::Web::Http::HttpClient httpClient(filter); + // TODO: Use currently configured dev server host + winrt::Windows::Foundation::Uri uri( + Microsoft::Common::Unicode::Utf8ToUtf16(facebook::react::DevServerHelper::get_OpenDebuggerUrl( + std::string{DevServerHelper::DefaultPackagerHost}, + DevServerHelper::DefaultPackagerPort, + GetDeviceId(GetPackageName(bundleAppId))))); + + winrt::Windows::Web::Http::HttpRequestMessage request(winrt::Windows::Web::Http::HttpMethod::Post(), uri); + httpClient.SendRequestAsync(request); +} + +void DevSupportManager::EnsureInspectorPackagerConnection( [[maybe_unused]] const std::string &packagerHost, - [[maybe_unused]] const uint16_t packagerPort) noexcept { + [[maybe_unused]] const uint16_t packagerPort, + [[maybe_unused]] const std::string &bundleAppId) noexcept { static std::once_flag once; - std::call_once(once, [this, &packagerHost, packagerPort]() { - // TODO: should we use the bundleAppId as the app param if available? - std::string packageName("RNW"); - wchar_t fullName[PACKAGE_FULL_NAME_MAX_LENGTH]{}; - UINT32 size = ARRAYSIZE(fullName); - if (SUCCEEDED(GetCurrentPackageFullName(&size, fullName))) { - // we are in an unpackaged app - packageName = winrt::to_string(fullName); - } - + std::call_once(once, [this, &packagerHost, packagerPort, &bundleAppId]() { + std::string packageName = GetPackageName(bundleAppId); std::string deviceName("RNWHost"); auto hostNames = winrt::Windows::Networking::Connectivity::NetworkInformation::GetHostNames(); if (hostNames && hostNames.First() && hostNames.First().Current()) { deviceName = winrt::to_string(hostNames.First().Current().DisplayName()); } - m_inspectorPackagerConnection = std::make_shared( - facebook::react::DevServerHelper::get_InspectorDeviceUrl(packagerHost, packagerPort, deviceName, packageName), - m_BundleStatusProvider); - m_inspectorPackagerConnection->connectAsync(); + std::string deviceId = GetDeviceId(packageName); + std::string inspectorUrl = facebook::react::DevServerHelper::get_InspectorDeviceUrl( + packagerHost, packagerPort, deviceName, packageName, deviceId); + jsinspector_modern::InspectorFlags &inspectorFlags = jsinspector_modern::InspectorFlags::getInstance(); + m_inspectorPackagerConnection = std::make_unique( + inspectorUrl, + deviceName, + packageName, + std::make_unique()); + m_inspectorPackagerConnection->connect(); }); } -void DevSupportManager::UpdateBundleStatus(bool isLastDownloadSuccess, int64_t updateTimestamp) noexcept { - m_BundleStatusProvider->updateBundleStatus(isLastDownloadSuccess, updateTimestamp); -} - std::pair GetJavaScriptFromServer( const std::string &sourceBundleHost, const uint16_t sourceBundlePort, diff --git a/vnext/Shared/DevSupportManager.h b/vnext/Shared/DevSupportManager.h index 5c33ed1de02..a6dc1705af3 100644 --- a/vnext/Shared/DevSupportManager.h +++ b/vnext/Shared/DevSupportManager.h @@ -14,7 +14,7 @@ #include #include -#include +#include namespace facebook { namespace react { @@ -48,29 +48,17 @@ class DevSupportManager final : public facebook::react::IDevSupportManager { const uint16_t sourceBundlePort, std::function onChangeCallback) override; virtual void StopPollingLiveReload() override; + virtual void OpenDevTools(const std::string &bundleAppId) override; - virtual void EnsureHermesInspector(const std::string &packagerHost, const uint16_t packagerPort) noexcept override; - virtual void UpdateBundleStatus(bool isLastDownloadSuccess, int64_t updateTimestamp) noexcept override; + virtual void EnsureInspectorPackagerConnection( + const std::string &packagerHost, + const uint16_t packagerPort, + const std::string &bundleAppId) noexcept override; private: std::atomic_bool m_cancellation_token; - std::shared_ptr m_inspectorPackagerConnection; - - struct BundleStatusProvider : public InspectorPackagerConnection::IBundleStatusProvider { - virtual InspectorPackagerConnection::BundleStatus getBundleStatus() { - return m_bundleStatus; - } - - void updateBundleStatus(bool isLastDownloadSuccess, int64_t updateTimestamp) { - m_bundleStatus.m_isLastDownloadSuccess = isLastDownloadSuccess; - m_bundleStatus.m_updateTimestamp = updateTimestamp; - } - - private: - InspectorPackagerConnection::BundleStatus m_bundleStatus; - }; - std::shared_ptr m_BundleStatusProvider = std::make_shared(); + std::unique_ptr m_inspectorPackagerConnection; }; } // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp new file mode 100644 index 00000000000..6a1b20535bc --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* + * 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. + */ + +// This file must match the code in React Native folder: +// ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegate.cpp +// Unlike the code in React Native sources, this class delegates calls to Hermes C-based API. + +#include "HermesRuntimeAgentDelegate.h" + +#include + +using namespace facebook::react::jsinspector_modern; + +namespace Microsoft::ReactNative { + +namespace { + +struct HermesStateWrapper : public RuntimeAgentDelegate::ExportedState { + explicit HermesStateWrapper(HermesUniqueCdpState &&hermesCdpState) : hermesCdpState_(std::move(hermesCdpState)) {} + + static HermesUniqueCdpState unwrapDestructively(ExportedState *wrapper) { + if (!wrapper) { + return {}; + } + if (auto *typedWrapper = dynamic_cast(wrapper)) { + return std::move(typedWrapper->hermesCdpState_); + } + return {}; + } + + private: + HermesUniqueCdpState hermesCdpState_; +}; + +} // namespace + +HermesRuntimeAgentDelegate::HermesRuntimeAgentDelegate( + FrontendChannel frontendChannel, + SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const ExecutionContextDescription &executionContextDescription, + hermes_runtime runtime, + HermesRuntimeTargetDelegate &runtimeTargetDelegate, + facebook::react::RuntimeExecutor runtimeExecutor) + : hermesCdpAgent_(HermesInspectorApi::createCdpAgent( + runtimeTargetDelegate.getCdpDebugApi(), + executionContextDescription.id, + // Adapt std::function&& callback)> + // to hermes_enqueue_runtime_task_functor + AsFunctor( + [runtimeExecutor = std::move(runtimeExecutor), runtime](hermes_run_runtime_task_functor runtimeTask) { + // Adapt std::function to hermes_runtime_task_functor + runtimeExecutor( + [runtime, fn = std::make_shared>(runtimeTask)]( + facebook::jsi::Runtime &rt) { (*fn)(runtime); }); + }), + // Adapt void(const char *json_utf8, size_t json_size) to std::function + AsFunctor( + [frontendChannel = std::move(frontendChannel)](const char *json_utf8, size_t json_size) { + frontendChannel(std::string_view(json_utf8, json_size)); + }), + HermesStateWrapper::unwrapDestructively(previouslyExportedState.get()).release())) { + // Enable domains conditionally based on session state + // This matches the iOS/Android implementation pattern: + // Domains are enabled in response to Chrome DevTools sending Runtime.enable/Debugger.enable + if (sessionState.isRuntimeDomainEnabled) { + HermesInspectorApi::enableRuntimeDomain(hermesCdpAgent_.get()); + } + if (sessionState.isDebuggerDomainEnabled) { + HermesInspectorApi::enableDebuggerDomain(hermesCdpAgent_.get()); + } +} + +HermesRuntimeAgentDelegate::~HermesRuntimeAgentDelegate() = default; + +bool HermesRuntimeAgentDelegate::handleRequest(const cdp::PreparsedRequest &req) { + if (req.method.starts_with("Log.")) { + // Since we know Hermes doesn't do anything useful with Log messages, + // but our containing HostAgent will, bail out early. + return false; + } + + std::string json = req.toJson(); + HermesInspectorApi::handleCommand(hermesCdpAgent_.get(), json.c_str(), json.size()); + return true; +} + +std::unique_ptr HermesRuntimeAgentDelegate::getExportedState() { + return std::make_unique(HermesInspectorApi::getCdpState(hermesCdpAgent_.get())); +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h new file mode 100644 index 00000000000..149acc0c032 --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* + * 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. + */ + +// This file must match the code in React Native folder: +// ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegate.h +// Unlike the code in React Native sources, this class delegates calls to Hermes C-based API. +// We use different namespace for this class comparing with the RN code. + +#pragma once + +#include "HermesRuntimeTargetDelegate.h" + +#include +#include +#include "HermesRuntimeHolder.h" + +namespace Microsoft::ReactNative { + +// A RuntimeAgentDelegate that handles requests from the Chrome DevTools +// Protocol for an instance of Hermes, using the modern CDPAgent API. +class HermesRuntimeAgentDelegate : public facebook::react::jsinspector_modern::RuntimeAgentDelegate { + public: + /** + * \param frontendChannel A channel used to send responses and events to the + * frontend. + * \param sessionState The state of the current CDP session. This will only + * be accessed on the main thread (during the constructor, in handleRequest, + * etc). + * \param previouslyExportedState The exported state from a previous instance + * of RuntimeAgentDelegate (NOT necessarily HermesRuntimeAgentDelegate). + * This may be nullptr, and if not nullptr it may be of any concrete type that + * implements RuntimeAgentDelegate::ExportedState. + * \param executionContextDescription A description of the execution context + * represented by this runtime. This is used for disambiguating the + * source/destination of CDP messages when there are multiple runtimes + * (concurrently or over the life of a Host). + * \param hermes_runtime The Hermes runtime that this agent is attached to. The caller + * is responsible for keeping this object alive for the duration of the + * \c HermesRuntimeAgentDelegate lifetime. + * \param runtimeTargetDelegate The \c HermesRuntimeTargetDelegate object + * object for the passed runtime. + * \param runtimeExecutor A callback for scheduling work on the JS thread. + * \c runtimeExecutor may drop scheduled work if the runtime is destroyed + * first. + */ + HermesRuntimeAgentDelegate( + facebook::react::jsinspector_modern::FrontendChannel frontendChannel, + facebook::react::jsinspector_modern::SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, + hermes_runtime runtime, + HermesRuntimeTargetDelegate &runtimeTargetDelegate, + facebook::react::RuntimeExecutor runtimeExecutor); + + ~HermesRuntimeAgentDelegate() override; + + public: // RuntimeAgentDelegate implementation + /** + * Handle a CDP request. The response will be sent over the provided + * \c FrontendChannel synchronously or asynchronously. + * \param req The parsed request. + * \returns true if this agent has responded, or will respond asynchronously, + * to the request (with either a success or error message). False if the + * agent expects another agent to respond to the request instead. + */ + bool handleRequest(const facebook::react::jsinspector_modern::cdp::PreparsedRequest &req) override; + + std::unique_ptr getExportedState() override; + + private: + HermesUniqueCdpAgent hermesCdpAgent_; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp new file mode 100644 index 00000000000..baf34e56eb6 --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* + * 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. + */ + +// This file must match the code in React Native folder: +// ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.cpp +// Unlike the code in React Native sources, this class delegates calls to Hermes C-based API. +// We use different namespace for this class comparing with the RN code. + +#include "HermesRuntimeTargetDelegate.h" +#include +#include +#include "HermesRuntimeAgentDelegate.h" + +using namespace facebook::react::jsinspector_modern; + +namespace Microsoft::ReactNative { + +namespace { + +const uint16_t HERMES_SAMPLING_FREQUENCY_HZ = 10000; + +class HermesStackTraceWrapper : public StackTrace { + public: + explicit HermesStackTraceWrapper(HermesUniqueStackTrace &&hermesStackTrace) + : hermesStackTrace_{std::move(hermesStackTrace)} {} + + HermesUniqueStackTrace &operator*() { + return hermesStackTrace_; + } + + HermesUniqueStackTrace *operator->() { + return &hermesStackTrace_; + } + + private: + HermesUniqueStackTrace hermesStackTrace_; +}; + +} // namespace + +HermesRuntimeTargetDelegate::HermesRuntimeTargetDelegate(std::shared_ptr &&hermesRuntimeHolder) + : hermesRuntimeHolder_(std::move(hermesRuntimeHolder)), + hermesCdpDebugApi_(HermesInspectorApi::createCdpDebugApi(hermesRuntimeHolder_->getHermesRuntime())) {} + +HermesRuntimeTargetDelegate::~HermesRuntimeTargetDelegate() = default; + +hermes_cdp_debug_api HermesRuntimeTargetDelegate::getCdpDebugApi() { + return hermesCdpDebugApi_.get(); +} + +std::unique_ptr HermesRuntimeTargetDelegate::createAgentDelegate( + FrontendChannel frontendChannel, + SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const ExecutionContextDescription &executionContextDescription, + facebook::react::RuntimeExecutor runtimeExecutor) { + return std::unique_ptr(new HermesRuntimeAgentDelegate( + frontendChannel, + sessionState, + std::move(previouslyExportedState), + executionContextDescription, + hermesRuntimeHolder_->getHermesRuntime(), + *this, + std::move(runtimeExecutor))); +} + +void HermesRuntimeTargetDelegate::addConsoleMessage(facebook::jsi::Runtime &runtime, ConsoleMessage message) { + // Convert ConsoleAPIType to hermes_console_api_type + hermes_console_api_type type{}; + switch (message.type) { + case ConsoleAPIType::kLog: + type = hermes_console_api_type_log; + break; + case ConsoleAPIType::kDebug: + type = hermes_console_api_type_debug; + break; + case ConsoleAPIType::kInfo: + type = hermes_console_api_type_info; + break; + case ConsoleAPIType::kError: + type = hermes_console_api_type_error; + break; + case ConsoleAPIType::kWarning: + type = hermes_console_api_type_warning; + break; + case ConsoleAPIType::kDir: + type = hermes_console_api_type_dir; + break; + case ConsoleAPIType::kDirXML: + type = hermes_console_api_type_dir_xml; + break; + case ConsoleAPIType::kTable: + type = hermes_console_api_type_table; + break; + case ConsoleAPIType::kTrace: + type = hermes_console_api_type_trace; + break; + case ConsoleAPIType::kStartGroup: + type = hermes_console_api_type_start_group; + break; + case ConsoleAPIType::kStartGroupCollapsed: + type = hermes_console_api_type_start_group_collapsed; + break; + case ConsoleAPIType::kEndGroup: + type = hermes_console_api_type_end_group; + break; + case ConsoleAPIType::kClear: + type = hermes_console_api_type_clear; + break; + case ConsoleAPIType::kAssert: + type = hermes_console_api_type_assert; + break; + case ConsoleAPIType::kTimeEnd: + type = hermes_console_api_type_time_end; + break; + case ConsoleAPIType::kCount: + type = hermes_console_api_type_count; + break; + default: + throw std::logic_error{"Unknown console message type"}; + } + + // Create a jsi::Array from the vector of jsi::Values + facebook::jsi::Array argsArray(runtime, message.args.size()); + for (size_t i = 0; i < message.args.size(); ++i) { + argsArray.setValueAtIndex(runtime, i, std::move(message.args[i])); + } + + // Store array as a temporary global property + // Using a property name that's unlikely to collide with user code + const char *propName = "__rnw_cdp_console_args"; + runtime.global().setProperty(runtime, propName, argsArray); + + // Convert stack trace to HermesUniqueStackTrace if available + HermesUniqueStackTrace hermesStackTrace{}; + if (auto hermesStackTraceWrapper = dynamic_cast(message.stackTrace.get())) { + hermesStackTrace = std::move(**hermesStackTraceWrapper); + } + + // Call C API with property name instead of serialized args + // The property will be cleaned up by the Hermes side + HermesInspectorApi::addConsoleMessage( + hermesCdpDebugApi_.get(), message.timestamp, type, propName, hermesStackTrace.get()); +} + +bool HermesRuntimeTargetDelegate::supportsConsole() const { + return true; +} + +std::unique_ptr HermesRuntimeTargetDelegate::captureStackTrace( + facebook::jsi::Runtime & /*runtime*/, + size_t /*framesToSkip*/) { + return std::make_unique( + HermesInspectorApi::captureStackTrace(hermesRuntimeHolder_->getHermesRuntime())); +} + +void HermesRuntimeTargetDelegate::enableSamplingProfiler() { + HermesInspectorApi::enableSamplingProfiler(hermesRuntimeHolder_->getHermesRuntime()); +} + +void HermesRuntimeTargetDelegate::disableSamplingProfiler() { + HermesInspectorApi::disableSamplingProfiler(hermesRuntimeHolder_->getHermesRuntime()); +} + +namespace { + +// Helper class to hold state while reading the sampling profile +struct SamplingProfileReaderState { + std::vector samples; + std::vector frames; + uint64_t timestamp; + uint64_t threadId; + bool hasCurrentSample; +}; + +static void NAPI_CDECL onInfo(void *cb_data, size_t sample_count) { + SamplingProfileReaderState *readerState = reinterpret_cast(cb_data); + readerState->samples.reserve(sample_count); +} + +// Callback invoked for each sample +static void NAPI_CDECL onSample(void *cb_data, uint64_t timestamp, uint64_t threadId, size_t frame_count) { + SamplingProfileReaderState *readerState = reinterpret_cast(cb_data); + if (readerState->hasCurrentSample) { + // Save the previous sample + readerState->samples.emplace_back(readerState->timestamp, readerState->threadId, std::move(readerState->frames)); + } + std::vector frames; + frames.reserve(frame_count); + readerState->frames = std::move(frames); + readerState->timestamp = timestamp; + readerState->threadId = threadId; + readerState->hasCurrentSample = true; +} + +// Callback invoked for each frame within a sample +static void NAPI_CDECL onFrame( + void *cb_data, + hermes_call_stack_frame_kind kind, + uint32_t scriptId, + const char *functionName, + size_t functionNameSize, + const char *scriptUrl, + size_t scriptUrlSize, + uint32_t lineNumber, + uint32_t columnNumber) { + SamplingProfileReaderState *readerState = reinterpret_cast(cb_data); + + using Kind = tracing::RuntimeSamplingProfile::SampleCallStackFrame::Kind; + + Kind frameKind; + switch (kind) { + case hermes_call_stack_frame_kind_js_function: + frameKind = Kind::JSFunction; + break; + case hermes_call_stack_frame_kind_native_function: + frameKind = Kind::NativeFunction; + break; + case hermes_call_stack_frame_kind_host_function: + frameKind = Kind::HostFunction; + break; + case hermes_call_stack_frame_kind_gc: + frameKind = Kind::GarbageCollector; + break; + default: + return; // Unknown frame kind, skip + } + + std::string funcName(functionName, functionNameSize); + std::optional url = + scriptUrl ? std::optional{std::string(scriptUrl, scriptUrlSize)} : std::nullopt; + std::optional line = lineNumber > 0 ? std::optional{lineNumber} : std::nullopt; + std::optional column = columnNumber > 0 ? std::optional{columnNumber} : std::nullopt; + + readerState->frames.emplace_back(frameKind, scriptId, std::move(funcName), std::move(url), line, column); +} + +} // namespace + +tracing::RuntimeSamplingProfile HermesRuntimeTargetDelegate::collectSamplingProfile() { + // Create a readerState state to gather samples and frames + SamplingProfileReaderState readerState{}; + + // Collect the profile from Hermes + HermesUniqueSamplingProfile profile = HermesInspectorApi::collectSamplingProfile( + hermesRuntimeHolder_->getHermesRuntime(), &readerState, onInfo, onSample, onFrame); + + if (readerState.hasCurrentSample) { + // Save the last sample + readerState.samples.emplace_back(readerState.timestamp, readerState.threadId, std::move(readerState.frames)); + } + + // Return the complete profile with samples. Wrap the raw profile since it owns the strings. + return tracing::RuntimeSamplingProfile("Hermes", std::move(readerState.samples)); +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h new file mode 100644 index 00000000000..d27d9f279cc --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* + * 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. + */ + +// This file must match the code in React Native folder: +// ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.h +// Unlike the code in React Native sources, this class delegates calls to Hermes C-based API. +// We use different namespace for this class comparing with the RN code. + +#pragma once + +#include + +#include +#include + +#include +#include "HermesRuntimeHolder.h" + +namespace Microsoft::ReactNative { + +/** + * A RuntimeTargetDelegate that enables debugging a Hermes runtime over CDP. + */ +class HermesRuntimeTargetDelegate : public facebook::react::jsinspector_modern::RuntimeTargetDelegate { + public: + explicit HermesRuntimeTargetDelegate(std::shared_ptr &&hermesRuntimeHolder); + + ~HermesRuntimeTargetDelegate() override; + + // Our C-API specific helper method to be used internally instead of RN getCDPDebugAPI() private function. + hermes_cdp_debug_api getCdpDebugApi(); + + public: // RuntimeTargetDelegate implementation + std::unique_ptr createAgentDelegate( + facebook::react::jsinspector_modern::FrontendChannel frontendChannel, + facebook::react::jsinspector_modern::SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, + facebook::react::RuntimeExecutor runtimeExecutor) override; + + void addConsoleMessage(facebook::jsi::Runtime &runtime, facebook::react::jsinspector_modern::ConsoleMessage message) + override; + + bool supportsConsole() const override; + + std::unique_ptr captureStackTrace( + facebook::jsi::Runtime &runtime, + size_t framesToSkip) override; + + /** + * Start sampling profiler. + */ + void enableSamplingProfiler() override; + + /** + * Stop sampling profiler. + */ + void disableSamplingProfiler() override; + + /** + * Return recorded sampling profile for the previous sampling session. + */ + facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile collectSamplingProfile() override; + + private: + std::shared_ptr hermesRuntimeHolder_; + const HermesUniqueCdpDebugApi hermesCdpDebugApi_; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/HermesSamplingProfiler.cpp b/vnext/Shared/Hermes/HermesSamplingProfiler.cpp similarity index 100% rename from vnext/Shared/HermesSamplingProfiler.cpp rename to vnext/Shared/Hermes/HermesSamplingProfiler.cpp diff --git a/vnext/Shared/HermesSamplingProfiler.h b/vnext/Shared/Hermes/HermesSamplingProfiler.h similarity index 100% rename from vnext/Shared/HermesSamplingProfiler.h rename to vnext/Shared/Hermes/HermesSamplingProfiler.h diff --git a/vnext/Shared/HermesRuntimeHolder.cpp b/vnext/Shared/HermesRuntimeHolder.cpp index 772b4fb6ba1..8c0b45790f1 100644 --- a/vnext/Shared/HermesRuntimeHolder.cpp +++ b/vnext/Shared/HermesRuntimeHolder.cpp @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include "Hermes/HermesRuntimeTargetDelegate.h" #include "SafeLoadLibrary.h" #define CRASH_ON_ERROR(result) VerifyElseCrash(result == napi_ok); @@ -27,6 +29,12 @@ using namespace Microsoft::NodeApiJsi; namespace Microsoft::ReactNative { +/*static*/ const hermes_inspector_vtable *HermesInspectorApi::vtable = nullptr; + +void setHermesInspectorVTable(const hermes_inspector_vtable *vtable) { + HermesInspectorApi::vtable = vtable; +} + React::ReactPropertyId>> HermesRuntimeHolderProperty() noexcept { static React::ReactPropertyId>> propId{ @@ -36,9 +44,6 @@ HermesRuntimeHolderProperty() noexcept { namespace { -int32_t NAPI_CDECL addInspectorPage(const char *title, const char *vm, void *connectFunc) noexcept; -void NAPI_CDECL removeInspectorPage(int32_t pageId) noexcept; - class HermesFuncResolver : public IFuncResolver { public: HermesFuncResolver() : libHandle_(LoadLibraryAsPeerFirst(L"hermes.dll")) {} @@ -55,7 +60,9 @@ HermesApi &initHermesApi() noexcept { static HermesFuncResolver funcResolver; static HermesApi s_hermesApi(&funcResolver); HermesApi::setCurrent(&s_hermesApi); - CRASH_ON_ERROR(s_hermesApi.hermes_set_inspector(&addInspectorPage, &removeInspectorPage)); + const hermes_inspector_vtable *inspectorVTable{}; + s_hermesApi.hermes_get_inspector_vtable(&inspectorVTable); + setHermesInspectorVTable(inspectorVTable); return s_hermesApi; } @@ -233,65 +240,6 @@ class HermesScriptCache { std::shared_ptr scriptStore_; }; -class HermesLocalConnection : public facebook::react::jsinspector_modern::ILocalConnection { - public: - HermesLocalConnection( - std::unique_ptr remoteConnection, - void *connectFunc) noexcept { - CRASH_ON_ERROR(getHermesApi().hermes_create_local_connection( - connectFunc, - reinterpret_cast(remoteConnection.release()), - &OnRemoteConnectionSendMessage, - &OnRemoteConnectionDisconnect, - &OnRemoteConnectionDelete, - nullptr, - &localConnection_)); - } - - ~HermesLocalConnection() override { - CRASH_ON_ERROR(getHermesApi().hermes_delete_local_connection(localConnection_)); - } - - void sendMessage(std::string message) { - CRASH_ON_ERROR(getHermesApi().hermes_local_connection_send_message(localConnection_, message.c_str())); - } - - void disconnect() { - CRASH_ON_ERROR(getHermesApi().hermes_local_connection_disconnect(localConnection_)); - } - - private: - static void NAPI_CDECL OnRemoteConnectionSendMessage(hermes_remote_connection remoteConnection, const char *message) { - reinterpret_cast(remoteConnection)->onMessage(message); - } - - static void NAPI_CDECL OnRemoteConnectionDisconnect(hermes_remote_connection remoteConnection) { - reinterpret_cast(remoteConnection)->onDisconnect(); - } - - static void NAPI_CDECL OnRemoteConnectionDelete(void *remoteConnection, void * /*deleterData*/) { - delete reinterpret_cast(remoteConnection); - } - - private: - hermes_local_connection localConnection_{}; -}; - -int32_t NAPI_CDECL addInspectorPage(const char *title, const char *vm, void *connectFunc) noexcept { - return facebook::react::jsinspector_modern::getInspectorInstance().addPage( - title, - vm, - [connectFunc, hermesApi = HermesApi::current()]( - std::unique_ptr remoteConnection) { - HermesApi::Scope apiScope(hermesApi); - return std::make_unique(std::move(remoteConnection), connectFunc); - }); -} - -void NAPI_CDECL removeInspectorPage(int32_t pageId) noexcept { - facebook::react::jsinspector_modern::getInspectorInstance().removePage(pageId); -} - } // namespace //============================================================================== @@ -307,9 +255,9 @@ HermesRuntimeHolder::HermesRuntimeHolder( m_preparedScriptStore(std::move(preparedScriptStore)) {} HermesRuntimeHolder::~HermesRuntimeHolder() { - if (m_runtime) { - CRASH_ON_ERROR(getHermesApi().jsr_delete_runtime(m_runtime)); - } + // if (m_runtime) { + // CRASH_ON_ERROR(getHermesApi().jsr_delete_runtime(m_runtime)); + // } } void HermesRuntimeHolder::initRuntime() noexcept { @@ -323,9 +271,6 @@ void HermesRuntimeHolder::initRuntime() noexcept { CRASH_ON_ERROR(api.jsr_create_config(&config)); CRASH_ON_ERROR(api.hermes_config_enable_default_crash_handler(config, devSettings->enableDefaultCrashHandler)); CRASH_ON_ERROR(api.jsr_config_enable_inspector(config, devSettings->useDirectDebugger)); - CRASH_ON_ERROR(api.jsr_config_set_inspector_runtime_name(config, devSettings->debuggerRuntimeName.c_str())); - CRASH_ON_ERROR(api.jsr_config_set_inspector_port(config, devSettings->debuggerPort)); - CRASH_ON_ERROR(api.jsr_config_set_inspector_break_on_start(config, devSettings->debuggerBreakOnNextLine)); CRASH_ON_ERROR(api.jsr_config_set_explicit_microtasks( config, facebook::react::ReactNativeFeatureFlags::enableBridgelessArchitecture())); @@ -338,6 +283,7 @@ void HermesRuntimeHolder::initRuntime() noexcept { jsr_runtime runtime{}; CRASH_ON_ERROR(api.jsr_create_runtime(config, &runtime)); CRASH_ON_ERROR(api.jsr_delete_config(config)); + m_runtime = runtime; napi_env env{}; CRASH_ON_ERROR(api.jsr_runtime_get_node_api_env(runtime, &env)); @@ -364,12 +310,13 @@ std::shared_ptr HermesRuntimeHolder::getRuntime() noexce return m_jsiRuntime; } -void HermesRuntimeHolder::crashHandler(int fileDescriptor) noexcept { - CRASH_ON_ERROR(getHermesApi().hermes_dump_crash_data(m_runtime, fileDescriptor)); +std::shared_ptr +HermesRuntimeHolder::createRuntimeTargetDelegate() { + return std::make_shared(shared_from_this()); } -void HermesRuntimeHolder::teardown() noexcept { - // TODO: (@vmoroz) Implement +void HermesRuntimeHolder::crashHandler(int fileDescriptor) noexcept { + CRASH_ON_ERROR(getHermesApi().hermes_dump_crash_data(m_runtime, fileDescriptor)); } std::shared_ptr HermesRuntimeHolder::loadFrom( @@ -403,6 +350,10 @@ void HermesRuntimeHolder::removeFromProfiling() const noexcept { CRASH_ON_ERROR(getHermesApi().hermes_sampling_profiler_dump_to_file(fileName.c_str())); } +hermes_runtime HermesRuntimeHolder::getHermesRuntime() noexcept { + return reinterpret_cast(m_runtime); +} + //============================================================================== // HermesJSRuntime implementation //============================================================================== @@ -414,44 +365,11 @@ facebook::jsi::Runtime &HermesJSRuntime::getRuntime() noexcept { return *m_holder->getRuntime(); } -void HermesJSRuntime::addConsoleMessage( - facebook::jsi::Runtime &runtime, - facebook::react::jsinspector_modern::ConsoleMessage message) { - return; -} - -bool HermesJSRuntime::supportsConsole() const { - return false; -} - -std::unique_ptr HermesJSRuntime::captureStackTrace( - facebook::jsi::Runtime &runtime, - size_t framesToSkip) { - return std::make_unique(); -} - -void HermesJSRuntime::enableSamplingProfiler() { - return; // [Windows TODO: stubbed implementation #14700] -} - -void HermesJSRuntime::disableSamplingProfiler() { - return; // [Windows TODO: stubbed implementation #14700] -} - -facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile HermesJSRuntime::collectSamplingProfile() { - return facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile( - "stubbed_impl", {}); // [Windows TODO: stubbed implementation #14700] -} - -std::unique_ptr HermesJSRuntime::createAgentDelegate( - facebook::react::jsinspector_modern::FrontendChannel frontendChannel, - facebook::react::jsinspector_modern::SessionState &sessionState, - std::unique_ptr previouslyExportedState, - const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, - facebook::react::RuntimeExecutor runtimeExecutor) { - (void)frontendChannel; - (void)sessionState; - return nullptr; +facebook::react::jsinspector_modern::RuntimeTargetDelegate &HermesJSRuntime::getRuntimeTargetDelegate() { + if (!m_runtimeTargetDelegate) { + m_runtimeTargetDelegate = m_holder->createRuntimeTargetDelegate(); + } + return *m_runtimeTargetDelegate; } } // namespace Microsoft::ReactNative diff --git a/vnext/Shared/HermesRuntimeHolder.h b/vnext/Shared/HermesRuntimeHolder.h index 4404cedbd65..d30f4b8ac23 100644 --- a/vnext/Shared/HermesRuntimeHolder.h +++ b/vnext/Shared/HermesRuntimeHolder.h @@ -8,16 +8,219 @@ #include #include #include -#include +#include namespace Microsoft::ReactNative { -class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit { +template +class FunctorAdapter { + static_assert(sizeof(TLambda) == -1, "Unsupported signature"); +}; + +template +class FunctorAdapter { + public: + static TResult NAPI_CDECL Invoke(void *data, TArgs... args) { + return reinterpret_cast(data)->operator()(args...); + } +}; + +template +inline TFunctor AsFunctor(TLambda &&lambda) { + using TLambdaType = std::remove_reference_t; + using TAdapter = + FunctorAdapter::invoke)>>; + return TFunctor{ + static_cast(new TLambdaType(std::forward(lambda))), &TAdapter::Invoke, [](void *data) { + delete static_cast(data); + }}; +} + +template +class FunctorWrapperBase { + static_assert(sizeof(TInvoke) == -1, "Unsupported signature"); +}; + +template +class FunctorWrapperBase { + public: + FunctorWrapperBase(TFunctor functor) : functor_(functor) {} + + ~FunctorWrapperBase() { + if (functor_.release != nullptr) { + functor_.release(functor_.data); + } + } + + TResult operator()(TArgs... args) { + return functor_.invoke(functor_.data, args...); + } + + private: + TFunctor functor_; +}; + +template +class FunctorWrapper : public FunctorWrapperBase().invoke)> { + public: + using FunctorWrapperBase().invoke)>::FunctorWrapperBase; +}; + +class HermesCdpDebugApiDeleter { + public: + void operator()(hermes_cdp_debug_api cdp_debug_api); +}; + +class HermesCdpAgentDeleter { + public: + void operator()(hermes_cdp_agent cdp_agent); +}; + +class HermesCdpStateDeleter { + public: + void operator()(hermes_cdp_state cdp_state); +}; + +class HermesStackTraceDeleter { + public: + void operator()(hermes_stack_trace stack_trace); +}; + +class HermesSamplingProfileDeleter { + public: + void operator()(hermes_sampling_profile sampling_profile); +}; + +using HermesUniqueCdpDebugApi = std::unique_ptr; +using HermesUniqueCdpAgent = std::unique_ptr; +using HermesUniqueCdpState = std::unique_ptr; +using HermesUniqueStackTrace = std::unique_ptr; +using HermesUniqueSamplingProfile = std::unique_ptr; + +class HermesInspectorApi { + public: + HermesInspectorApi() = delete; + + static void checkStatus(hermes_status status) { + if (status != hermes_status_ok) { + throw std::runtime_error("Hermes API call failed"); + } + } + + static HermesUniqueCdpDebugApi createCdpDebugApi(hermes_runtime runtime) { + hermes_cdp_debug_api cdp_debug_api{}; + checkStatus(vtable->create_cdp_debug_api(runtime, &cdp_debug_api)); + return HermesUniqueCdpDebugApi{cdp_debug_api}; + } + + static void addConsoleMessage( + hermes_cdp_debug_api cdpDebugApi, + double timestamp, + hermes_console_api_type type, + const char *argsPropertyName, + hermes_stack_trace stackTrace) { + checkStatus(vtable->add_console_message(cdpDebugApi, timestamp, type, argsPropertyName, stackTrace)); + } + + static HermesUniqueCdpAgent createCdpAgent( + hermes_cdp_debug_api cdpDebugApi, + int32_t execitionContextId, + hermes_enqueue_runtime_task_functor enqueueRuntimeTaskCallback, + hermes_enqueue_frontend_message_functor enqueueFrontendMessageCallback, + hermes_cdp_state cdp_state) { + hermes_cdp_agent cdp_agent{}; + checkStatus(vtable->create_cdp_agent( + cdpDebugApi, + execitionContextId, + enqueueRuntimeTaskCallback, + enqueueFrontendMessageCallback, + cdp_state, + &cdp_agent)); + return HermesUniqueCdpAgent{cdp_agent}; + } + + static HermesUniqueCdpState getCdpState(hermes_cdp_agent cdp_agent) { + hermes_cdp_state cdp_state{}; + checkStatus(vtable->cdp_agent_get_state(cdp_agent, &cdp_state)); + return HermesUniqueCdpState{cdp_state}; + } + + static void handleCommand(hermes_cdp_agent cdpAgent, const char *jsonUtf8, size_t jsonSize) { + checkStatus(vtable->cdp_agent_handle_command(cdpAgent, jsonUtf8, jsonSize)); + } + + static void enableRuntimeDomain(hermes_cdp_agent cdpAgent) { + checkStatus(vtable->cdp_agent_enable_runtime_domain(cdpAgent)); + } + + static void enableDebuggerDomain(hermes_cdp_agent cdpAgent) { + checkStatus(vtable->cdp_agent_enable_debugger_domain(cdpAgent)); + } + + static HermesUniqueStackTrace captureStackTrace(hermes_runtime runtime) { + hermes_stack_trace stack_trace{}; + checkStatus(vtable->capture_stack_trace(runtime, &stack_trace)); + return HermesUniqueStackTrace{stack_trace}; + } + + static void enableSamplingProfiler(hermes_runtime runtime) { + checkStatus(vtable->enable_sampling_profiler(runtime)); + } + + static void disableSamplingProfiler(hermes_runtime runtime) { + checkStatus(vtable->disable_sampling_profiler(runtime)); + } + + static HermesUniqueSamplingProfile collectSamplingProfile( + hermes_runtime runtime, + void *cb_data, + hermes_on_sampling_profile_info_callback on_info_callback, + hermes_on_sampling_profile_sample_callback on_sample_callback, + hermes_on_sampling_profile_frame_callback on_frame_callback) { + hermes_sampling_profile profile{}; + checkStatus(vtable->collect_sampling_profile( + runtime, cb_data, on_info_callback, on_sample_callback, on_frame_callback, &profile)); + return HermesUniqueSamplingProfile{profile}; + } + + private: + friend HermesCdpDebugApiDeleter; + friend HermesCdpAgentDeleter; + friend HermesCdpStateDeleter; + friend HermesStackTraceDeleter; + friend HermesSamplingProfileDeleter; + + friend void setHermesInspectorVTable(const hermes_inspector_vtable *vtable); + + static const hermes_inspector_vtable *vtable; +}; + +inline void HermesCdpDebugApiDeleter::operator()(hermes_cdp_debug_api cdp_debug_api) { + HermesInspectorApi::vtable->release_cdp_debug_api(cdp_debug_api); +} + +inline void HermesCdpAgentDeleter::operator()(hermes_cdp_agent cdp_agent) { + HermesInspectorApi::vtable->release_cdp_agent(cdp_agent); +} + +inline void HermesCdpStateDeleter::operator()(hermes_cdp_state cdp_state) { + HermesInspectorApi::vtable->release_cdp_state(cdp_state); +} + +inline void HermesStackTraceDeleter::operator()(hermes_stack_trace stack_trace) { + HermesInspectorApi::vtable->release_stack_trace(stack_trace); +} + +inline void HermesSamplingProfileDeleter::operator()(hermes_sampling_profile sampling_profile) { + HermesInspectorApi::vtable->release_sampling_profile(sampling_profile); +} + +class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit, + public std::enable_shared_from_this { public: // RuntimeHolderLazyInit implementation. std::shared_ptr getRuntime() noexcept override; facebook::react::JSIEngineOverride getRuntimeType() noexcept override; void crashHandler(int fileDescriptor) noexcept override; - void teardown() noexcept override; public: HermesRuntimeHolder( @@ -26,6 +229,8 @@ class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit { std::shared_ptr preparedScriptStore) noexcept; ~HermesRuntimeHolder(); + std::shared_ptr createRuntimeTargetDelegate() override; + static std::shared_ptr loadFrom( winrt::Microsoft::ReactNative::ReactPropertyBag const &propertyBag) noexcept; @@ -40,6 +245,8 @@ class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit { static void disableSamplingProfiler() noexcept; static void dumpSampledTraceToFile(const std::string &fileName) noexcept; + hermes_runtime getHermesRuntime() noexcept; + private: void initRuntime() noexcept; @@ -53,42 +260,17 @@ class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit { std::shared_ptr m_preparedScriptStore; }; -class HermesJSRuntime : public facebook::react::JSRuntime { +class HermesJSRuntime final : public facebook::react::JSRuntime { public: HermesJSRuntime(std::shared_ptr hermesRuntimeHolder); facebook::jsi::Runtime &getRuntime() noexcept override; - void addConsoleMessage(facebook::jsi::Runtime &runtime, facebook::react::jsinspector_modern::ConsoleMessage message) - override; - bool supportsConsole() const override; - std::unique_ptr captureStackTrace( - facebook::jsi::Runtime &runtime, - size_t framesToSkip = 0) override; - - /** - * Start sampling profiler. - */ - void enableSamplingProfiler() override; - - /** - * Stop sampling profiler. - */ - void disableSamplingProfiler() override; - - /** - * Return recorded sampling profile for the previous sampling session. - */ - facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile collectSamplingProfile() override; - - std::unique_ptr createAgentDelegate( - facebook::react::jsinspector_modern::FrontendChannel frontendChannel, - facebook::react::jsinspector_modern::SessionState &sessionState, - std::unique_ptr previouslyExportedState, - const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, - facebook::react::RuntimeExecutor runtimeExecutor) override; + + facebook::react::jsinspector_modern::RuntimeTargetDelegate &getRuntimeTargetDelegate() override; private: std::shared_ptr m_holder; + std::shared_ptr m_runtimeTargetDelegate; }; } // namespace Microsoft::ReactNative diff --git a/vnext/Shared/IDevSupportManager.h b/vnext/Shared/IDevSupportManager.h index dd90ff1bfc7..03c60ced0f5 100644 --- a/vnext/Shared/IDevSupportManager.h +++ b/vnext/Shared/IDevSupportManager.h @@ -23,9 +23,12 @@ struct IDevSupportManager { const uint16_t sourceBundlePort, std::function onChangeCallback) = 0; virtual void StopPollingLiveReload() = 0; + virtual void OpenDevTools(const std::string &bundleAppId) = 0; - virtual void EnsureHermesInspector(const std::string &packagerHost, const uint16_t packagerPort) noexcept = 0; - virtual void UpdateBundleStatus(bool isLastDownloadSuccess, int64_t updateTimestamp) noexcept = 0; + virtual void EnsureInspectorPackagerConnection( + const std::string &packagerHost, + const uint16_t packagerPort, + const std::string &bundleAppId) noexcept = 0; }; std::shared_ptr CreateDevSupportManager(); diff --git a/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp new file mode 100644 index 00000000000..65b7d6ddd0a --- /dev/null +++ b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "ReactInspectorPackagerConnectionDelegate.h" + +#include +#include +#include +#include "ReactInspectorThread.h" + +namespace Microsoft::ReactNative { + +namespace { + +class ReactInspectorWebSocket : public facebook::react::jsinspector_modern::IWebSocket { + public: + ReactInspectorWebSocket( + std::string const &url, + std::weak_ptr delegate); + void send(std::string_view message) override; + ~ReactInspectorWebSocket() override; + + private: + std::shared_ptr m_packagerWebSocketConnection; + std::weak_ptr m_weakDelegate; + std::atomic m_didConnect{false}; +}; + +ReactInspectorWebSocket::ReactInspectorWebSocket( + std::string const &url, + std::weak_ptr delegate) + : m_weakDelegate{delegate} { + std::vector certExceptions; + + m_packagerWebSocketConnection = + std::make_shared(std::move(certExceptions)); + + m_packagerWebSocketConnection->SetOnConnect([this, delegate]() { + ReactInspectorThread::Instance().InvokeElsePost([this, delegate]() { + m_didConnect = true; + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didOpen(); + } + }); + }); + m_packagerWebSocketConnection->SetOnMessage([delegate](auto &&, const std::string &message, bool isBinary) { + ReactInspectorThread::Instance().InvokeElsePost([delegate, message]() { + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didReceiveMessage(message); + } + }); + }); + m_packagerWebSocketConnection->SetOnError( + [delegate](const Microsoft::React::Networking::IWebSocketResource::Error &error) { + ReactInspectorThread::Instance().InvokeElsePost([delegate, error]() { + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didFailWithError(std::nullopt, error.Message); + } + }); + }); + m_packagerWebSocketConnection->SetOnClose([this, delegate](auto &&...) { + ReactInspectorThread::Instance().InvokeElsePost([this, delegate]() { + // Only call didClose() if we successfully connected first + // This prevents didClose() from being called during failed connection attempts + if (m_didConnect) { + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didClose(); + } + } + }); + }); + + Microsoft::React::Networking::IWebSocketResource::Protocols protocols; + Microsoft::React::Networking::IWebSocketResource::Options options; + m_packagerWebSocketConnection->Connect(std::string{url}, protocols, options); +} + +void ReactInspectorWebSocket::send(std::string_view message) { + m_packagerWebSocketConnection->Send(std::string{message}); +} + +ReactInspectorWebSocket::~ReactInspectorWebSocket() { + // Don't close WebSocket during shutdown - the OS will clean it up + // Attempting to close async during DLL unload causes thread pool failures + // The connection will be terminated when the process exits anyway +} + +} // namespace + +std::unique_ptr +ReactInspectorPackagerConnectionDelegate::connectWebSocket( + const std::string &url, + std::weak_ptr delegate) { + return std::make_unique(url, delegate); +} + +winrt::fire_and_forget RunWithDelayAsync(std::function callback, std::chrono::milliseconds delayMs) { + co_await winrt::resume_after(delayMs); + ReactInspectorThread::Instance().InvokeElsePost([callback]() { callback(); }); +} + +void ReactInspectorPackagerConnectionDelegate::scheduleCallback( + std::function callback, + std::chrono::milliseconds delayMs) { + RunWithDelayAsync(callback, delayMs); +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h new file mode 100644 index 00000000000..b7c0e16d9d5 --- /dev/null +++ b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once +#include + +namespace Microsoft::ReactNative { + +class ReactInspectorPackagerConnectionDelegate final + : public facebook::react::jsinspector_modern::InspectorPackagerConnectionDelegate { + public: // InspectorPackagerConnectionDelegate implementation + std::unique_ptr connectWebSocket( + const std::string &url, + std::weak_ptr delegate) override; + + void scheduleCallback(std::function callback, std::chrono::milliseconds delayMs) override; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Inspector/ReactInspectorThread.h b/vnext/Shared/Inspector/ReactInspectorThread.h new file mode 100644 index 00000000000..67eec4082de --- /dev/null +++ b/vnext/Shared/Inspector/ReactInspectorThread.h @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +namespace Microsoft::ReactNative { + +class ReactInspectorThread { + public: + static Mso::DispatchQueue &Instance() { + static Mso::DispatchQueue queue = Mso::DispatchQueue::MakeSerialQueue(); + return queue; + } +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/InspectorPackagerConnection.cpp b/vnext/Shared/InspectorPackagerConnection.cpp deleted file mode 100644 index 917382a5f3a..00000000000 --- a/vnext/Shared/InspectorPackagerConnection.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" - -#include -#include -#include "InspectorPackagerConnection.h" - -namespace Microsoft::ReactNative { - -namespace { - -struct InspectorProtocol { - static constexpr std::string_view Message_PAGEID = "pageId"; - static constexpr std::string_view Message_PAYLOAD = "payload"; - - static constexpr std::string_view Message_eventName_wrappedEvent = "wrappedEvent"; - static constexpr std::string_view Message_eventName_getPages = "getPages"; - static constexpr std::string_view Message_eventName_connect = "connect"; - static constexpr std::string_view Message_eventName_disconnect = "disconnect"; - - static constexpr std::string_view Message_EVENT = "event"; - - enum class EventType { GetPages, WrappedEvent, Connect, Disconnect }; - - static EventType getEventType(const folly::dynamic &messageFromPackager) { - std::string event = messageFromPackager.at(InspectorProtocol::Message_EVENT).getString(); - if (event == Message_eventName_getPages) { - return EventType::GetPages; - } - - if (event == Message_eventName_wrappedEvent) { - return EventType::WrappedEvent; - } - - if (event == Message_eventName_connect) { - return EventType::Connect; - } - - if (event == Message_eventName_disconnect) { - return EventType::Disconnect; - } - - assert(false && "Unknown event!"); - std::abort(); - } - - static folly::dynamic constructResponseForPackager(EventType eventType, folly::dynamic &&payload) { - folly::dynamic response = folly::dynamic::object; - - switch (eventType) { - case EventType::GetPages: - response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_getPages; - break; - case EventType::WrappedEvent: - response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_wrappedEvent; - break; - case EventType::Connect: - response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_connect; - break; - case EventType::Disconnect: - response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_disconnect; - break; - default: - assert(false && "Unknown event Type."); - std::abort(); - } - - response[InspectorProtocol::Message_PAYLOAD] = std::move(payload); - return response; - } - - static folly::dynamic constructGetPagesResponsePayloadForPackager( - const std::vector &pages, - InspectorPackagerConnection::BundleStatus bundleStatus) { - folly::dynamic payload = folly::dynamic::array; - for (const facebook::react::jsinspector_modern::InspectorPage &page : pages) { - folly::dynamic pageDyn = folly::dynamic::object; - pageDyn["id"] = page.id; - pageDyn["title"] = page.description; - pageDyn["vm"] = page.vm; - - pageDyn["isLastBundleDownloadSuccess"] = bundleStatus.m_isLastDownloadSuccess; - pageDyn["bundleUpdateTimestamp"] = bundleStatus.m_updateTimestamp; - - payload.push_back(pageDyn); - } - return payload; - } - - static folly::dynamic constructVMResponsePayloadForPackager(int32_t pageId, std::string &&messageFromVM) { - folly::dynamic payload = folly::dynamic::object; - payload[InspectorProtocol::Message_eventName_wrappedEvent] = messageFromVM; - payload[InspectorProtocol::Message_PAGEID] = pageId; - return payload; - } - - static folly::dynamic constructVMResponsePayloadOnDisconnectForPackager(int32_t pageId) { - folly::dynamic payload = folly::dynamic::object; - payload[InspectorProtocol::Message_PAGEID] = pageId; - return payload; - } -}; - -} // namespace - -RemoteConnection::RemoteConnection(int32_t pageId, const InspectorPackagerConnection &packagerConnection) - : m_packagerConnection(packagerConnection), m_pageId(pageId) {} - -void RemoteConnection::onMessage(std::string message) { - folly::dynamic response = InspectorProtocol::constructResponseForPackager( - InspectorProtocol::EventType::WrappedEvent, - InspectorProtocol::constructVMResponsePayloadForPackager(m_pageId, std::move(message))); - std::string responsestr = folly::toJson(response); - m_packagerConnection.sendMessageToPackager(std::move(responsestr)); -} - -void RemoteConnection::onDisconnect() { - folly::dynamic response = InspectorProtocol::constructResponseForPackager( - InspectorProtocol::EventType::Disconnect, - InspectorProtocol::constructVMResponsePayloadOnDisconnectForPackager(m_pageId)); - - std::string responsestr = folly::toJson(response); - m_packagerConnection.sendMessageToPackager(std::move(responsestr)); -} - -winrt::fire_and_forget InspectorPackagerConnection::sendMessageToPackagerAsync(std::string &&message) const { - std::string message_(std::move(message)); - co_await winrt::resume_background(); - m_packagerWebSocketConnection->Send(std::move(message_)); - co_return; -} - -void InspectorPackagerConnection::sendMessageToPackager(std::string &&message) const { - sendMessageToPackagerAsync(std::move(message)); -} - -void InspectorPackagerConnection::sendMessageToVM(int32_t pageId, std::string &&message) { - m_localConnections[pageId]->sendMessage(std::move(message)); -} - -InspectorPackagerConnection::InspectorPackagerConnection( - std::string &&url, - std::shared_ptr bundleStatusProvider) - : m_url(std::move(url)), m_bundleStatusProvider(std::move(bundleStatusProvider)) {} - -winrt::fire_and_forget InspectorPackagerConnection::disconnectAsync() { - co_await winrt::resume_background(); - std::string reason("Explicit close"); - m_packagerWebSocketConnection->Close(Microsoft::React::Networking::IWebSocketResource::CloseCode::GoingAway, reason); - co_return; -} - -winrt::fire_and_forget InspectorPackagerConnection::connectAsync() { - co_await winrt::resume_background(); - - m_packagerWebSocketConnection = Microsoft::React::Networking::IWebSocketResource::Make(); - - m_packagerWebSocketConnection->SetOnError([](const Microsoft::React::Networking::IWebSocketResource::Error &err) { - facebook::react::tracing::error(err.Message.c_str()); - }); - - m_packagerWebSocketConnection->SetOnConnect( - []() { facebook::react::tracing::log("Inspector: Websocket connection succeeded."); }); - - m_packagerWebSocketConnection->SetOnMessage( - [self = shared_from_this()](size_t /*length*/, const std::string &message, bool isBinary) { - assert(!isBinary && "We don't expect any binary messages !"); - folly::dynamic messageDyn = folly::parseJson(message); - - InspectorProtocol::EventType eventType = InspectorProtocol::getEventType(messageDyn); - switch (eventType) { - case InspectorProtocol::EventType::GetPages: { - std::vector inspectorPages = - facebook::react::jsinspector_modern::getInspectorInstance().getPages(); - folly::dynamic response = InspectorProtocol::constructResponseForPackager( - InspectorProtocol::EventType::GetPages, - InspectorProtocol::constructGetPagesResponsePayloadForPackager( - inspectorPages, self->m_bundleStatusProvider->getBundleStatus())); - - std::string responsestr = folly::toJson(response); - self->sendMessageToPackager(std::move(responsestr)); - break; - } - - case InspectorProtocol::EventType::WrappedEvent: { - folly::dynamic payload = messageDyn[InspectorProtocol::Message_PAYLOAD]; - int32_t pageId = static_cast(payload[InspectorProtocol::Message_PAGEID].asInt()); - - if (self->m_localConnections.find(pageId) == self->m_localConnections.end()) { - break; - } - - std::string wrappedEvent = payload[InspectorProtocol::Message_eventName_wrappedEvent].getString(); - self->sendMessageToVM(pageId, std::move(wrappedEvent)); - break; - } - - case InspectorProtocol::EventType::Connect: { - folly::dynamic payload = messageDyn[InspectorProtocol::Message_PAYLOAD]; - int32_t pageId = static_cast(payload[InspectorProtocol::Message_PAGEID].asInt()); - - if (self->m_localConnections.find(pageId) != self->m_localConnections.end()) { - break; - } - - self->m_localConnections[pageId] = facebook::react::jsinspector_modern::getInspectorInstance().connect( - pageId, std::make_unique(pageId, *self)); - break; - } - - case InspectorProtocol::EventType::Disconnect: { - folly::dynamic payload = messageDyn[InspectorProtocol::Message_PAYLOAD]; - int32_t pageId = static_cast(payload[InspectorProtocol::Message_PAGEID].asInt()); - if (self->m_localConnections.find(pageId) != self->m_localConnections.end()) { - self->m_localConnections[pageId]->disconnect(); - self->m_localConnections.erase(pageId); - } - break; - } - } - }); - - Microsoft::React::Networking::IWebSocketResource::Protocols protocols; - Microsoft::React::Networking::IWebSocketResource::Options options; - m_packagerWebSocketConnection->Connect(std::string{m_url}, protocols, options); - - co_return; -} - -} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/InspectorPackagerConnection.h b/vnext/Shared/InspectorPackagerConnection.h deleted file mode 100644 index b40b4f51e61..00000000000 --- a/vnext/Shared/InspectorPackagerConnection.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include - -namespace Microsoft::ReactNative { - -class InspectorPackagerConnection final : public std::enable_shared_from_this { - public: - winrt::fire_and_forget connectAsync(); - winrt::fire_and_forget disconnectAsync(); - - class BundleStatus { - public: - bool m_isLastDownloadSuccess; - int64_t m_updateTimestamp = -1; - - BundleStatus(bool isLastDownloadSuccess, long updateTimestamp) - : m_isLastDownloadSuccess(isLastDownloadSuccess), m_updateTimestamp(updateTimestamp) {} - BundleStatus() : m_isLastDownloadSuccess(false), m_updateTimestamp(-1) {} - }; - - struct IBundleStatusProvider { - virtual BundleStatus getBundleStatus() = 0; - }; - - InspectorPackagerConnection(std::string &&url, std::shared_ptr bundleStatusProvider); - - private: - friend class RemoteConnection; - - winrt::fire_and_forget sendMessageToPackagerAsync(std::string &&message) const; - void sendMessageToPackager(std::string &&message) const; - - // Note:: VM side Inspector processes the messages asynchronously in a sequential executor with dedicated thread. - // Hence, we don't bother invoking the inspector asynchronously. - void sendMessageToVM(int32_t pageId, std::string &&message); - - private: - std::unordered_map> - m_localConnections; - std::shared_ptr m_packagerWebSocketConnection; - std::shared_ptr m_bundleStatusProvider; - std::string m_url; -}; - -class RemoteConnection final : public facebook::react::jsinspector_modern::IRemoteConnection { - public: - RemoteConnection(int32_t pageId, const InspectorPackagerConnection &packagerConnection); - void onMessage(std::string message) override; - void onDisconnect() override; - - private: - int32_t m_pageId; - const InspectorPackagerConnection &m_packagerConnection; -}; - -} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/JSI/RuntimeHolder.h b/vnext/Shared/JSI/RuntimeHolder.h index 69a95b5a1cd..67f1bf10d64 100644 --- a/vnext/Shared/JSI/RuntimeHolder.h +++ b/vnext/Shared/JSI/RuntimeHolder.h @@ -4,6 +4,7 @@ #include #include +#include namespace Microsoft::JSI { @@ -18,11 +19,13 @@ struct RuntimeHolderLazyInit { virtual std::shared_ptr getRuntime() noexcept = 0; virtual facebook::react::JSIEngineOverride getRuntimeType() noexcept = 0; - virtual void teardown() noexcept {}; - // You can call this when a crash happens to attempt recording additional data // The fileDescriptor supplied is a raw file stream an implementation might write JSON to. virtual void crashHandler(int fileDescriptor) noexcept {}; + + virtual std::shared_ptr createRuntimeTargetDelegate() { + return nullptr; + } }; } // namespace Microsoft::JSI diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 82e66f38f86..e8c4ea5ad26 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -45,7 +45,9 @@ #include #include #include +#include "Inspector/ReactInspectorThread.h" #include "PackagerConnection.h" +#include "Threading/MessageDispatchQueue.h" #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) #include @@ -117,7 +119,6 @@ void LoadRemoteUrlScript( hermesBytecodeVersion); if (!success) { - devManager->UpdateBundleStatus(false, -1); devSettings->errorCallback(jsBundleString); return; } @@ -125,7 +126,6 @@ void LoadRemoteUrlScript( int64_t currentTimeInMilliSeconds = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) .count(); - devManager->UpdateBundleStatus(true, currentTimeInMilliSeconds); auto bundleUrl = facebook::react::DevServerHelper::get_BundleUrl( devSettings->sourceBundleHost, @@ -210,11 +210,33 @@ std::unique_ptr JsBigStringFromPath( } // namespace Microsoft::ReactNative -namespace facebook { -namespace react { +namespace facebook::react { namespace { +// OJSIExecutor is needed to override getRuntimeTargetDelegate to support the modern JSI inspector. +class OJSIExecutor : public JSIExecutor { + public: + OJSIExecutor( + std::shared_ptr runtime, + std::shared_ptr delegate, + const JSIScopedTimeoutInvoker &timeoutInvoker, + RuntimeInstaller runtimeInstaller, + std::shared_ptr &&targetDelegate) noexcept + : JSIExecutor(std::move(runtime), std::move(delegate), timeoutInvoker, std::move(runtimeInstaller)), + targetDelegate_(std::move(targetDelegate)) {} + jsinspector_modern::RuntimeTargetDelegate &getRuntimeTargetDelegate() override { + if (!targetDelegate_) { + // Use the fallback implementation from JSIExecutor. + return JSIExecutor::getRuntimeTargetDelegate(); + } + return *targetDelegate_; + } + + private: + std::shared_ptr targetDelegate_; +}; + class OJSIExecutorFactory : public JSExecutorFactory { public: std::unique_ptr createJSExecutor( @@ -231,7 +253,7 @@ class OJSIExecutorFactory : public JSExecutorFactory { } bindNativeLogger(*runtimeHolder_->getRuntime(), logger); - return std::make_unique( + return std::make_unique( runtimeHolder_->getRuntime(), std::move(delegate), JSIExecutor::defaultTimeoutInvoker, @@ -239,7 +261,8 @@ class OJSIExecutorFactory : public JSExecutorFactory { #ifdef ENABLE_JS_SYSTRACE_TO_ETW facebook::react::tracing::initializeJSHooks(runtime, isProfiling); #endif - }); + }, + runtimeHolder_->createRuntimeTargetDelegate()); } OJSIExecutorFactory( @@ -328,20 +351,6 @@ void InstanceImpl::SetInError() noexcept { m_isInError = true; } -namespace { -bool shouldStartHermesInspector(DevSettings &devSettings) { - bool isHermes = - ((devSettings.jsiEngineOverride == JSIEngineOverride::Hermes) || - (devSettings.jsiEngineOverride == JSIEngineOverride::Default && devSettings.jsiRuntimeHolder && - devSettings.jsiRuntimeHolder->getRuntimeType() == facebook::react::JSIEngineOverride::Hermes)); - - if (isHermes && devSettings.useDirectDebugger && !devSettings.useWebDebugger) - return true; - else - return false; -} -} // namespace - InstanceImpl::InstanceImpl( std::shared_ptr &&instance, std::string &&jsBundleBasePath, @@ -372,8 +381,9 @@ InstanceImpl::InstanceImpl( facebook::react::tracing::initializeETW(); #endif - if (shouldStartHermesInspector(*m_devSettings)) { - m_devManager->EnsureHermesInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort); + if (m_devSettings->useDirectDebugger) { + m_devManager->EnsureInspectorPackagerConnection( + m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort, m_devSettings->bundleAppId); } // Default (common) NativeModules @@ -483,7 +493,8 @@ InstanceImpl::InstanceImpl( } } - m_innerInstance->initializeBridge(std::move(callback), jsef, m_jsThread, m_moduleRegistry); + m_innerInstance->initializeBridge( + std::move(callback), jsef, m_jsThread, m_moduleRegistry, m_devSettings->inspectorHostTarget); // For RuntimeScheduler to work properly, we need to install TurboModuleManager with RuntimeSchedulerCallbackInvoker. // To be able to do that, we need to be able to call m_innerInstance->getRuntimeExecutor(), which we can only do after @@ -586,9 +597,16 @@ void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool s } InstanceImpl::~InstanceImpl() { - if (shouldStartHermesInspector(*m_devSettings) && m_devSettings->jsiRuntimeHolder) { - m_devSettings->jsiRuntimeHolder->teardown(); + if (m_devSettings->inspectorHostTarget) { + Mso::React::MessageDispatchQueue messageDispatchQueue{ + ::Microsoft::ReactNative::ReactInspectorThread::Instance(), nullptr}; + messageDispatchQueue.runOnQueueSync([weakInnerInstance = std::weak_ptr(m_innerInstance)]() { + if (std::shared_ptr innerInstance = weakInnerInstance.lock()) { + innerInstance->unregisterFromInspector(); + } + }); } + m_nativeQueue->quitSynchronous(); } @@ -722,5 +740,4 @@ void InstanceImpl::invokeCallback(const int64_t callbackId, folly::dynamic &&par m_innerInstance->callJSCallback(callbackId, std::move(params)); } -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 0bb372c02b3..6ad94f125ae 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -36,7 +36,7 @@ $(ReactNativeWindowsDir)Microsoft.ReactNative\Composition.Input.idl - true + true true @@ -76,6 +76,10 @@ true + + true + Code + true $(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeIsland.idl @@ -228,16 +232,16 @@ true - + true - + true $(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeAppBuilder.idl Code - + true $(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeAppBuilder.idl Code @@ -251,8 +255,10 @@ - - + + + + @@ -261,7 +267,7 @@ true - + @@ -342,10 +348,10 @@ $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\JsiApi.idl - + - - true + + true $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\ReactNativeAppBuilder.idl Code @@ -408,13 +414,18 @@ $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Timer.idl - + + - + + + + + @@ -460,7 +471,6 @@ - @@ -597,8 +607,8 @@ - - + + @@ -715,7 +725,7 @@ - + @@ -731,4 +741,4 @@ NotUsing - + \ No newline at end of file diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 26156838da0..a7f5c6f3db0 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -94,9 +94,6 @@ Source Files\JSI - - Source Files - Source Files\Modules @@ -218,6 +215,9 @@ Source Files\Fabric\Composition + + Source Files\Fabric\Composition + Source Files\Fabric\Composition @@ -230,9 +230,6 @@ Source Files\Fabric - - Source Files\Fabric - Source Files\Fabric @@ -265,9 +262,6 @@ Hermes - - Hermes - @@ -343,6 +337,18 @@ + + Hermes + + + Hermes + + + Hermes + + + Inspector + @@ -477,6 +483,9 @@ {b32590e6-ae3d-4388-ab98-767345ce38c9} + + {680511e1-15e0-48c9-a2a3-64addaeacce5} + @@ -630,9 +639,6 @@ Header Files\JSI - - Header Files - Header Files\tracing @@ -820,9 +826,6 @@ Hermes - - Hermes - @@ -831,6 +834,21 @@ + + Hermes + + + Hermes + + + Hermes + + + Inspector + + + Inspector + diff --git a/vnext/overrides.json b/vnext/overrides.json index c033644c26e..27cef4f5f23 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -40,27 +40,6 @@ "baseFile": "packages/react-native/ReactCommon/react/renderer/imagemanager/ImageRequest.cpp", "baseHash": "8ff58257e2af054b3c8367cad566abcaf36d7cba" }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.cpp", - "baseFile": "packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp", - "baseHash": "f6420ce2d56117dc241a9a9dd08b6184fb6b1447", - "issue": 13172 - }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSExecutor.h", - "baseFile": "packages/react-native/ReactCommon/cxxreact/JSExecutor.h", - "baseHash": "459d79bc399760546afaf3209c7ec96caeebc449", - "issue": 13172 - }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/NativeToJsBridge.cpp", - "baseFile": "packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp", - "baseHash": "e1317c960b04add1b13100df32bc73a9710dbacb", - "issue": 13172 - }, { "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/test/testlib.cpp", @@ -89,20 +68,6 @@ "baseHash": "d961f37983356cbc64622db6194cd4b2a9a4e05c", "issue": 13587 }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp", - "baseFile": "packages/react-native/ReactCommon/jsitooling/react/runtime/JSRuntimeFactory.cpp", - "baseHash": "c77f0b0829b850f8e2f115bfc3440ce94feb207d", - "issue": 13172 - }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h", - "baseFile": "packages/react-native/ReactCommon/jsitooling/react/runtime/JSRuntimeFactory.h", - "baseHash": "3e9e300ac2c01ed2b3bfa7e5afe7b5961d3e9080", - "issue": 13172 - }, { "type": "patch", "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/bridging/Bridging.h", @@ -178,13 +143,6 @@ "baseHash": "e03ce8e5db0216f23389ff8b3e9ea450c8a031b7", "issue": 14673 }, - { - "type": "patch", - "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp", - "baseFile": "packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp", - "baseHash": "651098ef1888fbabab0a70d8e542f46c35d09d7a", - "issue": 13172 - }, { "type": "copy", "directory": "ReactCopies/IntegrationTests", From d9789d0b5d24ff32d9591f5b9d47f778c9f62ee0 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Wed, 19 Nov 2025 14:13:19 -0800 Subject: [PATCH 2/3] Format code --- vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp index baf34e56eb6..49d5c62b3f8 100644 --- a/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp +++ b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp @@ -234,8 +234,7 @@ static void NAPI_CDECL onFrame( } std::string funcName(functionName, functionNameSize); - std::optional url = - scriptUrl ? std::optional{std::string(scriptUrl, scriptUrlSize)} : std::nullopt; + std::optional url = scriptUrl ? std::optional{std::string(scriptUrl, scriptUrlSize)} : std::nullopt; std::optional line = lineNumber > 0 ? std::optional{lineNumber} : std::nullopt; std::optional column = columnNumber > 0 ? std::optional{columnNumber} : std::nullopt; From 29d2c042d36b44b0b269f9b7698a1ca3d94ec9cd Mon Sep 17 00:00:00 2001 From: Abhijeet Jha <74712637+iamAbhi-916@users.noreply.github.com> Date: Thu, 27 Nov 2025 04:08:37 +0000 Subject: [PATCH 3/3] initialized pointer member -> inspectorHostTarget --- vnext/Microsoft.ReactNative/ReactHost/React.h | 2 +- vnext/Shared/DevSettings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vnext/Microsoft.ReactNative/ReactHost/React.h b/vnext/Microsoft.ReactNative/ReactHost/React.h index c7b8f8a15f1..e509ddcb2ba 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/React.h +++ b/vnext/Microsoft.ReactNative/ReactHost/React.h @@ -353,7 +353,7 @@ struct ReactOptions { OnReactInstanceDestroyedCallback OnInstanceDestroyed; //! The HostTarget instance for modern inspector integration. - facebook::react::jsinspector_modern::HostTarget *InspectorHostTarget; + facebook::react::jsinspector_modern::HostTarget *InspectorHostTarget{nullptr}; }; //! IReactHost manages a ReactNative instance. diff --git a/vnext/Shared/DevSettings.h b/vnext/Shared/DevSettings.h index 0591f9f404d..f0028d3353e 100644 --- a/vnext/Shared/DevSettings.h +++ b/vnext/Shared/DevSettings.h @@ -123,7 +123,7 @@ struct DevSettings { bool useTurboModulesOnly{false}; // The HostTarget instance for Fusebox - facebook::react::jsinspector_modern::HostTarget *inspectorHostTarget; + facebook::react::jsinspector_modern::HostTarget *inspectorHostTarget{nullptr}; }; } // namespace react