mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
202b17a149
Summary: Improve errors thrown when prop conversion fails by adding the property being converted. Removes the specialization of convertRawProp for std::optional since we can handle that in a fromRawValue specialization instead. To make this work, we need to remove noexcept from a number of calls. This noexcept behaviour was making these exceptions effectively uncatcheable. The original motivation of D23787492 (https://github.com/facebook/react-native/commit/57dd48b2464ac04b860f2f69cb4f131990fe4dbd) is correct, as we cannot reliably pass on exceptions to JS and assume that the state will be recoverable, so instead we log an error and carry on with the default value available. We should improve how the error gets reported to the user, as it will currently be hidden in logcat. Changelog: [Internal] Differential Revision: D39052812 fbshipit-source-id: 9692633930555e64a3769116fc484a19e534aa3f
148 lines
3.8 KiB
C++
148 lines
3.8 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <optional>
|
|
|
|
#include <folly/Likely.h>
|
|
#include <react/renderer/core/PropsParserContext.h>
|
|
#include <react/renderer/core/RawProps.h>
|
|
#include <react/renderer/core/RawPropsKey.h>
|
|
#include <react/renderer/graphics/Color.h>
|
|
#include <react/renderer/graphics/Geometry.h>
|
|
#include <react/renderer/graphics/conversions.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
/**
|
|
* Use this only when a prop update has definitely been sent from JS;
|
|
* essentially, cases where rawValue is virtually guaranteed to not be a
|
|
* nullptr.
|
|
*/
|
|
template <typename T>
|
|
void fromRawValue(
|
|
const PropsParserContext &context,
|
|
RawValue const &rawValue,
|
|
T &result,
|
|
T defaultValue) {
|
|
if (!rawValue.hasValue()) {
|
|
result = std::move(defaultValue);
|
|
return;
|
|
}
|
|
|
|
fromRawValue(context, rawValue, result);
|
|
}
|
|
|
|
template <typename T>
|
|
void fromRawValue(
|
|
const PropsParserContext &context,
|
|
RawValue const &rawValue,
|
|
T &result) {
|
|
result = (T)rawValue;
|
|
}
|
|
|
|
template <typename T>
|
|
void fromRawValue(
|
|
const PropsParserContext &context,
|
|
RawValue const &rawValue,
|
|
std::optional<T> &result) {
|
|
T resultValue;
|
|
fromRawValue(context, rawValue, resultValue);
|
|
result = std::optional<T>{std::move(resultValue)};
|
|
}
|
|
|
|
template <typename T>
|
|
void fromRawValue(
|
|
const PropsParserContext &context,
|
|
RawValue const &rawValue,
|
|
std::vector<T> &result) {
|
|
if (rawValue.hasType<std::vector<RawValue>>()) {
|
|
auto items = (std::vector<RawValue>)rawValue;
|
|
auto length = items.size();
|
|
result.clear();
|
|
result.reserve(length);
|
|
for (size_t i = 0; i < length; i++) {
|
|
T itemResult;
|
|
fromRawValue(context, items.at(i), itemResult);
|
|
result.push_back(itemResult);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// The case where `value` is not an array.
|
|
result.clear();
|
|
result.reserve(1);
|
|
T itemResult;
|
|
fromRawValue(context, rawValue, itemResult);
|
|
result.push_back(itemResult);
|
|
}
|
|
|
|
template <typename T>
|
|
void fromRawValue(
|
|
const PropsParserContext &context,
|
|
RawValue const &rawValue,
|
|
std::vector<std::vector<T>> &result) {
|
|
if (rawValue.hasType<std::vector<std::vector<RawValue>>>()) {
|
|
auto items = (std::vector<std::vector<RawValue>>)rawValue;
|
|
auto length = items.size();
|
|
result.clear();
|
|
result.reserve(length);
|
|
for (int i = 0; i < length; i++) {
|
|
T itemResult;
|
|
fromRawValue(context, items.at(i), itemResult);
|
|
result.push_back(itemResult);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// The case where `value` is not an array.
|
|
result.clear();
|
|
result.reserve(1);
|
|
T itemResult;
|
|
fromRawValue(context, rawValue, itemResult);
|
|
result.push_back(itemResult);
|
|
}
|
|
|
|
template <typename T, typename U = T>
|
|
T convertRawProp(
|
|
const PropsParserContext &context,
|
|
RawProps const &rawProps,
|
|
char const *name,
|
|
T const &sourceValue,
|
|
U const &defaultValue,
|
|
char const *namePrefix = nullptr,
|
|
char const *nameSuffix = nullptr) {
|
|
const auto *rawValue = rawProps.at(name, namePrefix, nameSuffix);
|
|
if (LIKELY(rawValue == nullptr)) {
|
|
return sourceValue;
|
|
}
|
|
|
|
// Special case: `null` always means "the prop was removed, use default
|
|
// value".
|
|
if (UNLIKELY(!rawValue->hasValue())) {
|
|
return defaultValue;
|
|
}
|
|
|
|
try {
|
|
T result;
|
|
fromRawValue(context, *rawValue, result);
|
|
return result;
|
|
} catch (const std::exception &e) {
|
|
// In case of errors, log the error and fall back to the default
|
|
RawPropsKey key{namePrefix, name, nameSuffix};
|
|
// TODO: report this using ErrorUtils so it's more visible to the user
|
|
LOG(ERROR) << "Error while converting prop '"
|
|
<< static_cast<std::string>(key) << "': " << e.what();
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|