Skip to content

Commit

Permalink
ReactNonAbiValue for IReactPropertyBag non-ABI values (#4911)
Browse files Browse the repository at this point in the history
* ReactNonAbiValue for IReactPropertyBag non-ABI values

* Added comments to the new code.
  • Loading branch information
vmoroz authored and NickGerleman committed May 19, 2020
1 parent e6f779c commit 82d78b9
Show file tree
Hide file tree
Showing 21 changed files with 379 additions and 102 deletions.
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "ReactNonAbiValue for IReactPropertyBag non-ABI values",
"packageName": "react-native-windows",
"email": "vmorozov@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-05-15T00:22:54.871Z"
}
25 changes: 12 additions & 13 deletions vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp
Expand Up @@ -34,20 +34,19 @@ TEST_CLASS (JSValueTest) {
TestCheckEqual(JSValueType::Int64, jsValue["IntValue"].Type());
TestCheckEqual(JSValueType::Double, jsValue["DoubleValue"].Type());

JSValueObject const *objValue = nullptr;
JSValueArray const *arrayValue = nullptr;
std::string const *stringValue = nullptr;
bool const *boolValue = nullptr;
int64_t const *intValue = nullptr;
double const *doubleValue = nullptr;

TestCheck(jsValue["NullValue"].IsNull());
TestCheck(objValue = jsValue["ObjValue"].TryGetObject());
TestCheck(arrayValue = jsValue["ArrayValue"].TryGetArray());
TestCheck(stringValue = jsValue["StringValue"].TryGetString());
TestCheck(boolValue = jsValue["BoolValue"].TryGetBoolean());
TestCheck(intValue = jsValue["IntValue"].TryGetInt64());
TestCheck(doubleValue = jsValue["DoubleValue"].TryGetDouble());
JSValueObject const *objValue = jsValue["ObjValue"].TryGetObject();
JSValueArray const *arrayValue = jsValue["ArrayValue"].TryGetArray();
std::string const *stringValue = jsValue["StringValue"].TryGetString();
bool const *boolValue = jsValue["BoolValue"].TryGetBoolean();
int64_t const *intValue = jsValue["IntValue"].TryGetInt64();
double const *doubleValue = jsValue["DoubleValue"].TryGetDouble();
TestCheck(objValue);
TestCheck(arrayValue);
TestCheck(stringValue);
TestCheck(boolValue);
TestCheck(intValue);
TestCheck(doubleValue);

TestCheckEqual(1, objValue->size());
TestCheckEqual(1, jsValue["ObjValue"].PropertyCount());
Expand Down
Expand Up @@ -138,13 +138,13 @@
</Text>
</ItemGroup>
<ItemGroup>
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IBoxedValue.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IJSValueReader.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IJSValueWriter.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactContext.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactModuleBuilder.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactPackageBuilder.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IViewManager.idl" />
<Midl Include="..\Microsoft.ReactNative\IReactNonAbiValue.idl" />
<Midl Include="..\Microsoft.ReactNative\IReactPropertyBag.idl" />
<Midl Include="..\Microsoft.ReactNative\NoExceptionAttribute.idl" />
</ItemGroup>
Expand Down
Expand Up @@ -598,8 +598,7 @@ TEST_CLASS (NativeModuleTest) {
m_moduleBuilder = winrt::make<React::ReactModuleBuilderImpl>(m_builderMock);
auto provider = React::MakeModuleProvider<SimpleNativeModule>();
m_moduleObject = m_builderMock.CreateModule(provider, m_moduleBuilder);
auto reactModule = m_moduleObject.as<React::IBoxedValue>();
m_module = &React::BoxedValue<SimpleNativeModule>::GetValueUnsafe(reactModule);
m_module = React::ReactNonAbiValue<SimpleNativeModule>::GetPtrUnsafe(m_moduleObject);
}

TEST_METHOD(TestMethodCall_Add) {
Expand Down
Expand Up @@ -603,8 +603,8 @@ TEST_CLASS (NoAttributeNativeModuleTest) {
m_moduleBuilder = winrt::make<React::ReactModuleBuilderImpl>(m_builderMock);
auto provider = React::MakeModuleProvider<SimpleNativeModule2>();
m_moduleObject = m_builderMock.CreateModule(provider, m_moduleBuilder);
auto reactModule = m_moduleObject.as<React::IBoxedValue>();
m_module = &React::BoxedValue<SimpleNativeModule2>::GetValueUnsafe(reactModule);
auto reactModule = m_moduleObject.as<React::IReactNonAbiValue>();
m_module = React::ReactNonAbiValue<SimpleNativeModule2>::GetPtrUnsafe(m_moduleObject);
}

TEST_METHOD(TestMethodCall_Add) {
Expand Down
Expand Up @@ -1117,8 +1117,7 @@ TEST_CLASS (TurboModuleTest) {
m_moduleBuilder = winrt::make<winrt::Microsoft::ReactNative::ReactModuleBuilderImpl>(m_builderMock);
auto provider = winrt::Microsoft::ReactNative::MakeTurboModuleProvider<MyTurboModule, MyTurboModuleSpec>();
m_moduleObject = m_builderMock.CreateModule(provider, m_moduleBuilder);
auto reactModule = m_moduleObject.as<winrt::Microsoft::ReactNative::IBoxedValue>();
m_module = &winrt::Microsoft::ReactNative::BoxedValue<MyTurboModule>::GetValueUnsafe(reactModule);
m_module = winrt::Microsoft::ReactNative::ReactNonAbiValue<MyTurboModule>::GetPtrUnsafe(m_moduleObject);
}

TEST_METHOD(TestMethodCall_Add) {
Expand Down
31 changes: 0 additions & 31 deletions vnext/Microsoft.ReactNative.Cxx/BoxedValue.h

This file was deleted.

Expand Up @@ -14,7 +14,6 @@
<ProjectCapability Include="SourceItemsFromImports" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)BoxedValue.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)Crash.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)JSValue.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)JSValueReader.h" />
Expand All @@ -24,6 +23,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)JSValueXaml.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ModuleRegistration.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)NativeModules.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ReactNonAbiValue.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ReactPropertyBag.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ReactContext.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ReactError.h" />
Expand Down
10 changes: 5 additions & 5 deletions vnext/Microsoft.ReactNative.Cxx/NativeModules.h
Expand Up @@ -4,11 +4,11 @@
#pragma once
#include "winrt/Microsoft.ReactNative.h"

#include "BoxedValue.h"
#include "JSValueReader.h"
#include "JSValueWriter.h"
#include "ModuleRegistration.h"
#include "ReactContext.h"
#include "ReactNonAbiValue.h"
#include "ReactPromise.h"

#include <functional>
Expand Down Expand Up @@ -1109,8 +1109,8 @@ struct TurboModuleSpec {
template <class TModule>
inline ReactModuleProvider MakeModuleProvider() noexcept {
return [](IReactModuleBuilder const &moduleBuilder) noexcept {
auto moduleObject = make<BoxedValue<TModule>>();
auto module = &BoxedValue<TModule>::GetValueUnsafe(moduleObject);
ReactNonAbiValue<TModule> moduleObject{std::in_place};
TModule *module = moduleObject.GetPtr();
ReactModuleBuilder builder{module, moduleBuilder};
GetReactModuleInfo(module, builder);
builder.CompleteRegistration();
Expand All @@ -1122,8 +1122,8 @@ template <class TModule, class TModuleSpec>
inline ReactModuleProvider MakeTurboModuleProvider() noexcept {
TModuleSpec::template ValidateModule<TModule>();
return [](IReactModuleBuilder const &moduleBuilder) noexcept {
auto moduleObject = make<BoxedValue<TModule>>();
auto module = &BoxedValue<TModule>::GetValueUnsafe(moduleObject);
ReactNonAbiValue<TModule> moduleObject{std::in_place};
TModule *module = moduleObject.GetPtr();
ReactModuleBuilder builder{module, moduleBuilder};
GetReactModuleInfo(module, builder);
builder.CompleteRegistration();
Expand Down
125 changes: 125 additions & 0 deletions vnext/Microsoft.ReactNative.Cxx/ReactNonAbiValue.h
@@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once
#ifndef MICROSOFT_REACTNATIVE_REACTNONABIVALUE
#define MICROSOFT_REACTNATIVE_REACTNONABIVALUE

#include <winrt/Microsoft.ReactNative.h>
#include <utility>

namespace winrt::Microsoft::ReactNative {

namespace implementation {

// The ReactNonAbiValue implementation that wraps up the non-ABI safe value.
// This class is created by winrt::Microsoft::ReactNative::ReactNonAbiValue constructor
// that accepts std::in_place of type std::in_place_t as the first parameter.
template <class T>
struct ReactNonAbiValue : implements<ReactNonAbiValue<T>, IReactNonAbiValue> {
// Create ReactNonAbiValue and construct the wrapped value.
template <class... TArgs>
ReactNonAbiValue(TArgs &&... args) noexcept : m_value{std::forward<TArgs>(args)...} {}

// Get a pointer to the wrapped value.
int64_t GetPtr() const noexcept {
return reinterpret_cast<int64_t>(&m_value);
}

private:
T m_value{}; // Wrapped non-ABI value.
};

} // namespace implementation

// Use this class to work with non-ABI value wrappers.
// This class is a smart pointer to the implementation::ReactNonAbiValue which is
// ref-counted and allocated in the heap. The ReactNonAbiValue should be used as if
// it would be a winrt::com_ptr<implementation::ReactNonAbiValue>. It has the same behavior in regards
// to copy/move semantic.
// Use the constructor that accepts std::in_place of type std::in_place_t as the first parameter to allocate
// a new instance of implementation::ReactNonAbiValue. The std::in_place allows to disambiguate the calls to
// other constructors.
template <class T>
struct ReactNonAbiValue : Windows::Foundation::IInspectable {
// Create a new instance of implementation::ReactNonAbiValue with args and keep a ref-counted pointer to it.
template <class... TArgs>
ReactNonAbiValue(std::in_place_t, TArgs &&... args) noexcept
: IInspectable{make<implementation::ReactNonAbiValue<T>>(std::forward<TArgs>(args)...)} {}

// Create an empty ReactNonAbiValue.
ReactNonAbiValue(std::nullptr_t = nullptr) noexcept {}

// Create a ReactNonAbiValue with taking the onwership from the provided pointer.
ReactNonAbiValue(void *ptr, take_ownership_from_abi_t) noexcept : IInspectable(ptr, take_ownership_from_abi) {}

// Get a pointer to the value from the object it implements IReactNonAbiValue.
// The method is unsafe because it provides no protection in case if the object has a value of different type.
// Treat this method as if you would cast to a value type from 'void*' type.
// The method returns nullptr if obj doe snot implement the IReactNonAbiValue interface.
static T *GetPtrUnsafe(IInspectable const &obj) noexcept {
if (IReactNonAbiValue temp = obj.try_as<IReactNonAbiValue>()) {
return reinterpret_cast<T *>(temp.GetPtr());
} else {
return nullptr;
}
}

// Get pointer to the stored value.
// Return nullptr if ReactNonAbiValue is empty.
T *GetPtr() const noexcept {
if (IReactNonAbiValue const &temp =
*static_cast<IReactNonAbiValue const *>(static_cast<IInspectable const *>(this))) {
return reinterpret_cast<T *>(temp.GetPtr());
} else {
return nullptr;
}
}

// Get pointer to the stored value.
// Return nullptr if ReactNonAbiValue is empty.
T *operator->() const noexcept {
return GetPtr();
}

// Get a reference to the stored value.
// Crash the app if ReactNonAbiValue is empty.
T &operator*() const noexcept {
return *GetPtr();
}

// Get a reference to the stored value.
// Crash the app if ReactNonAbiValue is empty.
T &Value() const noexcept {
return *GetPtr();
}

// Call the call operator() for the stored value.
// Crash the app if ReactNonAbiValue is empty.
template <class... TArgs>
auto operator()(TArgs &&... args) const {
return (*GetPtr())(std::forward<TArgs>(args)...);
}
};

// Type traits to check if type T is a IsReactNonAbiValue.
template <class T>
struct IsReactNonAbiValue : std::false_type {};
template <class T>
struct IsReactNonAbiValue<ReactNonAbiValue<T>> : std::true_type {};

// A shortcut for the value of the IsReactNonAbiValue type traits.
template <class T>
constexpr bool IsReactNonAbiValueV = IsReactNonAbiValue<T>::value;

} // namespace winrt::Microsoft::ReactNative

namespace winrt::impl {
// C++/WinRT binding to connect ReactNonAbiValue with the IReactNonAbiValue interface.
template <class T>
struct default_interface<Microsoft::ReactNative::ReactNonAbiValue<T>> {
using type = Microsoft::ReactNative::IReactNonAbiValue;
};
} // namespace winrt::impl

#endif // MICROSOFT_REACTNATIVE_REACTNONABIVALUE

0 comments on commit 82d78b9

Please sign in to comment.