diff --git a/change/react-native-windows-2019-12-11-12-44-46-JSValue_Read_Write.json b/change/react-native-windows-2019-12-11-12-44-46-JSValue_Read_Write.json
new file mode 100644
index 00000000000..6da3069cc6f
--- /dev/null
+++ b/change/react-native-windows-2019-12-11-12-44-46-JSValue_Read_Write.json
@@ -0,0 +1,8 @@
+{
+ "type": "prerelease",
+ "comment": "Strongly typed value serialization and deserialization using IJSValueReader, JSValue, and IJSValueWriter",
+ "packageName": "react-native-windows",
+ "email": "vmorozov@microsoft.com",
+ "commit": "d7599a1fbbb88300ccd2b3e280b74fda9f41f061",
+ "date": "2019-12-11T20:44:46.143Z"
+}
\ No newline at end of file
diff --git a/packages/microsoft-reactnative-sampleapps/index.windows.js b/packages/microsoft-reactnative-sampleapps/index.windows.js
index 95cf4f33a0a..cafab488a58 100644
--- a/packages/microsoft-reactnative-sampleapps/index.windows.js
+++ b/packages/microsoft-reactnative-sampleapps/index.windows.js
@@ -38,6 +38,12 @@ var getCallback = function(prefix) {
};
};
+var getErrorCallback = function(prefix) {
+ return function(error) {
+ log(prefix + (error || {}).message);
+ };
+};
+
class SampleApp extends Component {
componentDidMount() {
this._TimedEventCSSub = SampleModuleCSEmitter.addListener('TimedEventCS', getCallback('SampleModuleCS.TimedEventCS() => '));
@@ -77,10 +83,10 @@ class SampleApp extends Component {
NativeModules.SampleModuleCS.ExplicitCallbackMethodWithArgs(numberArg, getCallback('SampleModuleCS.ExplicitCallbackMethodWithArgs => '));
var promise1 = NativeModules.SampleModuleCS.ExplicitPromiseMethod();
- promise1.then(getCallback('SampleModuleCS.ExplicitPromiseMethod then => ')).catch(getCallback('SampleModuleCS.ExplicitPromiseMethod catch => '));
+ promise1.then(getCallback('SampleModuleCS.ExplicitPromiseMethod then => ')).catch(getErrorCallback('SampleModuleCS.ExplicitPromiseMethod catch => '));
var promise2 = NativeModules.SampleModuleCS.ExplicitPromiseMethodWithArgs(numberArg);
- promise2.then(getCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs then => ')).catch(getCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs catch => '));
+ promise2.then(getCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs then => ')).catch(getErrorCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs catch => '));
log('SampleModuleCS.SyncReturnMethod => ' + NativeModules.SampleModuleCS.SyncReturnMethod());
@@ -115,10 +121,10 @@ class SampleApp extends Component {
NativeModules.SampleModuleCPP.ExplicitCallbackMethodWithArgs(numberArg, getCallback('SampleModuleCPP.ExplicitCallbackMethodWithArgs => '));
var promise1 = NativeModules.SampleModuleCPP.ExplicitPromiseMethod();
- promise1.then(getCallback('SampleModuleCPP.ExplicitPromiseMethod then => ')).catch(getCallback('SampleModuleCPP.ExplicitPromiseMethod catch => '));
+ promise1.then(getCallback('SampleModuleCPP.ExplicitPromiseMethod then => ')).catch(getErrorCallback('SampleModuleCPP.ExplicitPromiseMethod catch => '));
var promise2 = NativeModules.SampleModuleCPP.ExplicitPromiseMethodWithArgs(numberArg);
- promise2.then(getCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs then => ')).catch(getCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs catch => '));
+ promise2.then(getCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs then => ')).catch(getErrorCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs catch => '));
log('SampleModuleCPP.SyncReturnMethod => ' + NativeModules.SampleModuleCPP.SyncReturnMethod());
@@ -191,7 +197,6 @@ class SampleApp extends Component {
CircleCPP!
-
Hello from Microsoft!
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp
index 96a1bd527cf..e6dd0f47b3c 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp
@@ -6,7 +6,6 @@
#include "DebugConsole.h"
-using namespace Microsoft::ReactNative;
using namespace winrt::Microsoft::ReactNative::Bridge;
namespace winrt::SampleApp::implementation {
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters
index 87afcd43391..872971f9b86 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters
@@ -44,9 +44,6 @@
-
-
-
{e48dc53e-40b1-40cb-970a-f89935452892}
@@ -54,6 +51,7 @@
+
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln b/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln
index 36c9235cc8f..bdef09722ff 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln
@@ -46,14 +46,20 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\..\..\vnext\Co
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx.UnitTests", "..\..\..\vnext\Microsoft.ReactNative.Cxx.UnitTests\Microsoft.ReactNative.Cxx.UnitTests.vcxproj", "{6C60E295-C8CA-4DC5-B8BE-09888F58B249}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.ReactNative.Managed.UnitTests", "..\..\..\vnext\Microsoft.ReactNative.Managed.UnitTests\Microsoft.ReactNative.Managed.UnitTests.csproj", "{46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{09f4e6c1-2d12-4059-aa96-0b190861fd6a}*SharedItemsImports = 4
..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9
..\..\..\vnext\Chakra\Chakra.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4
..\..\..\vnext\Shared\Shared.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4
+ ..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{46d76f7a-8fd9-4a7d-8102-2857e5da6b84}*SharedItemsImports = 4
..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{47eec7f3-40d3-49ba-82c1-eaf103b54215}*SharedItemsImports = 4
..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13
+ ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{6c60e295-c8ca-4dc5-b8be-09888f58b249}*SharedItemsImports = 4
..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{93f7572c-64b9-4096-9ef9-6ba0ede2b50d}*SharedItemsImports = 4
..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4
..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{c0a6bd9c-3ee5-4b12-8ce4-cee95178539c}*SharedItemsImports = 4
@@ -62,9 +68,11 @@ Global
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
@@ -72,6 +80,9 @@ Global
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM.ActiveCfg = Debug|ARM
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM.Build.0 = Debug|ARM
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM.Deploy.0 = Debug|ARM
+ {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM64.Build.0 = Debug|ARM64
+ {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM64.Deploy.0 = Debug|ARM64
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|x64.ActiveCfg = Debug|x64
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|x64.Build.0 = Debug|x64
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|x64.Deploy.0 = Debug|x64
@@ -81,6 +92,9 @@ Global
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM.ActiveCfg = Release|ARM
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM.Build.0 = Release|ARM
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM.Deploy.0 = Release|ARM
+ {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM64.ActiveCfg = Release|ARM64
+ {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM64.Build.0 = Release|ARM64
+ {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM64.Deploy.0 = Release|ARM64
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|x64.ActiveCfg = Release|x64
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|x64.Build.0 = Release|x64
{09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|x64.Deploy.0 = Release|x64
@@ -90,6 +104,7 @@ Global
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM.ActiveCfg = Debug|ARM
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM.Build.0 = Debug|ARM
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM.Deploy.0 = Debug|ARM
+ {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM64.ActiveCfg = Debug|Win32
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|x64.ActiveCfg = Debug|x64
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|x64.Build.0 = Debug|x64
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|x64.Deploy.0 = Debug|x64
@@ -99,6 +114,7 @@ Global
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM.ActiveCfg = Release|ARM
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM.Build.0 = Release|ARM
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM.Deploy.0 = Release|ARM
+ {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM64.ActiveCfg = Release|Win32
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x64.ActiveCfg = Release|x64
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x64.Build.0 = Release|x64
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x64.Deploy.0 = Release|x64
@@ -107,112 +123,166 @@ Global
{93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x86.Deploy.0 = Release|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM
+ {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM
+ {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|ARM.ActiveCfg = Debug|ARM
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|ARM.Build.0 = Debug|ARM
+ {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|ARM64.ActiveCfg = Debug|Win32
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x64.ActiveCfg = Debug|x64
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x64.Build.0 = Debug|x64
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x86.ActiveCfg = Debug|Win32
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x86.Build.0 = Debug|Win32
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|ARM.ActiveCfg = Release|ARM
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|ARM.Build.0 = Release|ARM
+ {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|ARM64.ActiveCfg = Release|Win32
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x64.ActiveCfg = Release|x64
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x64.Build.0 = Release|x64
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x86.ActiveCfg = Release|Win32
{2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x86.Build.0 = Release|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM
+ {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM
+ {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM
+ {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|Win32
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM
+ {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|Win32
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM
+ {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM
+ {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|ARM.ActiveCfg = Debug|ARM
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|ARM.Build.0 = Debug|ARM
+ {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|ARM64.ActiveCfg = Debug|x86
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x64.ActiveCfg = Debug|x64
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x64.Build.0 = Debug|x64
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x86.ActiveCfg = Debug|x86
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x86.Build.0 = Debug|x86
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|ARM.ActiveCfg = Release|ARM
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|ARM.Build.0 = Release|ARM
+ {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|ARM64.ActiveCfg = Release|x86
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x64.ActiveCfg = Release|x64
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x64.Build.0 = Release|x64
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x86.ActiveCfg = Release|x86
{C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x86.Build.0 = Release|x86
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|ARM.ActiveCfg = Debug|ARM
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|ARM.Build.0 = Debug|ARM
+ {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|ARM64.ActiveCfg = Debug|Win32
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x64.ActiveCfg = Debug|x64
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x64.Build.0 = Debug|x64
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x86.ActiveCfg = Debug|Win32
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x86.Build.0 = Debug|Win32
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|ARM.ActiveCfg = Release|ARM
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|ARM.Build.0 = Release|ARM
+ {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|ARM64.ActiveCfg = Release|Win32
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x64.ActiveCfg = Release|x64
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x64.Build.0 = Release|x64
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x86.ActiveCfg = Release|Win32
{47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x86.Build.0 = Release|Win32
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM
+ {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|Win32
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM
+ {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|Win32
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM
+ {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM
+ {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|ARM.ActiveCfg = Debug|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|ARM64.ActiveCfg = Debug|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x64.ActiveCfg = Debug|x64
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x64.Build.0 = Debug|x64
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x86.ActiveCfg = Debug|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x86.Build.0 = Debug|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|ARM.ActiveCfg = Release|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|ARM64.ActiveCfg = Release|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x64.ActiveCfg = Release|x64
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x64.Build.0 = Release|x64
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x86.ActiveCfg = Release|Win32
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x86.Build.0 = Release|Win32
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM.ActiveCfg = Debug|ARM
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM.Build.0 = Debug|ARM
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM.Deploy.0 = Debug|ARM
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM64.Build.0 = Debug|ARM64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x64.ActiveCfg = Debug|x64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x64.Build.0 = Debug|x64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x64.Deploy.0 = Debug|x64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x86.ActiveCfg = Debug|x86
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x86.Build.0 = Debug|x86
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x86.Deploy.0 = Debug|x86
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM.ActiveCfg = Release|ARM
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM.Build.0 = Release|ARM
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM.Deploy.0 = Release|ARM
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM64.ActiveCfg = Release|ARM64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM64.Build.0 = Release|ARM64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM64.Deploy.0 = Release|ARM64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x64.ActiveCfg = Release|x64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x64.Build.0 = Release|x64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x64.Deploy.0 = Release|x64
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x86.ActiveCfg = Release|x86
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x86.Build.0 = Release|x86
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -229,6 +299,8 @@ Global
{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{67A1076F-7790-4203-86EA-4402CCB5E782} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
+ {6C60E295-C8CA-4DC5-B8BE-09888F58B249} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
+ {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AE882847-CAE9-450F-8962-E76672DC23CE}
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp
index 3c624107e1c..fec7cfa9523 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp
@@ -38,7 +38,7 @@ FrameworkElement CircleViewManagerCPP::CreateView() noexcept {
// IViewManagerWithChildren
-void CircleViewManagerCPP::AddView(FrameworkElement const &parent, UIElement const &child, int64_t index) noexcept {
+void CircleViewManagerCPP::AddView(FrameworkElement const &parent, UIElement const &child, int64_t /*index*/) noexcept {
if (auto const &border = parent.try_as()) {
border.Child(child);
}
@@ -50,7 +50,7 @@ void CircleViewManagerCPP::RemoveAllChildren(FrameworkElement const &parent) noe
}
}
-void CircleViewManagerCPP::RemoveChildAt(FrameworkElement const &parent, int64_t index) noexcept {
+void CircleViewManagerCPP::RemoveChildAt(FrameworkElement const &parent, int64_t /*index*/) noexcept {
if (auto const &border = parent.try_as()) {
border.Child(nullptr);
}
@@ -58,7 +58,7 @@ void CircleViewManagerCPP::RemoveChildAt(FrameworkElement const &parent, int64_t
void CircleViewManagerCPP::ReplaceChild(
FrameworkElement const &parent,
- UIElement const &oldChild,
+ UIElement const & /*oldChild*/,
UIElement const &newChild) noexcept {
if (auto const &border = parent.try_as()) {
border.Child(newChild);
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h
index 303ef925f05..12f2e12fbf6 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h
@@ -40,9 +40,9 @@ struct HeightToCornerRadiusConverter
winrt::Windows::Foundation::IInspectable Convert(
winrt::Windows::Foundation::IInspectable const &value,
- winrt::Windows::UI::Xaml::Interop::TypeName const &targetType,
- winrt::Windows::Foundation::IInspectable const ¶meter,
- winrt::hstring const &language) noexcept {
+ winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/,
+ winrt::Windows::Foundation::IInspectable const & /*parameter*/,
+ winrt::hstring const & /*language*/) noexcept {
double d = winrt::unbox_value(value);
if (isnan(d)) {
@@ -54,9 +54,9 @@ struct HeightToCornerRadiusConverter
winrt::Windows::Foundation::IInspectable ConvertBack(
winrt::Windows::Foundation::IInspectable const &value,
- winrt::Windows::UI::Xaml::Interop::TypeName const &targetType,
- winrt::Windows::Foundation::IInspectable const ¶meter,
- winrt::hstring const &language) noexcept {
+ winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/,
+ winrt::Windows::Foundation::IInspectable const & /*parameter*/,
+ winrt::hstring const & /*language*/) noexcept {
return value;
}
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp
index 2ee07288c1e..58a46bec3c9 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp
@@ -12,7 +12,6 @@
#include "SampleModuleCPP.h"
using namespace winrt::Microsoft::ReactNative::Bridge;
-using namespace Microsoft::ReactNative;
namespace winrt::SampleLibraryCPP::implementation {
@@ -21,8 +20,9 @@ void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuil
packageBuilder.AddViewManager(L"CustomUserControlViewManagerCPP", [](IReactContext const &reactContext) {
return winrt::make(reactContext);
});
- packageBuilder.AddViewManager(
- L"CircleViewManagerCPP", [](IReactContext const &reactContext) { return winrt::make(); });
+ packageBuilder.AddViewManager(L"CircleViewManagerCPP", [](IReactContext const & /*reactContext*/) {
+ return winrt::make();
+ });
}
} // namespace winrt::SampleLibraryCPP::implementation
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj
index 38901561b68..fa6314550c2 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj
@@ -1,6 +1,6 @@
-
+
true
true
@@ -158,13 +158,13 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
+
+
\ No newline at end of file
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h
index 141c6fa115a..c59bc041f49 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h
@@ -29,9 +29,9 @@ struct SampleModuleCPP {
const std::string StringConstant = "Hello World";
REACT_CONSTANT_PROVIDER(ConstantsViaConstantsProvider)
- void ConstantsViaConstantsProvider(const winrt::Microsoft::ReactNative::Bridge::IJSValueWriter &writer) noexcept {
- ::Microsoft::ReactNative::WriteProperty(writer, "NumberConstantViaProvider", M_PI);
- ::Microsoft::ReactNative::WriteProperty(writer, "StringConstantViaProvider", "Hello World");
+ void ConstantsViaConstantsProvider(ReactConstantProvider &constants) noexcept {
+ constants.Add(L"NumberConstantViaProvider", M_PI);
+ constants.Add(L"StringConstantViaProvider", "Hello World");
}
#pragma endregion
@@ -77,27 +77,24 @@ struct SampleModuleCPP {
}
REACT_METHOD(ExplicitPromiseMethod);
- void ExplicitPromiseMethod(
- std::function &&resolve,
- std::function &&reject) noexcept {
+ void ExplicitPromiseMethod(winrt::Microsoft::ReactNative::Bridge::ReactPromise &&result) noexcept {
DebugWriteLine(Name, "ExplicitPromiseMethod");
try {
- resolve(M_PI);
+ result.Resolve(M_PI);
} catch (const std::exception &ex) {
- reject(ex.what());
+ result.Reject(ex.what());
}
}
REACT_METHOD(ExplicitPromiseMethodWithArgs);
void ExplicitPromiseMethodWithArgs(
double arg,
- std::function &&resolve,
- std::function &&reject) noexcept {
+ winrt::Microsoft::ReactNative::Bridge::ReactPromise &&result) noexcept {
DebugWriteLine(Name, "ExplicitPromiseMethodWithArgs", arg);
try {
- resolve(M_PI);
+ result.Resolve(M_PI);
} catch (const std::exception &ex) {
- reject(ex.what());
+ result.Reject(ex.what());
}
}
@@ -121,7 +118,7 @@ struct SampleModuleCPP {
#pragma region Events
- REACT_EVENT(TimedEvent, "TimedEventCPP");
+ REACT_EVENT(TimedEvent, L"TimedEventCPP");
std::function TimedEvent;
#pragma endregion
diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs
index a12360c4238..b96d8fc7406 100644
--- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs
+++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs
@@ -1,15 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using Microsoft.ReactNative.Managed;
using System;
-using System.Collections.Generic;
using System.Diagnostics;
-
using Windows.System.Threading;
-using Microsoft.ReactNative.Bridge;
-using Microsoft.ReactNative.Managed;
-
namespace SampleLibraryCS
{
// Sample ReactModule
@@ -83,32 +79,32 @@ public void ExplicitCallbackMethodWithArgs(double arg, ReactCallback cal
}
[ReactMethod]
- public void ExplicitPromiseMethod(ReactCallback resolve, ReactCallback reject)
+ public void ExplicitPromiseMethod(IReactPromise result)
{
Debug.WriteLine($"{Name}.{nameof(ExplicitPromiseMethod)}()");
try
{
- resolve(Math.PI);
+ result.Resolve(Math.PI);
}
catch (Exception ex)
{
- reject(ex.Message);
+ result.Reject(new ReactError { Message = ex.Message });
}
}
[ReactMethod]
- public void ExplicitPromiseMethodWithArgs(double arg, ReactCallback resolve, ReactCallback reject)
+ public void ExplicitPromiseMethodWithArgs(double arg, IReactPromise result)
{
Debug.WriteLine($"{Name}.{nameof(ExplicitPromiseMethodWithArgs)}({arg})");
try
{
- resolve(Math.PI);
+ result.Resolve(Math.PI);
}
catch (Exception ex)
{
- reject(ex.Message);
+ result.Reject(new ReactError { Message = ex.Message });
}
}
diff --git a/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj b/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj
index 6aeb7404fc8..4a7bc4f3790 100644
--- a/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj
+++ b/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj
@@ -77,15 +77,15 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
+
+
\ No newline at end of file
diff --git a/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj b/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj
index a13b12a0645..60897835fe1 100644
--- a/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj
+++ b/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj
@@ -120,8 +120,8 @@
-
-
+
+
@@ -130,7 +130,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/vnext/JSI/Desktop/JSI.Desktop.vcxproj b/vnext/JSI/Desktop/JSI.Desktop.vcxproj
index 3cd2dac30fc..914c809bc3d 100644
--- a/vnext/JSI/Desktop/JSI.Desktop.vcxproj
+++ b/vnext/JSI/Desktop/JSI.Desktop.vcxproj
@@ -80,14 +80,14 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
+
+
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueReaderTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueReaderTest.cpp
new file mode 100644
index 00000000000..be9941c7d02
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueReaderTest.cpp
@@ -0,0 +1,466 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "pch.h"
+#include "JSValueReader.h"
+#include
+#include "JSValueWriter.h"
+#include "JsonJSValueReader.h"
+#include "catch.hpp"
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+enum struct RobotModel {
+ T2,
+ R2D2,
+ C3PO,
+};
+
+// We are generating ReadValue for it.
+enum struct RobotShape {
+ Humanoid,
+ Trashcan,
+ Beercan,
+ Quadrocopter,
+};
+
+struct RobotTool {
+ std::string Name;
+ int Weight;
+ bool IsEnabled;
+};
+
+// This is how we can provide "reflection" information about type outside of struct.
+FieldMap GetStructInfo(RobotTool *) {
+ return {{L"Name", &RobotTool::Name}, {L"Weight", &RobotTool::Weight}, {L"IsEnabled", &RobotTool::IsEnabled}};
+}
+
+struct RobotPoint {
+ int X;
+ int Y;
+};
+
+FieldMap GetStructInfo(RobotPoint *) {
+ return {{L"X", &RobotPoint::X}, {L"Y", &RobotPoint::Y}};
+}
+
+// We can provide "reflection" information about struct using REACT_STRUCT and REACT_FIELD macros.
+REACT_STRUCT(T2Extra)
+struct T2Extra {
+ REACT_FIELD(ActorName)
+ std::string ActorName;
+
+ REACT_FIELD(MovieYear)
+ int MovieYear;
+};
+
+REACT_STRUCT(R2D2Extra)
+struct R2D2Extra {
+ REACT_FIELD(MovieSeries)
+ std::string MovieSeries;
+};
+
+struct RobotInfo {
+ RobotModel Model;
+ std::string Name;
+ int Age;
+ RobotShape Shape;
+ std::optional Shape2;
+ std::optional Shape3;
+ std::vector Steps;
+ std::map Dimensions;
+ std::tuple Badges;
+ std::vector Tools;
+ std::vector Path;
+ std::variant Extra;
+};
+
+// Reading RobotModel enum value. We could use template-based version instead.
+void ReadValue(IJSValueReader const &reader, RobotModel &value) noexcept {
+ value = static_cast(ReadValue(reader));
+}
+
+// Writing RobotModel enum value. We could use template-based version instead.
+void WriteValue(IJSValueWriter const &writer, RobotModel value) noexcept {
+ WriteValue(writer, static_cast(value));
+}
+
+// Reading discriminating union requires using JSValue.
+void ReadValue(const JSValue &jsValue, std::variant &value) noexcept {
+ switch (ReadValue(jsValue["Kind"])) {
+ case RobotModel::T2:
+ value = ReadValue(jsValue);
+ break;
+ case RobotModel::R2D2:
+ value = ReadValue(jsValue);
+ break;
+ }
+}
+
+// Writing discriminating union.
+void WriteValue(IJSValueWriter const &writer, std::variant const &value) noexcept {
+ writer.WriteObjectBegin();
+ if (const T2Extra *t2 = std::get_if(&value)) {
+ WriteProperty(writer, L"Kind", RobotModel::T2);
+ WriteProperties(writer, *t2);
+ } else if (const R2D2Extra *r2d2 = std::get_if(&value)) {
+ WriteProperty(writer, L"Kind", RobotModel::R2D2);
+ WriteProperties(writer, *r2d2);
+ }
+ writer.WriteObjectEnd();
+}
+
+// Reading RobotInfo value. It could be generated instead.
+void ReadValue(IJSValueReader const &reader, RobotInfo &value) noexcept {
+ value = RobotInfo();
+ if (reader.ValueType() == JSValueType::Object) {
+ hstring propertyName;
+ while (reader.GetNextObjectProperty(/*out*/ propertyName)) {
+ if (propertyName == L"Model")
+ ReadValue(reader, value.Model);
+ else if (propertyName == L"Name")
+ ReadValue(reader, value.Name);
+ else if (propertyName == L"Age")
+ ReadValue(reader, value.Age);
+ else if (propertyName == L"Shape")
+ ReadValue(reader, value.Shape);
+ else if (propertyName == L"Shape2")
+ ReadValue(reader, value.Shape2);
+ else if (propertyName == L"Shape3")
+ ReadValue(reader, value.Shape3);
+ else if (propertyName == L"Steps")
+ ReadValue(reader, value.Steps);
+ else if (propertyName == L"Dimensions")
+ ReadValue(reader, value.Dimensions);
+ else if (propertyName == L"Badges")
+ ReadValue(reader, value.Badges);
+ else if (propertyName == L"Tools")
+ ReadValue(reader, value.Tools);
+ else if (propertyName == L"Path")
+ ReadValue(reader, value.Path);
+ else if (propertyName == L"Extra")
+ ReadValue(reader, value.Extra);
+ else
+ ReadValue(reader);
+ }
+ }
+}
+
+// Writing RobotInfo value. It could be generated instead.
+void WriteValue(IJSValueWriter const &writer, RobotInfo const &value) noexcept {
+ writer.WriteObjectBegin();
+ WriteProperty(writer, L"Model", value.Model);
+ WriteProperty(writer, L"Name", value.Name);
+ WriteProperty(writer, L"Age", value.Age);
+ WriteProperty(writer, L"Shape", value.Shape);
+ WriteProperty(writer, L"Shape2", value.Shape2);
+ WriteProperty(writer, L"Shape3", value.Shape3);
+ WriteProperty(writer, L"Steps", value.Steps);
+ WriteProperty(writer, L"Dimensions", value.Dimensions);
+ WriteProperty(writer, L"Badges", value.Badges);
+ WriteProperty(writer, L"Tools", value.Tools);
+ WriteProperty(writer, L"Path", value.Path);
+ WriteProperty(writer, L"Extra", value.Extra);
+ writer.WriteObjectEnd();
+}
+
+TEST_CASE("TestReadCustomType", "JSValueReaderTest") {
+ const wchar_t *json =
+ LR"JSON({
+ "Model": 1,
+ "Name": "Bob",
+ "Age": 42,
+ "Shape": 1,
+ "Shape2": 2,
+ "Shape3": null,
+ "Steps": [1, 2, 3],
+ "Dimensions": {"Width": 24, "Height": 78},
+ "Badges": [2, "Maverick", true],
+ "Tools": [
+ {"Name": "Screwdriver", "Weight": 2, "IsEnabled": true},
+ {"Name": "Electro-shocker", "Weight": 3, "IsEnabled": false}],
+ "Path": [{"X": 5, "Y": 6}, {"X": 45, "Y": 90}, {"X": 15, "Y": 16}],
+ "Extra": {"Kind": 1, "MovieSeries" : "Episode 2"}
+ })JSON";
+
+ IJSValueReader reader = make(json);
+
+ RobotInfo robot = ReadValue(reader);
+ REQUIRE(robot.Model == RobotModel::R2D2);
+ REQUIRE(robot.Name == "Bob");
+ REQUIRE(robot.Age == 42);
+ REQUIRE(robot.Shape == RobotShape::Trashcan);
+ REQUIRE(*robot.Shape2 == RobotShape::Beercan);
+ REQUIRE(!robot.Shape3.has_value());
+ REQUIRE(robot.Steps.size() == 3);
+ REQUIRE(robot.Steps[0] == 1);
+ REQUIRE(robot.Steps[1] == 2);
+ REQUIRE(robot.Steps[2] == 3);
+ REQUIRE(robot.Dimensions.size() == 2);
+ REQUIRE(robot.Dimensions["Width"] == 24);
+ REQUIRE(robot.Dimensions["Height"] == 78);
+ REQUIRE(std::get<0>(robot.Badges) == 2);
+ REQUIRE(std::get<1>(robot.Badges) == "Maverick");
+ REQUIRE(std::get<2>(robot.Badges) == true);
+ REQUIRE(robot.Tools.size() == 2);
+ REQUIRE(robot.Tools[0].Name == "Screwdriver");
+ REQUIRE(robot.Tools[0].Weight == 2);
+ REQUIRE(robot.Tools[0].IsEnabled == true);
+ REQUIRE(robot.Tools[1].Name == "Electro-shocker");
+ REQUIRE(robot.Tools[1].Weight == 3);
+ REQUIRE(robot.Tools[1].IsEnabled == false);
+ REQUIRE(robot.Path.size() == 3);
+ REQUIRE(robot.Path[0].X == 5);
+ REQUIRE(robot.Path[0].Y == 6);
+ REQUIRE(robot.Path[1].X == 45);
+ REQUIRE(robot.Path[1].Y == 90);
+ REQUIRE(robot.Path[2].X == 15);
+ REQUIRE(robot.Path[2].Y == 16);
+ const R2D2Extra *r2d2Extra = std::get_if(&robot.Extra);
+ REQUIRE(r2d2Extra != nullptr);
+ REQUIRE(r2d2Extra->MovieSeries == "Episode 2");
+}
+
+TEST_CASE("TestWriteCustomType", "JSValueReaderTest") {
+ RobotInfo robot{};
+ robot.Model = RobotModel::R2D2;
+ robot.Name = "Bob";
+ robot.Age = 42;
+ robot.Shape = RobotShape::Trashcan;
+ robot.Shape2 = RobotShape::Beercan;
+ robot.Shape3 = std::nullopt;
+ robot.Steps = std::vector{1, 2, 3};
+ robot.Dimensions = std::map{{"Width", 24}, {"Height", 78}};
+ robot.Badges = std::tuple{2, "Maverick", true};
+ robot.Tools = std::vector{RobotTool{/*Name =*/"Screwdriver", /*Weight =*/2, /*IsEnabled =*/true},
+ RobotTool{/*Name =*/"Electro-shocker", /*Weight =*/3, /*IsEnabled =*/false}};
+ robot.Path = std::vector{
+ RobotPoint{/*X =*/5, /*Y =*/6}, RobotPoint{/*X =*/45, /*Y =*/90}, RobotPoint{/*X =*/15, /*Y =*/16}};
+ robot.Extra = R2D2Extra{/*MovieSeries =*/"Episode 2"};
+
+ JSValue jsValue;
+ auto writer = MakeJSValueTreeWriter(jsValue);
+ WriteValue(writer, robot);
+
+ REQUIRE(jsValue["Model"] == (int)RobotModel::R2D2);
+ REQUIRE(jsValue["Name"] == "Bob");
+ REQUIRE(jsValue["Age"] == 42);
+ REQUIRE(jsValue["Shape"] == (int)RobotShape::Trashcan);
+ REQUIRE(jsValue["Shape2"] == (int)RobotShape::Beercan);
+ REQUIRE(jsValue["Shape3"] == nullptr);
+ REQUIRE(jsValue["Steps"].ItemCount() == 3);
+ REQUIRE(jsValue["Steps"][0] == 1);
+ REQUIRE(jsValue["Steps"][1] == 2);
+ REQUIRE(jsValue["Steps"][2] == 3);
+ REQUIRE(jsValue["Dimensions"].PropertyCount() == 2);
+ REQUIRE(jsValue["Dimensions"]["Width"] == 24);
+ REQUIRE(jsValue["Dimensions"]["Height"] == 78);
+ REQUIRE(jsValue["Badges"][0] == 2);
+ REQUIRE(jsValue["Badges"][1] == "Maverick");
+ REQUIRE(jsValue["Badges"][2] == true);
+ REQUIRE(jsValue["Tools"].ItemCount() == 2);
+ REQUIRE(jsValue["Tools"][0]["Name"] == "Screwdriver");
+ REQUIRE(jsValue["Tools"][0]["Weight"] == 2);
+ REQUIRE(jsValue["Tools"][0]["IsEnabled"] == true);
+ REQUIRE(jsValue["Tools"][1]["Name"] == "Electro-shocker");
+ REQUIRE(jsValue["Tools"][1]["Weight"] == 3);
+ REQUIRE(jsValue["Tools"][1]["IsEnabled"] == false);
+ REQUIRE(jsValue["Path"].ItemCount() == 3);
+ REQUIRE(jsValue["Path"][0]["X"] == 5);
+ REQUIRE(jsValue["Path"][0]["Y"] == 6);
+ REQUIRE(jsValue["Path"][1]["X"] == 45);
+ REQUIRE(jsValue["Path"][1]["Y"] == 90);
+ REQUIRE(jsValue["Path"][2]["X"] == 15);
+ REQUIRE(jsValue["Path"][2]["Y"] == 16);
+ REQUIRE(jsValue["Extra"]["MovieSeries"] == "Episode 2");
+}
+
+TEST_CASE("TestReadValueDefaultExtensions", "JSValueReaderTest") {
+ const wchar_t *json =
+ LR"JSON({
+ "StringValue1": "",
+ "StringValue2": "5",
+ "StringValue3": "Hello",
+ "BoolValue1": false,
+ "BoolValue2": true,
+ "IntValue1": 0,
+ "IntValue2": 42,
+ "FloatValue": 3.14,
+ "NullValue": null
+ })JSON";
+
+ IJSValueReader reader = make(json);
+
+ REQUIRE(reader.ValueType() == JSValueType::Object);
+ int properyCount = 0;
+ hstring propertyName;
+ while (reader.GetNextObjectProperty(/*out*/ propertyName)) {
+ if (propertyName == L"StringValue1") {
+ REQUIRE(ReadValue(reader) == "");
+ REQUIRE(ReadValue(reader) == L"");
+ REQUIRE(ReadValue(reader) == false);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ ++properyCount;
+ } else if (propertyName == L"StringValue2") {
+ REQUIRE(ReadValue(reader) == "5");
+ REQUIRE(ReadValue(reader) == L"5");
+ REQUIRE(ReadValue(reader) == true);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ REQUIRE(ReadValue(reader) == 5);
+ ++properyCount;
+ } else if (propertyName == L"StringValue3") {
+ REQUIRE(ReadValue(reader) == "Hello");
+ REQUIRE(ReadValue(reader) == L"Hello");
+ REQUIRE(ReadValue(reader) == true);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ ++properyCount;
+ } else if (propertyName == L"BoolValue1") {
+ REQUIRE(ReadValue(reader) == "false");
+ REQUIRE(ReadValue(reader) == L"false");
+ REQUIRE(ReadValue(reader) == false);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ ++properyCount;
+ } else if (propertyName == L"BoolValue2") {
+ REQUIRE(ReadValue(reader) == "true");
+ REQUIRE(ReadValue(reader) == L"true");
+ REQUIRE(ReadValue(reader) == true);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ REQUIRE(ReadValue(reader) == 1);
+ ++properyCount;
+ } else if (propertyName == L"IntValue1") {
+ REQUIRE(ReadValue(reader) == "0");
+ REQUIRE(ReadValue(reader) == L"0");
+ REQUIRE(ReadValue(reader) == false);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ ++properyCount;
+ } else if (propertyName == L"IntValue2") {
+ REQUIRE(ReadValue(reader) == "42");
+ REQUIRE(ReadValue(reader) == L"42");
+ REQUIRE(ReadValue(reader) == true);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ REQUIRE(ReadValue(reader) == 42);
+ ++properyCount;
+ } else if (propertyName == L"FloatValue") {
+ REQUIRE(ReadValue(reader) == "3.14");
+ REQUIRE(ReadValue(reader) == L"3.14");
+ REQUIRE(ReadValue(reader) == true);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3);
+ REQUIRE(ReadValue(reader) == 3.14f);
+ REQUIRE(ReadValue(reader) == 3.14);
+ ++properyCount;
+ } else if (propertyName == L"NullValue") {
+ REQUIRE(ReadValue(reader) == "");
+ REQUIRE(ReadValue(reader) == L"");
+ REQUIRE(ReadValue(reader) == false);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+ REQUIRE(ReadValue(reader) == 0);
+
+ ++properyCount;
+ }
+ }
+ REQUIRE(properyCount == 9);
+}
+
+TEST_CASE("TestWriteValueDefaultExtensions", "JSValueReaderTest") {
+ JSValue jsValue;
+ auto writer = MakeJSValueTreeWriter(jsValue);
+ writer.WriteObjectBegin();
+ WriteProperty(writer, L"StringValue1", "");
+ WriteProperty(writer, L"StringValue2", "5");
+ WriteProperty(writer, L"StringValue3", "Hello");
+ WriteProperty(writer, L"BoolValue1", false);
+ WriteProperty(writer, L"BoolValue2", true);
+ WriteProperty(writer, L"IntValue1", 0);
+ WriteProperty(writer, L"IntValue2", 42);
+ WriteProperty(writer, L"FloatValue", 3.14);
+ WriteProperty(writer, L"NullValue", nullptr);
+ writer.WriteObjectEnd();
+
+ REQUIRE(jsValue["StringValue1"] == "");
+ REQUIRE(jsValue["StringValue2"] == "5");
+ REQUIRE(jsValue["StringValue3"] == "Hello");
+ REQUIRE(jsValue["BoolValue1"] == false);
+ REQUIRE(jsValue["BoolValue2"] == true);
+ REQUIRE(jsValue["IntValue1"] == 0);
+ REQUIRE(jsValue["IntValue2"] == 42);
+ REQUIRE(jsValue["FloatValue"] == 3.14);
+ REQUIRE(jsValue["NullValue"] == nullptr);
+ REQUIRE(jsValue["NullValue"] == JSValue::Null);
+}
+
+} // namespace winrt::Microsoft::ReactNative::Bridge
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp
new file mode 100644
index 00000000000..4096b502235
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "pch.h"
+#include "JSValue.h"
+#include "JsonJSValueReader.h"
+#include "catch.hpp"
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+TEST_CASE("TestReadObject", "JSValueTest") {
+ const wchar_t *json =
+ LR"JSON({
+ "NullValue": null,
+ "ObjValue": {},
+ "ArrayValue": [],
+ "StringValue": "Hello",
+ "BoolValue": true,
+ "IntValue": 42,
+ "DoubleValue": 4.5
+ })JSON";
+
+ IJSValueReader reader = make(json);
+
+ JSValue jsValue = JSValue::ReadFrom(reader);
+ REQUIRE(jsValue.Type() == JSValueType::Object);
+ REQUIRE(jsValue.Object().at("NullValue").IsNull());
+ REQUIRE(jsValue.Object().at("ObjValue").Object().empty());
+ REQUIRE(jsValue.Object().at("ArrayValue").Array().empty());
+ REQUIRE(jsValue.Object().at("StringValue").String() == "Hello");
+ REQUIRE(jsValue.Object().at("BoolValue").Boolean() == true);
+ REQUIRE(jsValue.Object().at("IntValue").Int64() == 42);
+ REQUIRE(jsValue.Object().at("DoubleValue").Double() == 4.5);
+}
+
+TEST_CASE("TestReadNestedObject", "JSValueTest") {
+ const wchar_t *json =
+ LR"JSON({
+ "NestedObj": {
+ "NullValue": null,
+ "ObjValue": {},
+ "ArrayValue": [],
+ "StringValue": "Hello",
+ "BoolValue": true,
+ "IntValue": 42,
+ "DoubleValue": 4.5
+ }
+ })JSON";
+
+ IJSValueReader reader = make(json);
+
+ JSValue jsValue = JSValue::ReadFrom(reader);
+ REQUIRE(jsValue.Type() == JSValueType::Object);
+ const auto &nestedObj = jsValue.Object().at("NestedObj").Object();
+ REQUIRE(nestedObj.at("NullValue").IsNull());
+ REQUIRE(nestedObj.at("ObjValue").Object().empty());
+ REQUIRE(nestedObj.at("ArrayValue").Array().empty());
+ REQUIRE(nestedObj.at("StringValue").String() == "Hello");
+ REQUIRE(nestedObj.at("BoolValue").Boolean() == true);
+ REQUIRE(nestedObj.at("IntValue").Int64() == 42);
+ REQUIRE(nestedObj.at("DoubleValue").Double() == 4.5);
+}
+
+TEST_CASE("TestReadArray", "JSValueTest") {
+ const wchar_t *json = LR"JSON([null, {}, [], "Hello", true, 42, 4.5])JSON";
+ IJSValueReader reader = make(json);
+
+ JSValue jsValue = JSValue::ReadFrom(reader);
+ REQUIRE(jsValue.Type() == JSValueType::Array);
+ REQUIRE(jsValue.Array()[0].IsNull());
+ REQUIRE(jsValue.Array()[1].Object().empty());
+ REQUIRE(jsValue.Array()[2].Array().empty());
+ REQUIRE(jsValue.Array()[3].String() == "Hello");
+ REQUIRE(jsValue.Array()[4].Boolean() == true);
+ REQUIRE(jsValue.Array()[5].Int64() == 42);
+ REQUIRE(jsValue.Array()[6].Double() == 4.5);
+}
+
+TEST_CASE("TestReadNestedArray", "JSValueTest") {
+ const wchar_t *json = LR"JSON([[null, {}, [], "Hello", true, 42, 4.5]])JSON";
+ IJSValueReader reader = make(json);
+
+ JSValue jsValue = JSValue::ReadFrom(reader);
+ REQUIRE(jsValue.Type() == JSValueType::Array);
+ const auto &nestedArr = jsValue.Array()[0].Array();
+ REQUIRE(nestedArr[0].IsNull());
+ REQUIRE(nestedArr[1].Object().empty());
+ REQUIRE(nestedArr[2].Array().empty());
+ REQUIRE(nestedArr[3].String() == "Hello");
+ REQUIRE(nestedArr[4].Boolean() == true);
+ REQUIRE(nestedArr[5].Int64() == 42);
+ REQUIRE(nestedArr[6].Double() == 4.5);
+}
+
+} // namespace winrt::Microsoft::ReactNative::Bridge
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.cpp
new file mode 100644
index 00000000000..9af889d2a38
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.cpp
@@ -0,0 +1,160 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "pch.h"
+#include "JsonJSValueReader.h"
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+//===========================================================================
+// JsonJSValueReader implementation
+//===========================================================================
+
+JsonJSValueReader::JsonJSValueReader(std::wstring &&jsonText) noexcept
+ : m_jsonText{std::move(jsonText)}, m_jsonReader{JsonSource{m_jsonText}} {
+ SetCurrentValue(m_jsonReader.ReadNext());
+}
+
+JSValueType JsonJSValueReader::ValueType() noexcept {
+ return m_valueType;
+}
+
+bool JsonJSValueReader::GetNextObjectProperty(hstring &propertyName) noexcept {
+ if (!m_isIterating) {
+ if (m_valueType == JSValueType::Object) {
+ if (m_jsonReader.ReadNext() == JsonParseState::Name) {
+ m_stack.push_back(JSValueType::Object);
+ propertyName = GetHString();
+ SetCurrentValue(m_jsonReader.ReadNext());
+ return true;
+ } else {
+ m_isIterating = !m_stack.empty();
+ }
+ }
+ } else if (!m_stack.empty()) {
+ JSValueType valueType = m_stack.back();
+ if (valueType == JSValueType::Object) {
+ if (m_jsonReader.ReadNext() == JsonParseState::Name) {
+ propertyName = GetHString();
+ SetCurrentValue(m_jsonReader.ReadNext());
+ return true;
+ } else {
+ m_valueType = valueType;
+ m_stack.pop_back();
+ m_isIterating = !m_stack.empty();
+ }
+ }
+ }
+
+ propertyName = to_hstring(L"");
+ return false;
+}
+
+bool JsonJSValueReader::GetNextArrayItem() noexcept {
+ if (!m_isIterating) {
+ if (m_valueType == JSValueType::Array) {
+ JsonParseState parseState = m_jsonReader.ReadNext();
+ if (IsContainerOrValue(parseState)) {
+ m_stack.push_back(JSValueType::Array);
+ SetCurrentValue(parseState);
+ return true;
+ } else {
+ m_isIterating = !m_stack.empty();
+ }
+ }
+ } else if (!m_stack.empty()) {
+ JSValueType valueType = m_stack.back();
+ if (valueType == JSValueType::Array) {
+ JsonParseState parseState = m_jsonReader.ReadNext();
+ if (IsContainerOrValue(parseState)) {
+ SetCurrentValue(parseState);
+ return true;
+ } else {
+ m_valueType = valueType;
+ m_stack.pop_back();
+ m_isIterating = !m_stack.empty();
+ }
+ }
+ }
+
+ return false;
+}
+
+void JsonJSValueReader::SetCurrentValue(JsonParseState parseState) noexcept {
+ switch (parseState) {
+ case JsonParseState::StartObject:
+ m_valueType = JSValueType::Object;
+ m_isIterating = false;
+ break;
+
+ case JsonParseState::StartArray:
+ m_valueType = JSValueType::Array;
+ m_isIterating = false;
+ break;
+
+ case JsonParseState::Value: {
+ m_isIterating = true;
+
+ if (m_jsonReader.IsString()) {
+ m_valueType = JSValueType::String;
+ m_stringValue = GetHString();
+ break;
+ }
+
+ if (m_jsonReader.GetInt64(&m_int64Value)) {
+ m_valueType = JSValueType::Int64;
+ break;
+ }
+
+ if (m_jsonReader.GetDouble(&m_doubleValue)) {
+ m_valueType = JSValueType::Double;
+ break;
+ }
+
+ if (m_jsonReader.GetBool(&m_boolValue)) {
+ m_valueType = JSValueType::Boolean;
+ break;
+ }
+
+ m_valueType = JSValueType::Null;
+ break;
+ }
+
+ default:
+ m_valueType = JSValueType::Null;
+ break;
+ }
+}
+
+hstring JsonJSValueReader::GetString() noexcept {
+ return (m_valueType == JSValueType::String) ? m_stringValue : hstring(L"");
+}
+
+bool JsonJSValueReader::GetBoolean() noexcept {
+ return (m_valueType == JSValueType::Boolean) ? m_boolValue : false;
+}
+
+int64_t JsonJSValueReader::GetInt64() noexcept {
+ return (m_valueType == JSValueType::Int64) ? m_int64Value : 0;
+}
+
+double JsonJSValueReader::GetDouble() noexcept {
+ return (m_valueType == JSValueType::Double) ? m_doubleValue : 0;
+}
+
+hstring JsonJSValueReader::GetHString() noexcept {
+ const wchar_t *data;
+ size_t length;
+ if (m_jsonReader.GetString(&data, &length)) {
+ return hstring(data, static_cast(length));
+ } else {
+ return hstring(L"");
+ }
+}
+
+bool JsonJSValueReader::IsContainerOrValue(JsonParseState parseState) noexcept {
+ return parseState == JsonParseState::StartArray || parseState == JsonParseState::StartObject ||
+ parseState == JsonParseState::Value;
+}
+
+} // namespace winrt::Microsoft::ReactNative::Bridge
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.h b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.h
new file mode 100644
index 00000000000..08361f20012
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.h
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include "JsonReader.h"
+#include "winrt/Microsoft.ReactNative.Bridge.h"
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+struct JsonJSValueReader : implements {
+ JsonJSValueReader(std::wstring &&jsonText) noexcept;
+
+ public: // IJSValueReader
+ JSValueType ValueType() noexcept;
+ bool GetNextObjectProperty(hstring &propertyName) noexcept;
+ bool GetNextArrayItem() noexcept;
+ hstring GetString() noexcept;
+ bool GetBoolean() noexcept;
+ int64_t GetInt64() noexcept;
+ double GetDouble() noexcept;
+
+ private:
+ void SetCurrentValue(JsonParseState parseState) noexcept;
+ hstring GetHString() noexcept;
+ static bool IsContainerOrValue(JsonParseState parseState) noexcept;
+
+ private:
+ const std::wstring m_jsonText;
+ JsonReader m_jsonReader;
+ JSValueType m_valueType{JSValueType::Null};
+ std::vector m_stack;
+ bool m_isIterating{false};
+ hstring m_stringValue;
+ union {
+ bool m_boolValue;
+ int64_t m_int64Value;
+ double m_doubleValue;
+ };
+};
+
+} // namespace winrt::Microsoft::ReactNative::Bridge
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.cpp
new file mode 100644
index 00000000000..f67579fa23c
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.cpp
@@ -0,0 +1,379 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+// Terms used in here:
+// Container: represents a [] or {}
+// isStringContent: is the current value a string? Either a name or quoted value
+
+#include "pch.h"
+#include "JsonReader.h"
+#include "Crash.h"
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+static bool IsSimpleWhitespace(wchar_t c) noexcept {
+ return (c == ' ' || c == '\t' || c == '\r' || c == '\n');
+}
+
+// Main processing loop: reads tokens until end of stream, error, or
+// a valid state transition is reached.
+JsonParseState JsonReader::ReadNext() noexcept {
+ for (;; m_source.Inc()) {
+ if (m_source.IsEndOfData())
+ return JsonParseState::ErrorEndOfData;
+
+ wchar_t ch = m_source.PeekChar();
+
+ if (m_isAllowed.Option.StringChar) {
+ if (m_isAllowed.Option.EscapeChar) {
+ if (HandleEscapeChar(ch))
+ continue;
+ return HandleInvalidData();
+ }
+
+ if (m_isAllowed.Option.EscapeCharHex) {
+ if (HandleEscapeCharHex(ch))
+ continue;
+ return HandleInvalidData();
+ }
+
+ if (ch == '\\') {
+ m_isAllowed.Option.EscapeChar = 1;
+ continue;
+ }
+
+ if (ch == '\"') {
+ if (HandleEndName()) {
+ m_source.Inc();
+ return JsonParseState::Name;
+ }
+
+ if (HandleEndContainerOrValue(JsonParseState::Value)) {
+ m_source.Inc();
+ return JsonParseState::Value;
+ }
+
+ VerifyElseCrashSz(false, "This should be impossible to hit");
+ }
+
+ // Regular string character
+ m_textBuffer += ch;
+ continue;
+ }
+
+ // All whitespace outside of strings is ignored
+ if (IsSimpleWhitespace(ch))
+ continue;
+
+ switch (ch) {
+ case '[':
+ return HandleStartContainer(JsonParseState::StartArray);
+ case ']':
+ return HandleEndContainer(JsonParseState::StartArray, JsonParseState::EndArray);
+ case '{':
+ return HandleStartContainer(JsonParseState::StartObject);
+ case '}':
+ return HandleEndContainer(JsonParseState::StartObject, JsonParseState::EndObject);
+
+ case ',': {
+ if (m_isAllowed.Option.EndComma) {
+ if (HandleEndContainerOrValue(JsonParseState::Value))
+ return JsonParseState::Value;
+
+ ResetContainerState();
+ continue;
+ }
+ break;
+ }
+
+ case '"': {
+ if (HandleBeginString())
+ continue;
+ break;
+ }
+
+ case ':': {
+ if (m_isAllowed.Option.NameDelim) {
+ m_isAllowed.All = 0;
+ OnValueExpected();
+ continue;
+ }
+ break;
+ }
+
+ default: {
+ // Regular non-string character
+ if (HandleNonStringChar(ch))
+ continue;
+
+ break;
+ }
+ }
+
+ return HandleInvalidData();
+ }
+}
+
+// Called when a new Container is started
+// Called after each item in a Container
+void JsonReader::ResetContainerState() noexcept {
+ m_isAllowed.All = 0;
+
+ if (m_states.top() == JsonParseState::StartArray) {
+ OnValueExpected();
+ } else {
+ m_isAllowed.Option.BeginName = 1;
+ m_isStringContent = false;
+ m_textBuffer.clear();
+ }
+}
+
+// Called after '[', ':', ','
+void JsonReader::OnValueExpected() noexcept {
+ m_isAllowed.Option.StartContainer = 1;
+ m_isAllowed.Option.BeginValue = 1;
+ m_isStringContent = false;
+ m_textBuffer.clear();
+}
+
+// Handle the start of a new Container: [ {
+JsonParseState JsonReader::HandleStartContainer(JsonParseState state) noexcept {
+ if (!m_isAllowed.Option.StartContainer)
+ return HandleInvalidData();
+
+ m_states.push(state);
+ ResetContainerState();
+ m_isAllowed.Option.EndContainer = 1;
+
+ m_source.Inc();
+ return state;
+}
+
+// Handle the end of a Container: } ]
+JsonParseState JsonReader::HandleEndContainer(JsonParseState oldState, JsonParseState newState) noexcept {
+ if (!m_isAllowed.Option.EndContainer)
+ return HandleInvalidData();
+
+ // Check for previously ended value first
+ if (HandleEndContainerOrValue(JsonParseState::Value))
+ return JsonParseState::Value;
+
+ if (HandleEndContainerOrValue(oldState)) {
+ m_isStringContent = false;
+ m_textBuffer.clear();
+
+ m_source.Inc();
+ return newState;
+ }
+
+ return HandleInvalidData();
+}
+
+// Handle starting a new string: "
+bool JsonReader::HandleBeginString() noexcept {
+ if (m_isAllowed.Option.BeginName || m_isAllowed.Option.BeginValue) {
+ m_states.push(m_isAllowed.Option.BeginName ? JsonParseState::Name : JsonParseState::Value);
+
+ m_isAllowed.All = 0;
+ m_isAllowed.Option.StringChar = 1;
+ m_isStringContent = true;
+ return true;
+ }
+
+ return false;
+}
+
+// Handle value characters: numbers, true, false, null
+bool JsonReader::HandleNonStringChar(wchar_t ch) noexcept {
+ // FUTURE: could add more validation here
+
+ if (m_isAllowed.Option.NonStringChar) {
+ m_textBuffer += ch;
+ return true;
+ }
+
+ if (m_isAllowed.Option.BeginValue) {
+ m_isAllowed.All = 0;
+ m_isAllowed.Option.NonStringChar = 1;
+ m_isAllowed.Option.EndComma = 1;
+ m_isAllowed.Option.EndContainer = 1;
+ m_states.push(JsonParseState::Value);
+ m_textBuffer.clear();
+
+ m_textBuffer += ch;
+ return true;
+ }
+
+ return false;
+}
+
+// Handle an escaped string character
+bool JsonReader::HandleEscapeChar(wchar_t ch) noexcept {
+ switch (ch) {
+ case '\"':
+ case '\\':
+ case '/':
+ break;
+ case 'b':
+ ch = '\b';
+ break;
+ case 'f':
+ ch = '\f';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 't':
+ ch = '\t';
+ break;
+ case 'v':
+ ch = '\v';
+ break;
+
+ case 'u':
+ // Switch to hex-mode
+ m_isAllowed.Option.EscapeChar = 0;
+ m_isAllowed.Option.EscapeCharHex = 1;
+ m_hexStartIndex = m_textBuffer.length();
+ return true;
+
+ default:
+ return false;
+ }
+
+ m_textBuffer += ch;
+
+ m_isAllowed.Option.EscapeChar = 0;
+ return true;
+}
+
+// Appends the current char into the buffer
+// If all hex characters have been collected, try converting to wchar
+bool JsonReader::HandleEscapeCharHex(wchar_t ch) noexcept {
+ constexpr size_t HexCharCount = 4;
+
+ m_textBuffer += ch;
+ if (m_textBuffer.length() < m_hexStartIndex + HexCharCount)
+ return true;
+
+ const wchar_t *hexStart = m_textBuffer.c_str() + m_hexStartIndex;
+ wchar_t *hexEnd = nullptr;
+ ch = static_cast(wcstoul(hexStart, &hexEnd, 16 /*base*/));
+
+ if (hexStart + HexCharCount != hexEnd)
+ return false;
+
+ m_textBuffer.resize(m_hexStartIndex);
+ m_textBuffer += ch;
+
+ m_isAllowed.Option.EscapeCharHex = 0;
+ return true;
+}
+
+// Helper for handling the end of a Container or value: , ] }
+bool JsonReader::HandleEndContainerOrValue(JsonParseState state) noexcept {
+ if (m_states.top() == state) {
+ m_states.pop();
+
+ m_isAllowed.All = 0;
+ if (m_states.size() > 0) {
+ m_isAllowed.Option.EndComma = 1;
+ m_isAllowed.Option.EndContainer = 1;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// Handle the end of a name: "
+bool JsonReader::HandleEndName() noexcept {
+ if (m_states.top() == JsonParseState::Name) {
+ m_states.pop();
+ m_isAllowed.All = 0;
+ m_isAllowed.Option.NameDelim = 1;
+ return true;
+ }
+
+ return false;
+}
+
+// Return the current string literal
+bool JsonReader::GetString(const wchar_t **value, size_t *valueLength) noexcept {
+ if (m_isStringContent) {
+ *value = m_textBuffer.c_str();
+ *valueLength = m_textBuffer.length();
+ return true;
+ }
+
+ *value = nullptr;
+ *valueLength = 0;
+ return false;
+}
+
+// Return the current value as a bool
+bool JsonReader::GetBool(bool *value) noexcept {
+ if (!m_isStringContent) {
+ if (m_textBuffer.compare(L"true") == 0) {
+ *value = true;
+ return true;
+ }
+
+ if (m_textBuffer.compare(L"false") == 0) {
+ *value = false;
+ return true;
+ }
+ }
+
+ *value = false;
+ return false;
+}
+
+// Return the current value as an Int64
+bool JsonReader::GetInt64(int64_t *value) noexcept {
+ if (!m_isStringContent && m_textBuffer.length()) {
+ wchar_t *end = nullptr;
+ auto iValue = _wcstoi64(m_textBuffer.c_str(), &end, 10 /*base*/);
+ if (end == m_textBuffer.c_str() + m_textBuffer.size()) {
+ *value = iValue;
+ return true;
+ }
+
+ // Didn't read enough characters, fall through and fail
+ }
+
+ *value = 0;
+ return false;
+}
+
+// Return the current value as a Double
+bool JsonReader::GetDouble(double *value) noexcept {
+ if (!m_isStringContent && m_textBuffer.length()) {
+ wchar_t *end = nullptr;
+ auto dvalue = wcstod(m_textBuffer.c_str(), &end);
+ if (end == m_textBuffer.c_str() + m_textBuffer.size()) {
+ *value = dvalue;
+ return true;
+ }
+
+ // Didn't read enough characters, fall through and fail
+ }
+
+ *value = 0;
+ return false;
+}
+
+// Return true if the current value is the null value
+bool JsonReader::IsNull() noexcept {
+ if (!m_isStringContent) {
+ return (m_textBuffer.compare(L"null") == 0);
+ }
+
+ return false;
+}
+
+} // namespace winrt::Microsoft::ReactNative::Bridge
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.h b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.h
new file mode 100644
index 00000000000..34b4f332f07
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.h
@@ -0,0 +1,144 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+// Simple JsonReader
+
+#include
+#include
+#include
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+struct JsonSource {
+ static_assert(sizeof(wchar_t) == 2, "This code expects 2-byte wchars");
+
+ JsonSource(std::wstring_view buffer) noexcept : m_current{buffer.data()}, m_end{buffer.data() + buffer.size()} {}
+
+ // Caller is responsible for checking IsEndOfData() before calling PeekChar()
+ wchar_t PeekChar() const noexcept {
+ return *m_current;
+ }
+
+ // Caller is responsible for checking IsEndOfData() before calling Inc()
+ void Inc() noexcept {
+ ++m_current;
+ }
+
+ bool IsEndOfData() const noexcept {
+ return m_current == m_end;
+ }
+
+ private:
+ const wchar_t *m_current = nullptr;
+ const wchar_t *m_end = nullptr;
+};
+
+enum class JsonParseState {
+ StartArray,
+ StartObject,
+ Name,
+ Value,
+ EndObject,
+ EndArray,
+
+ // Returned for invalid data
+ ErrorInvalidData,
+
+ // End of input reached before end of Json.
+ ErrorEndOfData,
+};
+
+struct JsonReader {
+ public:
+ JsonReader(JsonSource source) : m_source{source} {
+ m_isAllowed.All = 0;
+ m_isAllowed.Option.StartContainer = 1;
+ }
+
+ JsonReader(const JsonReader &) = delete;
+ JsonReader &operator=(const JsonReader &) = delete;
+
+ JsonParseState ReadNext() noexcept;
+ bool GetString(_Deref_post_count_(*valueLength) const wchar_t **value, _Out_ size_t *valueLength) noexcept;
+ bool GetBool(_Out_ bool *value) noexcept;
+ bool GetInt64(_Out_ std::int64_t *value) noexcept;
+ bool GetDouble(_Out_ double *value) noexcept;
+ bool IsNull() noexcept;
+ bool IsString() noexcept {
+ return m_isStringContent;
+ }
+
+ private:
+ void ResetContainerState() noexcept;
+ void OnValueExpected() noexcept;
+
+ JsonParseState HandleStartContainer(JsonParseState state) noexcept;
+ JsonParseState HandleEndContainer(JsonParseState oldState, JsonParseState newState) noexcept;
+
+ bool HandleBeginString() noexcept;
+ bool HandleNonStringChar(wchar_t wch) noexcept;
+ bool HandleEscapeChar(wchar_t wch) noexcept;
+ bool HandleEscapeCharHex(wchar_t wch) noexcept;
+ bool HandleEndContainerOrValue(JsonParseState state) noexcept;
+ bool HandleEndName() noexcept;
+
+ JsonParseState HandleInvalidData() noexcept {
+ m_isAllowed.All = 0;
+ return JsonParseState::ErrorInvalidData;
+ }
+
+ private:
+ JsonSource m_source;
+
+ // Tracks the next allowed state(s)
+ union {
+ struct {
+ // [ or {
+ uint32_t StartContainer : 1;
+
+ // "
+ uint32_t BeginName : 1;
+
+ // :
+ uint32_t NameDelim : 1;
+
+ // " or value char
+ uint32_t BeginValue : 1;
+
+ // valid name or value char
+ uint32_t NonStringChar : 1;
+
+ // valid string character
+ uint32_t StringChar : 1;
+
+ // valid escape char
+ uint32_t EscapeChar : 1;
+
+ // 4 hex digits
+ uint32_t EscapeCharHex : 1;
+
+ // ] or }
+ uint32_t EndContainer : 1;
+
+ // ,
+ uint32_t EndComma : 1;
+ } Option;
+
+ uint32_t All;
+
+ } m_isAllowed;
+
+ // Stores the current name / value text
+ std::wstring m_textBuffer;
+
+ // Stack of parse states (Array, Object, Name, Value)
+ std::stack m_states;
+
+ // Used when decoding \uHHHH values
+ size_t m_hexStartIndex = 0;
+
+ // True when m_textBuffer contains a name or value string
+ bool m_isStringContent = false;
+};
+
+} // namespace winrt::Microsoft::ReactNative::Bridge
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj
new file mode 100644
index 00000000000..090ceaf2d69
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj
@@ -0,0 +1,153 @@
+
+
+
+
+ true
+ true
+ true
+ 15.0
+ {6c60e295-c8ca-4dc5-b8be-09888f58b249}
+ Win32Proj
+ Microsoft_ReactNative_Cxx_UnitTests
+ 10.0.18362.0
+ 10.0.17134.0
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ Application
+ v140
+ v141
+ v142
+ Unicode
+
+
+ true
+ true
+
+
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Use
+ pch.h
+ $(IntDir)pch.pch
+ _CONSOLE;%(PreprocessorDefinitions)
+ Level4
+ %(AdditionalOptions) /permissive- /bigobj
+
+
+
+
+ Disabled
+ _DEBUG;%(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+
+
+ Console
+ false
+
+
+ C:\git\vmoroz\react-native-windows\vnext\Microsoft.ReactNative;%(AdditionalIncludeDirectories)
+
+
+
+
+ WIN32;%(PreprocessorDefinitions)
+
+
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/NativeModuleTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/NativeModuleTest.cpp
new file mode 100644
index 00000000000..554e1510084
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/NativeModuleTest.cpp
@@ -0,0 +1,775 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "pch.h"
+#include
+#include "NativeModules.h"
+#include "ReactModuleBuilderMock.h"
+#include "catch.hpp"
+
+namespace winrt::Microsoft::ReactNative::Bridge {
+
+REACT_STRUCT(Point)
+struct Point {
+ REACT_FIELD(X)
+ int X;
+
+ REACT_FIELD(Y)
+ int Y;
+};
+
+REACT_MODULE(SimpleNativeModule)
+struct SimpleNativeModule {
+ REACT_METHOD(Add)
+ int Add(int x, int y) noexcept {
+ return x + y;
+ }
+
+ REACT_METHOD(Negate)
+ int Negate(int x) noexcept {
+ return -x;
+ }
+
+ REACT_METHOD(SayHello)
+ std::string SayHello() noexcept {
+ return "Hello";
+ }
+
+ REACT_METHOD(StaticAdd)
+ static int StaticAdd(int x, int y) noexcept {
+ return x + y;
+ }
+
+ REACT_METHOD(StaticNegate)
+ static int StaticNegate(int x) noexcept {
+ return -x;
+ }
+
+ REACT_METHOD(StaticSayHello)
+ static std::string StaticSayHello() noexcept {
+ return "Hello";
+ }
+
+ REACT_METHOD(SayHello0)
+ void SayHello0() noexcept {
+ Message = "Hello_0";
+ }
+
+ REACT_METHOD(PrintPoint)
+ void PrintPoint(Point pt) noexcept {
+ std::stringstream ss;
+ ss << "Point: (" << pt.X << ", " << pt.Y << ")";
+ Message = ss.str();
+ }
+
+ REACT_METHOD(PrintLine)
+ void PrintLine(Point start, Point end) noexcept {
+ std::stringstream ss;
+ ss << "Line: (" << start.X << ", " << start.Y << ")-(" << end.X << ", " << end.Y << ")";
+ Message = ss.str();
+ }
+
+ REACT_METHOD(StaticSayHello1)
+ static void StaticSayHello1() noexcept {
+ StaticMessage = "Hello_1";
+ }
+
+ REACT_METHOD(StaticPrintPoint)
+ static void StaticPrintPoint(Point pt) noexcept {
+ std::stringstream ss;
+ ss << "Static Point: (" << pt.X << ", " << pt.Y << ")";
+ StaticMessage = ss.str();
+ }
+
+ REACT_METHOD(StaticPrintLine)
+ static void StaticPrintLine(Point start, Point end) noexcept {
+ std::stringstream ss;
+ ss << "Static Line: (" << start.X << ", " << start.Y << ")-(" << end.X << ", " << end.Y << ")";
+ StaticMessage = ss.str();
+ }
+
+ REACT_METHOD(AddCallback)
+ void AddCallback(int x, int y, std::function const &resolve) noexcept {
+ resolve(x + y);
+ }
+
+ REACT_METHOD(NegateCallback)
+ void NegateCallback(int x, std::function const &resolve) noexcept {
+ resolve(-x);
+ }
+
+ REACT_METHOD(SayHelloCallback)
+ void SayHelloCallback(std::function const &resolve) noexcept {
+ resolve("Hello_2");
+ }
+
+ REACT_METHOD(StaticAddCallback)
+ static void StaticAddCallback(int x, int y, std::function const &resolve) noexcept {
+ resolve(x + y);
+ }
+
+ REACT_METHOD(StaticNegateCallback)
+ static void StaticNegateCallback(int x, std::function const &resolve) noexcept {
+ resolve(-x);
+ }
+
+ REACT_METHOD(StaticSayHelloCallback)
+ static void StaticSayHelloCallback(std::function const &resolve) noexcept {
+ resolve("Static Hello_2");
+ }
+
+ REACT_METHOD(DivideCallbacks)
+ void DivideCallbacks(
+ int x,
+ int y,
+ std::function const &resolve,
+ std::function const &reject) noexcept {
+ if (y != 0) {
+ resolve(x / y);
+ } else {
+ reject("Division by 0");
+ }
+ }
+
+ REACT_METHOD(NegateCallbacks)
+ void NegateCallbacks(
+ int x,
+ std::function const &resolve,
+ std::function const &reject) noexcept {
+ if (x >= 0) {
+ resolve(-x);
+ } else {
+ reject("Already negative");
+ }
+ }
+
+ REACT_METHOD(ResolveSayHelloCallbacks)
+ void ResolveSayHelloCallbacks(
+ std::function const &resolve,
+ std::function const & /*reject*/) noexcept {
+ resolve("Hello_3");
+ }
+
+ REACT_METHOD(RejectSayHelloCallbacks)
+ void RejectSayHelloCallbacks(
+ std::function const & /*resolve*/,
+ std::function const &reject) noexcept {
+ reject("Goodbye");
+ }
+
+ REACT_METHOD(StaticDivideCallbacks)
+ static void StaticDivideCallbacks(
+ int x,
+ int y,
+ std::function const &resolve,
+ std::function const &reject) noexcept {
+ if (y != 0) {
+ resolve(x / y);
+ } else {
+ reject("Division by 0");
+ }
+ }
+
+ REACT_METHOD(StaticNegateCallbacks)
+ static void StaticNegateCallbacks(
+ int x,
+ std::function const &resolve,
+ std::function const &reject) noexcept {
+ if (x >= 0) {
+ resolve(-x);
+ } else {
+ reject("Already negative");
+ }
+ }
+
+ REACT_METHOD(StaticResolveSayHelloCallbacks)
+ static void StaticResolveSayHelloCallbacks(
+ std::function const &resolve,
+ std::function const & /*reject*/) noexcept {
+ resolve("Hello_3");
+ }
+
+ REACT_METHOD(StaticRejectSayHelloCallbacks)
+ static void StaticRejectSayHelloCallbacks(
+ std::function const & /*resolve*/,
+ std::function const &reject) noexcept {
+ reject("Goodbye");
+ }
+
+ REACT_METHOD(DividePromise)
+ void DividePromise(int x, int y, ReactPromise &&result) noexcept {
+ if (y != 0) {
+ result.Resolve(x / y);
+ } else {
+ ReactError error{};
+ error.Message = "Division by 0";
+ result.Reject(std::move(error));
+ }
+ }
+
+ REACT_METHOD(NegatePromise)
+ void NegatePromise(int x, ReactPromise &&result) noexcept {
+ if (x >= 0) {
+ result.Resolve(-x);
+ } else {
+ ReactError error{};
+ error.Message = "Already negative";
+ result.Reject(std::move(error));
+ }
+ }
+
+ REACT_METHOD(ResolveSayHelloPromise)
+ void ResolveSayHelloPromise(ReactPromise &&result) noexcept {
+ result.Resolve("Hello_4");
+ }
+
+ REACT_METHOD(RejectSayHelloPromise)
+ void RejectSayHelloPromise(ReactPromise &&result) noexcept {
+ ReactError error{};
+ error.Message = "Promise rejected";
+ result.Reject(std::move(error));
+ }
+
+ REACT_METHOD(StaticDividePromise)
+ static void StaticDividePromise(int x, int y, ReactPromise &&result) noexcept {
+ if (y != 0) {
+ result.Resolve(x / y);
+ } else {
+ ReactError error{};
+ error.Message = "Division by 0";
+ result.Reject(std::move(error));
+ }
+ }
+
+ REACT_METHOD(StaticNegatePromise)
+ static void StaticNegatePromise(int x, ReactPromise &&result) noexcept {
+ if (x >= 0) {
+ result.Resolve(-x);
+ } else {
+ ReactError error{};
+ error.Message = "Already negative";
+ result.Reject(std::move(error));
+ }
+ }
+
+ REACT_METHOD(StaticResolveSayHelloPromise)
+ static void StaticResolveSayHelloPromise(ReactPromise &&result) noexcept {
+ result.Resolve("Hello_4");
+ }
+
+ REACT_METHOD(StaticRejectSayHelloPromise)
+ static void StaticRejectSayHelloPromise(ReactPromise &&result) noexcept {
+ ReactError error{};
+ error.Message = "Promise rejected";
+ result.Reject(std::move(error));
+ }
+
+ REACT_SYNC_METHOD(AddSync)
+ int AddSync(int x, int y) noexcept {
+ return x + y;
+ }
+
+ REACT_SYNC_METHOD(NegateSync)
+ int NegateSync(int x) noexcept {
+ return -x;
+ }
+
+ REACT_SYNC_METHOD(SayHelloSync)
+ std::string SayHelloSync() noexcept {
+ return "Hello";
+ }
+
+ REACT_SYNC_METHOD(StaticAddSync)
+ static int StaticAddSync(int x, int y) noexcept {
+ return x + y;
+ }
+
+ REACT_SYNC_METHOD(StaticNegateSync)
+ static int StaticNegateSync(int x) noexcept {
+ return -x;
+ }
+
+ REACT_SYNC_METHOD(StaticSayHelloSync)
+ static std::string StaticSayHelloSync() noexcept {
+ return "Hello";
+ }
+
+ REACT_CONSTANT(Constant1)
+ const std::string Constant1{"MyConstant1"};
+
+ REACT_CONSTANT(Constant2, L"const2")
+ const std::string Constant2{"MyConstant2"};
+
+ REACT_CONSTANT(Constant3, L"const3")
+ static constexpr Point Constant3{/*X =*/2, /*Y =*/3};
+
+ REACT_CONSTANT(Constant4)
+ static constexpr Point Constant4{/*X =*/3, /*Y =*/4};
+
+ REACT_CONSTANT_PROVIDER(Constant5)
+ void Constant5(ReactConstantProvider &provider) noexcept {
+ provider.Add(L"const51", Point{/*X =*/12, /*Y =*/14});
+ provider.Add(L"const52", "MyConstant52");
+ }
+
+ REACT_CONSTANT_PROVIDER(Constant6)
+ static void Constant6(ReactConstantProvider &provider) noexcept {
+ provider.Add(L"const61", Point{/*X =*/15, /*Y =*/17});
+ provider.Add(L"const62", "MyConstant62");
+ }
+
+ REACT_EVENT(OnIntResult1)
+ std::function OnIntResult1;
+
+ REACT_EVENT(OnPointResult2, L"onPointResult2")
+ std::function OnPointResult2;
+
+ std::string Message;
+ static std::string StaticMessage;
+};
+
+/*static*/ std::string SimpleNativeModule::StaticMessage;
+
+// Common base class used by all unit tests below.
+struct NativeModuleTestFixture {
+ NativeModuleTestFixture() {
+ m_moduleBuilder = make(m_builderMock);
+ auto provider = MakeModuleProvider();
+ m_moduleObject = provider(m_moduleBuilder);
+ auto reactModule = m_moduleObject.as();
+ m_module = &BoxedValue::GetImpl(reactModule);
+ }
+
+ protected:
+ ReactModuleBuilderMock m_builderMock{};
+ IReactModuleBuilder m_moduleBuilder;
+ Windows::Foundation::IInspectable m_moduleObject{nullptr};
+ SimpleNativeModule *m_module;
+};
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_Add", "NativeModuleTest") {
+ m_builderMock.Call1(L"Add", std::function([](int result) noexcept { REQUIRE(result == 8); }), 3, 5);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_Negate", "NativeModuleTest") {
+ m_builderMock.Call1(L"Negate", std::function([](int result) noexcept { REQUIRE(result == -3); }), 3);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_SayHello", "NativeModuleTest") {
+ m_builderMock.Call1(L"SayHello", std::function([](const std::string &result) noexcept {
+ REQUIRE(result == "Hello");
+ }));
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticAdd", "NativeModuleTest") {
+ m_builderMock.Call1(
+ L"StaticAdd", std::function([](int result) noexcept { REQUIRE(result == 25); }), 20, 5);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegate", "NativeModuleTest") {
+ m_builderMock.Call1(L"StaticNegate", std::function([](int result) noexcept { REQUIRE(result == -7); }), 7);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticSayHello", "NativeModuleTest") {
+ m_builderMock.Call1(
+ L"StaticSayHello",
+ std::function([](const std::string &result) noexcept { REQUIRE(result == "Hello"); }));
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_SayHello0", "NativeModuleTest") {
+ m_builderMock.Call0(L"SayHello0");
+ REQUIRE(m_module->Message == "Hello_0");
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_PrintPoint", "NativeModuleTest") {
+ m_builderMock.Call0(L"PrintPoint", Point{/*X =*/3, /*Y =*/5});
+ REQUIRE(m_module->Message == "Point: (3, 5)");
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_PrintLine", "NativeModuleTest") {
+ m_builderMock.Call0(L"PrintLine", Point{/*X =*/3, /*Y =*/5}, Point{/*X =*/6, /*Y =*/8});
+ REQUIRE(m_module->Message == "Line: (3, 5)-(6, 8)");
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticSayHello1", "NativeModuleTest") {
+ m_builderMock.Call0(L"StaticSayHello1");
+ REQUIRE(SimpleNativeModule::StaticMessage == "Hello_1");
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticPrintPoint", "NativeModuleTest") {
+ m_builderMock.Call0(L"StaticPrintPoint", Point{/*X =*/13, /*Y =*/15});
+ REQUIRE(SimpleNativeModule::StaticMessage == "Static Point: (13, 15)");
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticPrintLine", "NativeModuleTest") {
+ m_builderMock.Call0(L"StaticPrintLine", Point{/*X =*/13, /*Y =*/15}, Point{/*X =*/16, /*Y =*/18});
+ REQUIRE(SimpleNativeModule::StaticMessage == "Static Line: (13, 15)-(16, 18)");
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_AddCallback", "NativeModuleTest") {
+ m_builderMock.Call1(
+ L"AddCallback", std::function([](int result) noexcept { REQUIRE(result == -1); }), 7, -8);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegateCallback", "NativeModuleTest") {
+ m_builderMock.Call1(
+ L"NegateCallback", std::function([](int result) noexcept { REQUIRE(result == -4); }), 4);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_SayHelloCallback", "NativeModuleTest") {
+ m_builderMock.Call1(L"SayHelloCallback", std::function([
+ ](const std::string &result) noexcept { REQUIRE(result == "Hello_2"); }));
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticAddCallback", "NativeModuleTest") {
+ m_builderMock.Call1(
+ L"StaticAddCallback", std::function([](int result) noexcept { REQUIRE(result == 60); }), 4, 56);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegateCallback", "NativeModuleTest") {
+ m_builderMock.Call1(
+ L"StaticNegateCallback", std::function([](int result) noexcept { REQUIRE(result == -33); }), 33);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticSayHelloCallback", "NativeModuleTest") {
+ m_builderMock.Call1(L"StaticSayHelloCallback", std::function([
+ ](const std::string &result) noexcept { REQUIRE(result == "Static Hello_2"); }));
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DivideCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"DivideCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == 3); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }),
+ 6,
+ 2);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DivideCallbacksError", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"DivideCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == 3); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }),
+ 6,
+ 0);
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegateCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"NegateCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == -5); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }),
+ 5);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegateCallbacksError", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"NegateCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == -5); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }),
+ -5);
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_ResolveSayHelloCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"ResolveSayHelloCallbacks",
+ std::function(
+ [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); }));
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_RejectSayHelloCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"RejectSayHelloCallbacks",
+ std::function(
+ [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); }));
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticDivideCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"StaticDivideCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == 3); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }),
+ 6,
+ 2);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticDivideCallbacksError", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"StaticDivideCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == 3); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }),
+ 6,
+ 0);
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegateCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"StaticNegateCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == -5); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }),
+ 5);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegateCallbacksError", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"StaticNegateCallbacks",
+ std::function([](int result) noexcept { REQUIRE(result == -5); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }),
+ -5);
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticResolveSayHelloCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"StaticResolveSayHelloCallbacks",
+ std::function(
+ [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); }));
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticRejectSayHelloCallbacks", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"StaticRejectSayHelloCallbacks",
+ std::function(
+ [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); }));
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DividePromise", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"DividePromise",
+ std::function([](int result) noexcept { REQUIRE(result == 3); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }),
+ 6,
+ 2);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DividePromiseError", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"DividePromise",
+ std::function([](int result) noexcept { REQUIRE(result == 3); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }),
+ 6,
+ 0);
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegatePromise", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"NegatePromise",
+ std::function([](int result) noexcept { REQUIRE(result == -5); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }),
+ 5);
+ REQUIRE(m_builderMock.IsResolveCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegatePromiseError", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"NegatePromise",
+ std::function([](int result) noexcept { REQUIRE(result == -5); }),
+ std::function(
+ [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }),
+ -5);
+ REQUIRE(m_builderMock.IsRejectCallbackCalled());
+}
+
+TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_ResolveSayHelloPromise", "NativeModuleTest") {
+ m_builderMock.Call2(
+ L"ResolveSayHelloPromise",
+ std::function(
+ [](const std::string &result) noexcept { REQUIRE(result == "Hello_4"); }),
+ std::function