feat(android): allow passing filter function for prop folly::dynamic conversion (#48202)

Summary:
### Motivation

- We need to exclude certain prop keys from conversion to `folly::dynamic` on android for our custom use case where we pass down `jsi::object`s with NativeState attached down the props

Otherwise we run into crashes such as:

![CleanShot 2024-12-11 at 09 18 26@2x](https://github.com/user-attachments/assets/b460187e-5442-4547-ae36-ffd188f444f2)

### Changes

- `dynamicFromValue` was marked as `noexcept` although it can throw, I removed the `noexcept` for correctness
- Made it so you can pass down a filter function to exclude certain props from conversion (using the existing mechanism for that)
	- I think there is no way to pass a filter function and retain it in `RawProps` as that is constructed very early on in `UIManagerBinding`

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[INTERNAL] [ADDED] - Allow passing a filter function to `BaseViewProps` to exclude certain props on android from being dynamically casted

Pull Request resolved: https://github.com/facebook/react-native/pull/48202

Test Plan:
You can try modifying for example `ScrollViewProps.cpp` and pass a fourth argument to `ViewProps` to confirm that the filtering is working:

```cpp
ScrollViewProps::ScrollViewProps(
    const PropsParserContext& context,
    const ScrollViewProps& sourceProps,
    const RawProps& rawProps)
    : ViewProps(context, sourceProps, rawProps, [&](const std::string& keyName){
        return true;
    }),
```

Reviewed By: NickGerleman

Differential Revision: D67088540

Pulled By: javache

fbshipit-source-id: ed8cf5d773d357dfc54553f5ccf7adf27c781d56
This commit is contained in:
Hanno J. Gödecke
2024-12-12 11:38:36 -08:00
committed by Facebook GitHub Bot
parent 1763321c89
commit feff4a7556
10 changed files with 74 additions and 20 deletions
@@ -53,8 +53,9 @@ std::array<float, 3> getTranslateForTransformOrigin(
BaseViewProps::BaseViewProps(
const PropsParserContext& context,
const BaseViewProps& sourceProps,
const RawProps& rawProps)
: YogaStylableProps(context, sourceProps, rawProps),
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys)
: YogaStylableProps(context, sourceProps, rawProps, filterObjectKeys),
AccessibilityProps(context, sourceProps, rawProps),
opacity(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
@@ -31,7 +31,9 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
BaseViewProps(
const PropsParserContext& context,
const BaseViewProps& sourceProps,
const RawProps& rawProps);
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys =
nullptr);
void setProp(
const PropsParserContext& context,
@@ -18,9 +18,10 @@ namespace facebook::react {
YogaStylableProps::YogaStylableProps(
const PropsParserContext& context,
const YogaStylableProps& sourceProps,
const RawProps& rawProps)
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys)
: Props() {
initialize(context, sourceProps, rawProps);
initialize(context, sourceProps, rawProps, filterObjectKeys);
yogaStyle.setDirection(convertRawProp(
context,
@@ -21,7 +21,9 @@ class YogaStylableProps : public Props {
YogaStylableProps(
const PropsParserContext& context,
const YogaStylableProps& sourceProps,
const RawProps& rawProps);
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys =
nullptr);
void setProp(
const PropsParserContext& context,
@@ -20,8 +20,9 @@ namespace facebook::react {
HostPlatformViewProps::HostPlatformViewProps(
const PropsParserContext& context,
const HostPlatformViewProps& sourceProps,
const RawProps& rawProps)
: BaseViewProps(context, sourceProps, rawProps),
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys)
: BaseViewProps(context, sourceProps, rawProps, filterObjectKeys),
elevation(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.elevation
@@ -26,7 +26,9 @@ class HostPlatformViewProps : public BaseViewProps {
HostPlatformViewProps(
const PropsParserContext& context,
const HostPlatformViewProps& sourceProps,
const RawProps& rawProps);
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys =
nullptr);
void setProp(
const PropsParserContext& context,
@@ -17,19 +17,22 @@ namespace facebook::react {
Props::Props(
const PropsParserContext& context,
const Props& sourceProps,
const RawProps& rawProps) {
initialize(context, sourceProps, rawProps);
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys) {
initialize(context, sourceProps, rawProps, filterObjectKeys);
}
void Props::initialize(
const PropsParserContext& context,
const Props& sourceProps,
const RawProps& rawProps) {
const RawProps& rawProps,
[[maybe_unused]] const std::function<bool(const std::string&)>&
filterObjectKeys) {
nativeId = ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.nativeId
: convertRawProp(context, rawProps, "nativeID", sourceProps.nativeId, {});
#ifdef ANDROID
this->rawProps = (folly::dynamic)rawProps;
this->rawProps = rawProps.toDynamic(filterObjectKeys);
#endif
}
@@ -33,7 +33,9 @@ class Props : public virtual Sealable, public virtual DebugStringConvertible {
Props(
const PropsParserContext& context,
const Props& sourceProps,
const RawProps& rawProps);
const RawProps& rawProps,
const std::function<bool(const std::string&)>& filterObjectKeys =
nullptr);
virtual ~Props() = default;
Props(const Props& other) = delete;
@@ -71,7 +73,13 @@ class Props : public virtual Sealable, public virtual DebugStringConvertible {
void initialize(
const PropsParserContext& context,
const Props& sourceProps,
const RawProps& rawProps);
const RawProps& rawProps,
/**
* Filter object keys to be excluded when converting the RawProps to
* folly::dynamic (android only)
*/
const std::function<bool(const std::string&)>& filterObjectKeys =
nullptr);
};
} // namespace facebook::react
@@ -172,13 +172,38 @@ void RawProps::parse(const RawPropsParser& parser) noexcept {
* The support for explicit conversion to `folly::dynamic` is deprecated and
* will be removed as soon Android implementation does not need it.
*/
RawProps::operator folly::dynamic() const noexcept {
RawProps::operator folly::dynamic() const {
return toDynamic();
}
/*
* Deprecated. Do not use.
* The support for explicit conversion to `folly::dynamic` is deprecated and
* will be removed as soon Android implementation does not need it.
*/
folly::dynamic RawProps::toDynamic(
const std::function<bool(const std::string&)>& filterObjectKeys) const {
switch (mode_) {
case Mode::Empty:
return folly::dynamic::object();
case Mode::JSI:
return jsi::dynamicFromValue(
*runtime_, value_, ignoreYogaStyleProps_ ? isYogaStyleProp : nullptr);
case Mode::JSI: {
if (ignoreYogaStyleProps_ || filterObjectKeys != nullptr) {
// We need to filter props
return jsi::dynamicFromValue(
*runtime_, value_, [&](const std::string& key) {
if (ignoreYogaStyleProps_ && isYogaStyleProp(key)) {
return true;
}
if (filterObjectKeys) {
return filterObjectKeys(key);
}
return false;
});
} else {
// We don't need to filter, just include all props by default
return jsi::dynamicFromValue(*runtime_, value_, nullptr);
}
}
case Mode::Dynamic:
return dynamic_;
}
@@ -71,7 +71,16 @@ class RawProps final {
* The support for explicit conversion to `folly::dynamic` is deprecated and
* will be removed as soon Android implementation does not need it.
*/
explicit operator folly::dynamic() const noexcept;
explicit operator folly::dynamic() const;
/*
* Deprecated. Do not use.
* The support for explicit conversion to `folly::dynamic` is deprecated and
* will be removed as soon Android implementation does not need it.
*/
folly::dynamic toDynamic(
const std::function<bool(const std::string&)>& filterObjectKeys =
nullptr) const;
/*
* Once called, Yoga style props will be filtered out during conversion to