Skip to content

Commit

Permalink
Add key to prop conversion errors
Browse files Browse the repository at this point in the history
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 (57dd48b) 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
  • Loading branch information
javache authored and facebook-github-bot committed Aug 30, 2022
1 parent 9fac885 commit 202b17a
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 49 deletions.
24 changes: 10 additions & 14 deletions ReactCommon/react/renderer/core/RawValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class RawValue {
* Casts the value to a specified type.
*/
template <typename T>
explicit operator T() const noexcept {
explicit operator T() const {
return castValue(dynamic_, (T *)nullptr);
}

Expand Down Expand Up @@ -212,40 +212,36 @@ class RawValue {
return RawValue(dynamic);
}

static bool castValue(const folly::dynamic &dynamic, bool *type) noexcept {
static bool castValue(const folly::dynamic &dynamic, bool *type) {
return dynamic.getBool();
}

static int castValue(const folly::dynamic &dynamic, int *type) noexcept {
static int castValue(const folly::dynamic &dynamic, int *type) {
return static_cast<int>(dynamic.asInt());
}

static int64_t castValue(
const folly::dynamic &dynamic,
int64_t *type) noexcept {
static int64_t castValue(const folly::dynamic &dynamic, int64_t *type) {
return dynamic.asInt();
}

static float castValue(const folly::dynamic &dynamic, float *type) noexcept {
static float castValue(const folly::dynamic &dynamic, float *type) {
return static_cast<float>(dynamic.asDouble());
}

static double castValue(
const folly::dynamic &dynamic,
double *type) noexcept {
static double castValue(const folly::dynamic &dynamic, double *type) {
return dynamic.asDouble();
}

static std::string castValue(
const folly::dynamic &dynamic,
std::string *type) noexcept {
std::string *type) {
return dynamic.getString();
}

template <typename T>
static std::vector<T> castValue(
const folly::dynamic &dynamic,
std::vector<T> *type) noexcept {
std::vector<T> *type) {
react_native_assert(dynamic.isArray());
auto result = std::vector<T>{};
result.reserve(dynamic.size());
Expand All @@ -258,7 +254,7 @@ class RawValue {
template <typename T>
static std::vector<std::vector<T>> castValue(
const folly::dynamic &dynamic,
std::vector<std::vector<T>> *type) noexcept {
std::vector<std::vector<T>> *type) {
react_native_assert(dynamic.isArray());
auto result = std::vector<std::vector<T>>{};
result.reserve(dynamic.size());
Expand All @@ -271,7 +267,7 @@ class RawValue {
template <typename T>
static butter::map<std::string, T> castValue(
const folly::dynamic &dynamic,
butter::map<std::string, T> *type) noexcept {
butter::map<std::string, T> *type) {
react_native_assert(dynamic.isObject());
auto result = butter::map<std::string, T>{};
for (const auto &item : dynamic.items()) {
Expand Down
52 changes: 17 additions & 35 deletions ReactCommon/react/renderer/core/propsConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
#include <optional>

#include <folly/Likely.h>
#include <folly/dynamic.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>
Expand Down Expand Up @@ -43,18 +43,18 @@ template <typename T>
void fromRawValue(
const PropsParserContext &context,
RawValue const &rawValue,
std::optional<T> &result) {
T res{};
fromRawValue(context, rawValue, res);
result = std::optional<T>(res);
T &result) {
result = (T)rawValue;
}

template <typename T>
void fromRawValue(
const PropsParserContext &context,
RawValue const &rawValue,
T &result) {
result = (T)rawValue;
std::optional<T> &result) {
T resultValue;
fromRawValue(context, rawValue, resultValue);
result = std::optional<T>{std::move(resultValue)};
}

template <typename T>
Expand Down Expand Up @@ -119,7 +119,6 @@ T convertRawProp(
char const *namePrefix = nullptr,
char const *nameSuffix = nullptr) {
const auto *rawValue = rawProps.at(name, namePrefix, nameSuffix);

if (LIKELY(rawValue == nullptr)) {
return sourceValue;
}
Expand All @@ -130,35 +129,18 @@ T convertRawProp(
return defaultValue;
}

T result;
fromRawValue(context, *rawValue, result);
return result;
}

template <typename T>
static std::optional<T> convertRawProp(
const PropsParserContext &context,
RawProps const &rawProps,
char const *name,
std::optional<T> const &sourceValue,
std::optional<T> 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())) {
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;
}

T result;
fromRawValue(context, *rawValue, result);
return std::optional<T>{result};
}

} // namespace react
Expand Down

0 comments on commit 202b17a

Please sign in to comment.