Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "Allow storing non-WinRT types in ReactPropertyBag",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-05-13T01:57:59.370Z"
}
31 changes: 31 additions & 0 deletions vnext/Microsoft.ReactNative.Cxx/BoxedValue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once
#ifndef MICROSOFT_REACTNATIVE_BOXEDVALUE
#define MICROSOFT_REACTNATIVE_BOXEDVALUE

#include <winrt/Microsoft.ReactNative.h>

namespace winrt::Microsoft::ReactNative {

template <class T>
struct BoxedValue : implements<BoxedValue<T>, IBoxedValue> {
template <class... TArgs>
BoxedValue(TArgs &&... args) noexcept : m_value(std::forward<TArgs>(args)...) {}

int64_t GetPtr() const noexcept {
return reinterpret_cast<int64_t>(&m_value);
}

static T &GetValueUnsafe(IBoxedValue const &boxedValue) noexcept {
return *reinterpret_cast<T *>(boxedValue.GetPtr());
}

private:
T m_value{};
};

} // namespace winrt::Microsoft::ReactNative

#endif // MICROSOFT_REACTNATIVE_BOXEDVALUE
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<ProjectCapability Include="SourceItemsFromImports" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)BoxedValue.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)CppWinRTIncludes.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)Crash.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)JSValue.h" />
Expand Down
18 changes: 1 addition & 17 deletions vnext/Microsoft.ReactNative.Cxx/NativeModules.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once
#include "winrt/Microsoft.ReactNative.h"

#include "BoxedValue.h"
#include "JSValueReader.h"
#include "JSValueWriter.h"
#include "ModuleRegistration.h"
Expand Down Expand Up @@ -1105,23 +1106,6 @@ struct TurboModuleSpec {
}
};

template <class T>
struct BoxedValue : implements<BoxedValue<T>, IBoxedValue> {
template <class... TArgs>
BoxedValue(TArgs &&... args) noexcept : m_value(std::forward<TArgs>(args)...) {}

int64_t GetPtr() noexcept {
return reinterpret_cast<int64_t>(&m_value);
}

static T &GetValueUnsafe(IBoxedValue const &boxedValue) noexcept {
return *reinterpret_cast<T *>(boxedValue.GetPtr());
}

private:
T m_value{};
};

template <class TModule>
inline ReactModuleProvider MakeModuleProvider() noexcept {
return [](IReactModuleBuilder const &moduleBuilder) noexcept {
Expand Down
24 changes: 19 additions & 5 deletions vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@
// directly because their null value may indicated absent property value.
// For other types we return std::optional<T>. It has std::nullopt value in case if
// no property value is found or if it has a wrong type.
// To avoid compilation errors the non-IInspectable types must be WinRT types which are described here:
// To pass values through the ABI boundary the non-IInspectable types must be WinRT types
// which are described here:
// https://docs.microsoft.com/en-us/uwp/api/windows.foundation.propertytype?view=winrt-18362
//

#include <winrt/Microsoft.ReactNative.h>
#include <optional>
#include "BoxedValue.h"

namespace winrt::Microsoft::ReactNative {

Expand Down Expand Up @@ -113,10 +115,10 @@ struct ReactPropertyId {
IReactPropertyName m_handle;
};

// ReactPropertyBag is a wrapper for IReactPropertyBag to stores strongly-typed properties in a thread-safe way.
// ReactPropertyBag is a wrapper for IReactPropertyBag to store strongly-typed properties in a thread-safe way.
// Types inherited from IInspectable are stored directly.
// Values of other types are boxed with help of winrt::box_value.
// Only WinRT types can be stored.
// Non-WinRT types are wrapped with the help of BoxedValue template.
struct ReactPropertyBag {
// Property result type is either T or std::optional<T>.
// T is returned for types inherited from IInspectable.
Expand Down Expand Up @@ -235,7 +237,11 @@ struct ReactPropertyBag {
private:
template <class T>
static Windows::Foundation::IInspectable ToObject(T const &value) noexcept {
return winrt::box_value(value);
if constexpr (impl::has_category_v<T>) {
return box_value(value);
} else {
return make<BoxedValue<T>>(value);
}
}

template <class T>
Expand All @@ -247,7 +253,7 @@ struct ReactPropertyBag {
static auto FromObject(Windows::Foundation::IInspectable const &obj) noexcept {
if constexpr (std::is_base_of_v<Windows::Foundation::IInspectable, T>) {
return obj.try_as<T>();
} else {
} else if constexpr (impl::has_category_v<T>) {
if (obj) {
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
if constexpr (std::is_same_v<T, GUID>) {
Expand All @@ -267,6 +273,14 @@ struct ReactPropertyBag {
}
}

return std::optional<T>{};
} else {
if (obj) {
if (auto temp = obj.try_as<IBoxedValue>()) {
return std::optional<T>{BoxedValue<T>::GetValueUnsafe(temp)};
Copy link
Member

@asklar asklar May 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't you need a std::move and make temp a && ? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the static GetValueUnsafe method receives const&. It uses to call the GetPtr method. Moving value here would not optimize anything.


In reply to: 424138773 [](ancestors = 424138773)

}
}

return std::optional<T>{};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using namespace winrt;
using namespace Microsoft::ReactNative;
using namespace Windows::Foundation;

namespace ReactNativeIntegrationTests {

Expand Down Expand Up @@ -522,6 +523,27 @@ TEST_CLASS (ReactPropertyBagTests) {
pb.Remove(fooName);
TestCheck(!pb.Get(fooName));
}

TEST_METHOD(PropertyBag_Property_Functor) {
ReactPropertyId<Mso::Functor<IInspectable()>> fooName{L"Foo"};
ReactPropertyBag pb{ReactPropertyBagHelper::CreatePropertyBag()};

TestCheck(!pb.Get(fooName));
Mso::Functor<IInspectable()> createValue1 = []() noexcept {
return winrt::box_value(5);
};
TestCheckEqual(createValue1, *pb.GetOrCreate(fooName, [&createValue1]() { return createValue1; }));
TestCheckEqual(createValue1, *pb.Get(fooName));
TestCheckEqual(5, winrt::unbox_value<int>((*pb.Get(fooName))()));

Mso::Functor<IInspectable()> createValue2 = []() { return winrt::box_value(10); };
pb.Set(fooName, createValue2);
TestCheckEqual(createValue2, *pb.Get(fooName));
TestCheckEqual(10, winrt::unbox_value<int>((*pb.Get(fooName))()));
TestCheck(createValue1 != *pb.Get(fooName));
pb.Remove(fooName);
TestCheck(!pb.Get(fooName));
}
};

} // namespace ReactNativeIntegrationTests