Skip to content

Commit a8c90e6

Browse files
mhorowitzfacebook-github-bot
authored andcommitted
Make conversions between folly::dynamic and JSI non-recursive
Summary: Changelog: [General][Fixed] - Fix handling of very deeply nested data across the bridge fixes facebook/hermes#339 Reviewed By: sammy-SC Differential Revision: D24182938 fbshipit-source-id: b674283a112b98cc63f20e436c538e3789ddf6dd
1 parent 9218e0c commit a8c90e6

File tree

1 file changed

+136
-40
lines changed

1 file changed

+136
-40
lines changed

ReactCommon/jsi/jsi/JSIDynamic.cpp

+136-40
Original file line numberDiff line numberDiff line change
@@ -17,72 +17,163 @@ using namespace facebook::jsi;
1717
namespace facebook {
1818
namespace jsi {
1919

20-
Value valueFromDynamic(Runtime& runtime, const folly::dynamic& dyn) {
20+
namespace {
21+
22+
struct FromDynamic {
23+
FromDynamic(const folly::dynamic* dynArg, Object objArg)
24+
: dyn(dynArg), obj(std::move(objArg)) {}
25+
26+
const folly::dynamic* dyn;
27+
Object obj;
28+
};
29+
30+
// This converts one element. If it's a collection, it gets pushed onto
31+
// the stack for later processing.
32+
Value valueFromDynamicShallow(
33+
Runtime& runtime,
34+
std::vector<FromDynamic>& stack,
35+
const folly::dynamic& dyn) {
2136
switch (dyn.type()) {
2237
case folly::dynamic::NULLT:
2338
return Value::null();
2439
case folly::dynamic::ARRAY: {
25-
Array ret = Array(runtime, dyn.size());
26-
for (size_t i = 0; i < dyn.size(); ++i) {
27-
ret.setValueAtIndex(runtime, i, valueFromDynamic(runtime, dyn[i]));
28-
}
29-
return std::move(ret);
40+
Object arr = Array(runtime, dyn.size());
41+
Value ret = Value(runtime, arr);
42+
stack.emplace_back(&dyn, std::move(arr));
43+
return ret;
3044
}
3145
case folly::dynamic::BOOL:
32-
return dyn.getBool();
46+
return Value(dyn.getBool());
3347
case folly::dynamic::DOUBLE:
3448
return dyn.getDouble();
3549
case folly::dynamic::INT64:
36-
// Can't use asDouble() here. If the int64 value is too bit to be
37-
// represented precisely as a double, folly will throw an
38-
// exception.
39-
return (double)dyn.getInt();
50+
return Value((double)dyn.getInt());
4051
case folly::dynamic::OBJECT: {
41-
Object ret(runtime);
42-
for (const auto& element : dyn.items()) {
43-
Value value = valueFromDynamic(runtime, element.second);
44-
if (element.first.isNumber() || element.first.isString()) {
45-
ret.setProperty(
52+
auto obj = Object(runtime);
53+
Value ret = Value(runtime, obj);
54+
stack.emplace_back(&dyn, std::move(obj));
55+
return ret;
56+
}
57+
case folly::dynamic::STRING:
58+
return Value(String::createFromUtf8(runtime, dyn.getString()));
59+
}
60+
CHECK(false);
61+
}
62+
63+
} // namespace
64+
65+
Value valueFromDynamic(Runtime& runtime, const folly::dynamic& dynInput) {
66+
std::vector<FromDynamic> stack;
67+
68+
Value ret = valueFromDynamicShallow(runtime, stack, dynInput);
69+
70+
while (!stack.empty()) {
71+
auto top = std::move(stack.back());
72+
stack.pop_back();
73+
74+
switch (top.dyn->type()) {
75+
case folly::dynamic::ARRAY: {
76+
Array arr = std::move(top.obj).getArray(runtime);
77+
for (size_t i = 0; i < top.dyn->size(); ++i) {
78+
arr.setValueAtIndex(
4679
runtime,
47-
PropNameID::forUtf8(runtime, element.first.asString()),
48-
value);
80+
i,
81+
valueFromDynamicShallow(runtime, stack, (*top.dyn)[i]));
82+
}
83+
break;
84+
}
85+
case folly::dynamic::OBJECT: {
86+
Object obj = std::move(top.obj);
87+
for (const auto& element : top.dyn->items()) {
88+
if (element.first.isNumber() || element.first.isString()) {
89+
obj.setProperty(
90+
runtime,
91+
PropNameID::forUtf8(runtime, element.first.asString()),
92+
valueFromDynamicShallow(runtime, stack, element.second));
93+
}
4994
}
95+
break;
5096
}
51-
return std::move(ret);
97+
default:
98+
CHECK(false);
5299
}
53-
case folly::dynamic::STRING:
54-
return String::createFromUtf8(runtime, dyn.getString());
55100
}
56-
CHECK(false);
101+
102+
return ret;
57103
}
58104

59-
folly::dynamic dynamicFromValue(Runtime& runtime, const Value& value) {
105+
namespace {
106+
107+
struct FromValue {
108+
FromValue(folly::dynamic* dynArg, Object objArg)
109+
: dyn(dynArg), obj(std::move(objArg)) {}
110+
111+
folly::dynamic* dyn;
112+
Object obj;
113+
};
114+
115+
// This converts one element. If it's a collection, it gets pushed
116+
// onto the stack for later processing. The output is created by
117+
// mutating the output argument, because we need its actual pointer to
118+
// push onto the stack.
119+
void dynamicFromValueShallow(
120+
Runtime& runtime,
121+
std::vector<FromValue>& stack,
122+
const jsi::Value& value,
123+
folly::dynamic& output) {
60124
if (value.isUndefined() || value.isNull()) {
61-
return nullptr;
125+
output = nullptr;
62126
} else if (value.isBool()) {
63-
return value.getBool();
127+
output = value.getBool();
64128
} else if (value.isNumber()) {
65-
return value.getNumber();
129+
output = value.getNumber();
66130
} else if (value.isString()) {
67-
return value.getString(runtime).utf8(runtime);
131+
output = value.getString(runtime).utf8(runtime);
68132
} else {
133+
CHECK(value.isObject());
69134
Object obj = value.getObject(runtime);
70135
if (obj.isArray(runtime)) {
71-
Array array = obj.getArray(runtime);
72-
folly::dynamic ret = folly::dynamic::array();
73-
for (size_t i = 0; i < array.size(runtime); ++i) {
74-
ret.push_back(
75-
dynamicFromValue(runtime, array.getValueAtIndex(runtime, i)));
76-
}
77-
return ret;
136+
output = folly::dynamic::array();
78137
} else if (obj.isFunction(runtime)) {
79138
throw JSError(runtime, "JS Functions are not convertible to dynamic");
80139
} else {
81-
folly::dynamic ret = folly::dynamic::object();
82-
Array names = obj.getPropertyNames(runtime);
140+
output = folly::dynamic::object();
141+
}
142+
stack.emplace_back(&output, std::move(obj));
143+
}
144+
}
145+
146+
} // namespace
147+
148+
folly::dynamic dynamicFromValue(Runtime& runtime, const Value& valueInput) {
149+
std::vector<FromValue> stack;
150+
folly::dynamic ret;
151+
152+
dynamicFromValueShallow(runtime, stack, valueInput, ret);
153+
154+
while (!stack.empty()) {
155+
auto top = std::move(stack.back());
156+
stack.pop_back();
157+
158+
if (top.obj.isArray(runtime)) {
159+
// Inserting into a dyn can invalidate references into it, so we
160+
// need to insert new elements up front, then push stuff onto
161+
// the stack.
162+
Array array = top.obj.getArray(runtime);
163+
size_t arraySize = array.size(runtime);
164+
for (size_t i = 0; i < arraySize; ++i) {
165+
top.dyn->push_back(nullptr);
166+
}
167+
for (size_t i = 0; i < arraySize; ++i) {
168+
dynamicFromValueShallow(
169+
runtime, stack, array.getValueAtIndex(runtime, i), top.dyn->at(i));
170+
}
171+
} else {
172+
Array names = top.obj.getPropertyNames(runtime);
173+
std::vector<std::pair<std::string, jsi::Value>> props;
83174
for (size_t i = 0; i < names.size(runtime); ++i) {
84175
String name = names.getValueAtIndex(runtime, i).getString(runtime);
85-
Value prop = obj.getProperty(runtime, name);
176+
Value prop = top.obj.getProperty(runtime, name);
86177
if (prop.isUndefined()) {
87178
continue;
88179
}
@@ -92,12 +183,17 @@ folly::dynamic dynamicFromValue(Runtime& runtime, const Value& value) {
92183
if (prop.isObject() && prop.getObject(runtime).isFunction(runtime)) {
93184
prop = Value::null();
94185
}
95-
ret.insert(
96-
name.utf8(runtime), dynamicFromValue(runtime, std::move(prop)));
186+
props.emplace_back(name.utf8(runtime), std::move(prop));
187+
top.dyn->insert(props.back().first, nullptr);
188+
}
189+
for (const auto& prop : props) {
190+
dynamicFromValueShallow(
191+
runtime, stack, prop.second, (*top.dyn)[prop.first]);
97192
}
98-
return ret;
99193
}
100194
}
195+
196+
return ret;
101197
}
102198

103199
} // namespace jsi

0 commit comments

Comments
 (0)