Skip to content

Commit

Permalink
Add JSI<->C++ bridging conversion for folly::dynamic
Browse files Browse the repository at this point in the history
Summary:
Enables bridging conversions to/from jsi::Value and folly::dynamic.

Changelog:
[Internal] - Add JSI<->C++ bridging conversion for folly::dynamic

Differential Revision: D45544843

fbshipit-source-id: d1bae315d84d0d9841a54e73de5cba4ab87b13d0
  • Loading branch information
genkikondo authored and facebook-github-bot committed May 3, 2023
1 parent 1be65ba commit cdfa044
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <react/bridging/Array.h>
#include <react/bridging/Bool.h>
#include <react/bridging/Class.h>
#include <react/bridging/Dynamic.h>
#include <react/bridging/Error.h>
#include <react/bridging/Function.h>
#include <react/bridging/Number.h>
Expand Down
85 changes: 85 additions & 0 deletions packages/react-native/ReactCommon/react/bridging/Dynamic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 <folly/dynamic.h>
#include <react/bridging/Number.h>

namespace facebook::react {

template <>
struct Bridging<folly::dynamic> {
static folly::dynamic fromJs(jsi::Runtime &rt, const jsi::Value &value) {
if (value.isNull()) {
return nullptr;
} else if (value.isBool()) {
return value.asBool();
} else if (value.isNumber()) {
return value.asNumber();
} else if (value.isString()) {
return value.asString(rt).utf8(rt);
} else if (value.isObject()) {
auto obj = value.asObject(rt);
if (obj.isArray(rt)) {
folly::dynamic result = folly::dynamic::array();
auto array = obj.asArray(rt);
auto length = array.size(rt);
for (auto i = 0; i < length; ++i) {
result.push_back(Bridging<folly::dynamic>::fromJs(
rt, array.getValueAtIndex(rt, i)));
}
return result;
} else {
folly::dynamic result = folly::dynamic::object();
auto propertyNames = obj.getPropertyNames(rt);
auto length = propertyNames.length(rt);
for (auto i = 0; i < length; i++) {
auto propertyName = propertyNames.getValueAtIndex(rt, i);
result[Bridging<folly::dynamic>::fromJs(rt, propertyName)] =
Bridging<folly::dynamic>::fromJs(
rt, obj.getProperty(rt, propertyName.asString(rt)));
}
return result;
}
}
return nullptr;
}

static jsi::Value toJs(jsi::Runtime &rt, folly::dynamic value) {
if (value.isNull()) {
return jsi::Value::null();
} else if (value.isBool()) {
return Bridging<bool>::toJs(rt, value.asBool());
} else if (value.isNumber()) {
return Bridging<double>::toJs(rt, value.asDouble());
} else if (value.isString()) {
return Bridging<std::string>::toJs(rt, value.asString());
} else if (value.isArray()) {
auto result = jsi::Array(rt, value.size());
for (auto i = 0; i < value.size(); ++i) {
result.setValueAtIndex(
rt, i, Bridging<folly::dynamic>::toJs(rt, value[i]));
}
return result;
} else if (value.isObject()) {
auto result = jsi::Object(rt);
for (auto &pair : value.items()) {
if (pair.first.isString()) {
result.setProperty(
rt,
pair.first.asString().c_str(),
Bridging<folly::dynamic>::toJs(rt, pair.second));
}
}
return result;
}
return jsi::Value::undefined();
}
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -532,4 +532,90 @@ TEST_F(BridgingTest, supportTest) {
EXPECT_FALSE((bridging::supportsToJs<std::vector<int>, jsi::Function>));
}

TEST_F(BridgingTest, dynamicTest) {
// Null
auto nullFromJsResult =
bridging::fromJs<folly::dynamic>(rt, jsi::Value::null(), invoker);
EXPECT_TRUE(nullFromJsResult.isNull());

auto nullToJsResult = bridging::toJs<folly::dynamic>(rt, nullptr, invoker);
EXPECT_TRUE(nullToJsResult.isNull());

// Boolean
auto booleanFromJsResult =
bridging::fromJs<folly::dynamic>(rt, jsi::Value(true), invoker);
EXPECT_TRUE(booleanFromJsResult.isBool());
EXPECT_TRUE(booleanFromJsResult.asBool());

auto booleanToJsResult = bridging::toJs<folly::dynamic>(rt, true, invoker);
EXPECT_TRUE(booleanToJsResult.isBool());
EXPECT_TRUE(booleanToJsResult.asBool());

// Number
auto numberFromJsResult =
bridging::fromJs<folly::dynamic>(rt, jsi::Value(1.2), invoker);
EXPECT_TRUE(numberFromJsResult.isNumber());
EXPECT_DOUBLE_EQ(1.2, numberFromJsResult.asDouble());

auto numberToJsResult = bridging::toJs<folly::dynamic>(rt, 1.2, invoker);
EXPECT_TRUE(numberToJsResult.isNumber());
EXPECT_DOUBLE_EQ(1.2, numberToJsResult.asNumber());

// String
auto stringFromJsResult = bridging::fromJs<folly::dynamic>(
rt, jsi::Value(jsi::String::createFromAscii(rt, "hello")), invoker);
EXPECT_TRUE(stringFromJsResult.isString());
EXPECT_EQ("hello"s, stringFromJsResult.asString());

auto stringToJsResult = bridging::toJs<folly::dynamic>(rt, "hello", invoker);
EXPECT_TRUE(stringToJsResult.isString());
EXPECT_EQ("hello"s, stringToJsResult.asString(rt).utf8(rt));

// Array
auto arrayFromJsResult = bridging::fromJs<folly::dynamic>(
rt,
jsi::Value(jsi::Array::createWithElements(rt, "foo", "bar")),
invoker);
EXPECT_TRUE(arrayFromJsResult.isArray());
EXPECT_EQ(2, arrayFromJsResult.size());
EXPECT_EQ("foo"s, arrayFromJsResult[0].asString());
EXPECT_EQ("bar"s, arrayFromJsResult[1].asString());

auto arrayToJsResult = bridging::toJs<folly::dynamic>(
rt, folly::dynamic::array("foo", "bar"), invoker);
EXPECT_TRUE(arrayToJsResult.isObject());
EXPECT_TRUE(arrayToJsResult.asObject(rt).isArray(rt));
auto arrayToJsResultArray = arrayToJsResult.asObject(rt).asArray(rt);
EXPECT_EQ(2, arrayToJsResultArray.size(rt));
EXPECT_EQ(
"foo"s,
arrayToJsResultArray.getValueAtIndex(rt, 0).asString(rt).utf8(rt));
EXPECT_EQ(
"bar"s,
arrayToJsResultArray.getValueAtIndex(rt, 1).asString(rt).utf8(rt));

// Object
auto jsiObject = jsi::Object(rt);
jsiObject.setProperty(rt, "foo", "bar");
auto objectFromJsResult = bridging::fromJs<folly::dynamic>(
rt, jsi::Value(std::move(jsiObject)), invoker);

EXPECT_TRUE(objectFromJsResult.isObject());
EXPECT_EQ(1, objectFromJsResult.size());
EXPECT_EQ("bar"s, objectFromJsResult["foo"].asString());

auto objectToJsResult = bridging::toJs<folly::dynamic>(
rt, folly::dynamic::object("foo", "bar"), invoker);
EXPECT_TRUE(objectToJsResult.isObject());
auto objectToJsResultObject = objectToJsResult.asObject(rt);
EXPECT_EQ(
"bar"s,
objectToJsResultObject.getProperty(rt, "foo").asString(rt).utf8(rt));

// Undefined
auto undefinedFromJsResult =
bridging::fromJs<folly::dynamic>(rt, jsi::Value::undefined(), invoker);
EXPECT_TRUE(undefinedFromJsResult.isNull());
}

} // namespace facebook::react

0 comments on commit cdfa044

Please sign in to comment.