Skip to content

Commit 087624c

Browse files
appdenfacebook-github-bot
authored andcommitted
Add supportsFromJs and supportsToJs template variables
Summary: These `constexpr` template variables make it really easy to test for bridging conversion to/from the specified types. The unit tests for this actually uncovered a bug with incompatible casts from lvalue references that was fixed in this diff as well. Changelog: Internal Reviewed By: christophpurrer Differential Revision: D35105398 fbshipit-source-id: 6e5f16e44ba99b296284970bf32c1f2f47201391
1 parent 57a90f7 commit 087624c

File tree

4 files changed

+171
-26
lines changed

4 files changed

+171
-26
lines changed

ReactCommon/react/bridging/Base.h

+30
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,35 @@ auto toJs(
120120
return Bridging<bridging_t<T>>::toJs(rt, std::forward<T>(value), jsInvoker);
121121
}
122122

123+
template <typename, typename = jsi::Value, typename = void>
124+
inline constexpr bool supportsFromJs = false;
125+
126+
template <typename T, typename Arg = jsi::Value>
127+
inline constexpr bool supportsFromJs<
128+
T,
129+
Arg,
130+
std::void_t<decltype(fromJs<T>(
131+
std::declval<jsi::Runtime &>(),
132+
std::declval<Arg>(),
133+
nullptr))>> = true;
134+
135+
template <typename, typename = jsi::Value, typename = void>
136+
inline constexpr bool supportsToJs = false;
137+
138+
template <typename T, typename Ret = jsi::Value>
139+
inline constexpr bool supportsToJs<
140+
T,
141+
Ret,
142+
std::void_t<decltype(toJs(
143+
std::declval<jsi::Runtime &>(),
144+
std::declval<T>(),
145+
nullptr))>> =
146+
std::is_convertible_v<
147+
decltype(toJs(
148+
std::declval<jsi::Runtime &>(),
149+
std::declval<T>(),
150+
nullptr)),
151+
Ret>;
152+
123153
} // namespace bridging
124154
} // namespace facebook::react

ReactCommon/react/bridging/Class.h

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ T callFromJs(
2525
JSArgs &&...args) {
2626
static_assert(
2727
sizeof...(Args) == sizeof...(JSArgs), "Incorrect arguments length");
28+
static_assert(
29+
(supportsFromJs<Args, JSArgs> && ...), "Incompatible arguments");
2830

2931
if constexpr (std::is_void_v<T>) {
3032
(instance->*method)(
@@ -40,13 +42,17 @@ T callFromJs(
4042
return jsi::Value();
4143

4244
} else if constexpr (is_jsi_v<T>) {
45+
static_assert(supportsToJs<R, T>, "Incompatible return type");
46+
4347
return toJs(
4448
rt,
4549
(instance->*method)(
4650
rt, fromJs<Args>(rt, std::forward<JSArgs>(args), jsInvoker)...),
4751
jsInvoker);
4852

4953
} else {
54+
static_assert(std::is_convertible_v<R, T>, "Incompatible return type");
55+
5056
return (instance->*method)(
5157
rt, fromJs<Args>(rt, std::forward<JSArgs>(args), jsInvoker)...);
5258
}

ReactCommon/react/bridging/Convert.h

+40-26
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,53 @@ inline constexpr bool is_jsi_v =
2323
std::is_same_v<jsi::String, remove_cvref_t<T>> ||
2424
std::is_base_of_v<jsi::Object, remove_cvref_t<T>>;
2525

26+
template <typename T>
27+
struct Converter;
28+
2629
template <typename T>
2730
struct ConverterBase {
31+
using BaseT = remove_cvref_t<T>;
32+
2833
ConverterBase(jsi::Runtime &rt, T &&value)
2934
: rt_(rt), value_(std::forward<T>(value)) {}
3035

31-
operator T() && {
32-
return std::forward<T>(this->value_);
36+
operator BaseT() && {
37+
if constexpr (std::is_lvalue_reference_v<T>) {
38+
// Copy the reference into a Value that then can be moved from.
39+
auto value = jsi::Value(rt_, value_);
40+
41+
if constexpr (std::is_same_v<BaseT, jsi::Value>) {
42+
return std::move(value);
43+
} else if constexpr (std::is_same_v<BaseT, jsi::String>) {
44+
return std::move(value).getString(rt_);
45+
} else if constexpr (std::is_same_v<BaseT, jsi::Object>) {
46+
return std::move(value).getObject(rt_);
47+
} else if constexpr (std::is_same_v<BaseT, jsi::Array>) {
48+
return std::move(value).getObject(rt_).getArray(rt_);
49+
} else if constexpr (std::is_same_v<BaseT, jsi::Function>) {
50+
return std::move(value).getObject(rt_).getFunction(rt_);
51+
}
52+
} else {
53+
return std::move(value_);
54+
}
55+
}
56+
57+
template <
58+
typename U,
59+
std::enable_if_t<
60+
std::is_lvalue_reference_v<T> &&
61+
// Ensure non-reference type can be converted to the desired type.
62+
std::is_convertible_v<Converter<BaseT>, U>,
63+
int> = 0>
64+
operator U() && {
65+
return Converter<BaseT>(rt_, std::move(*this).operator BaseT());
3366
}
3467

68+
template <
69+
typename U,
70+
std::enable_if_t<is_jsi_v<T> && std::is_same_v<U, jsi::Value>, int> = 0>
71+
operator U() && = delete; // Prevent unwanted upcasting of JSI values.
72+
3573
protected:
3674
jsi::Runtime &rt_;
3775
T value_;
@@ -76,30 +114,6 @@ struct Converter<jsi::Object> : public ConverterBase<jsi::Object> {
76114
}
77115
};
78116

79-
template <typename T>
80-
struct Converter<T &> {
81-
Converter(jsi::Runtime &rt, T &value) : rt_(rt), value_(value) {}
82-
83-
operator T() && {
84-
// Copy the reference into a Value that then can be moved from.
85-
return Converter<jsi::Value>(rt_, jsi::Value(rt_, value_));
86-
}
87-
88-
template <
89-
typename U,
90-
// Ensure the non-reference type can be converted to the desired type.
91-
std::enable_if_t<
92-
std::is_convertible_v<Converter<std::remove_cv_t<T>>, U>,
93-
int> = 0>
94-
operator U() && {
95-
return Converter<jsi::Value>(rt_, jsi::Value(rt_, value_));
96-
}
97-
98-
private:
99-
jsi::Runtime &rt_;
100-
const T &value_;
101-
};
102-
103117
template <typename T, std::enable_if_t<is_jsi_v<T>, int> = 0>
104118
auto convert(jsi::Runtime &rt, T &&value) {
105119
return Converter<T>(rt, std::forward<T>(value));

ReactCommon/react/bridging/tests/BridgingTest.cpp

+95
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,99 @@ TEST_F(BridgingTest, pointerTest) {
299299
EXPECT_TRUE(bridging::toJs(rt, weak, invoker).isNull());
300300
}
301301

302+
TEST_F(BridgingTest, supportTest) {
303+
// Ensure sure can convert some basic types, including primitives that can be
304+
// trivially converted to JSI values.
305+
EXPECT_TRUE((bridging::supportsFromJs<bool>));
306+
EXPECT_TRUE((bridging::supportsFromJs<bool, bool>));
307+
EXPECT_TRUE((bridging::supportsFromJs<bool, jsi::Value &>));
308+
EXPECT_TRUE((bridging::supportsFromJs<int>));
309+
EXPECT_TRUE((bridging::supportsFromJs<int, int>));
310+
EXPECT_TRUE((bridging::supportsFromJs<int, jsi::Value &>));
311+
EXPECT_TRUE((bridging::supportsFromJs<double>));
312+
EXPECT_TRUE((bridging::supportsFromJs<double, double>));
313+
EXPECT_TRUE((bridging::supportsFromJs<double, jsi::Value &>));
314+
EXPECT_TRUE((bridging::supportsFromJs<std::string>));
315+
EXPECT_TRUE((bridging::supportsFromJs<std::string, jsi::String>));
316+
EXPECT_TRUE((bridging::supportsFromJs<std::string, jsi::String &>));
317+
EXPECT_TRUE((bridging::supportsFromJs<std::vector<int>, jsi::Array>));
318+
EXPECT_TRUE((bridging::supportsFromJs<std::vector<int>, jsi::Array &>));
319+
EXPECT_TRUE(
320+
(bridging::supportsFromJs<std::map<std::string, int>, jsi::Object>));
321+
EXPECT_TRUE(
322+
(bridging::supportsFromJs<std::map<std::string, int>, jsi::Object &>));
323+
324+
// Ensure incompatible conversions will fail.
325+
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::String>));
326+
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::String &>));
327+
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::String>));
328+
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::String &>));
329+
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::String>));
330+
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::String &>));
331+
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::Object>));
332+
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::Object &>));
333+
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::Object>));
334+
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::Object &>));
335+
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::Object>));
336+
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::Object &>));
337+
EXPECT_FALSE((bridging::supportsFromJs<std::string, jsi::Object>));
338+
EXPECT_FALSE((bridging::supportsFromJs<std::string, jsi::Object &>));
339+
EXPECT_FALSE((bridging::supportsFromJs<std::vector<int>, jsi::String>));
340+
EXPECT_FALSE((bridging::supportsFromJs<std::vector<int>, jsi::String &>));
341+
342+
// Ensure copying and up/down casting JSI values is also supported.
343+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Value>));
344+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Value, jsi::Value &>));
345+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Value, jsi::Object>));
346+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Value, jsi::Object &>));
347+
EXPECT_TRUE((bridging::supportsFromJs<jsi::String>));
348+
EXPECT_TRUE((bridging::supportsFromJs<jsi::String, jsi::String>));
349+
EXPECT_TRUE((bridging::supportsFromJs<jsi::String, jsi::String &>));
350+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object>));
351+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Object>));
352+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Object &>));
353+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Array>));
354+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Array &>));
355+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Function>));
356+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Function &>));
357+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array>));
358+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Array>));
359+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Array &>));
360+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Object>));
361+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Object &>));
362+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function>));
363+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Function>));
364+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Function &>));
365+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Object>));
366+
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Object &>));
367+
368+
// Ensure incorrect casts will fail.
369+
EXPECT_FALSE((bridging::supportsFromJs<jsi::Array, jsi::Function>));
370+
EXPECT_FALSE((bridging::supportsFromJs<jsi::Array, jsi::Function &>));
371+
EXPECT_FALSE((bridging::supportsFromJs<jsi::Function, jsi::Array>));
372+
EXPECT_FALSE((bridging::supportsFromJs<jsi::Function, jsi::Array &>));
373+
374+
// Ensure we can convert some basic types to JSI values.
375+
EXPECT_TRUE((bridging::supportsToJs<bool>));
376+
EXPECT_TRUE((bridging::supportsToJs<int>));
377+
EXPECT_TRUE((bridging::supportsToJs<double>));
378+
EXPECT_TRUE((bridging::supportsToJs<std::string>));
379+
EXPECT_TRUE((bridging::supportsToJs<std::string, jsi::String>));
380+
EXPECT_TRUE((bridging::supportsToJs<std::vector<int>>));
381+
EXPECT_TRUE((bridging::supportsToJs<std::vector<int>, jsi::Array>));
382+
EXPECT_TRUE((bridging::supportsToJs<std::map<std::string, int>>));
383+
EXPECT_TRUE(
384+
(bridging::supportsToJs<std::map<std::string, int>, jsi::Object>));
385+
EXPECT_TRUE((bridging::supportsToJs<void (*)()>));
386+
EXPECT_TRUE((bridging::supportsToJs<void (*)(), jsi::Function>));
387+
388+
// Ensure invalid conversions to JSI values are not supported.
389+
EXPECT_FALSE((bridging::supportsToJs<void *>));
390+
EXPECT_FALSE((bridging::supportsToJs<bool, jsi::Object>));
391+
EXPECT_FALSE((bridging::supportsToJs<int, jsi::Object>));
392+
EXPECT_FALSE((bridging::supportsToJs<double, jsi::Object>));
393+
EXPECT_FALSE((bridging::supportsToJs<std::string, jsi::Object>));
394+
EXPECT_FALSE((bridging::supportsToJs<std::vector<int>, jsi::Function>));
395+
}
396+
302397
} // namespace facebook::react

0 commit comments

Comments
 (0)