Skip to content

Commit b5eec4e

Browse files
mrousavymeta-codesync[bot]
authored andcommitted
Forward createValueFromJsonUtf8 in RuntimeDecorator (#58960)
Summary: Pull Request resolved: #58960 `RuntimeDecorator` doesn't forward `createValueFromJsonUtf8` to the plain runtime, so decorated runtimes use the `JSON.parse`-based default from jsi.cpp instead of the plain runtime's parser. For Hermes that means `makeThreadSafeHermesRuntime` and anything built on `WithRuntimeDecorator` never reach the native parser from a3f615cec. I think this was missed rather than chosen. `WithRuntimeDecorator` already overrides it (added in c2a0da2ae as `Around` plus `RD::createValueFromJsonUtf8`), which only makes sense if `RuntimeDecorator` forwards it. Today that `RD::` call lands in the default, so it takes one `Around` plus several nested ones for `global`, `getProperty`, `createStringFromUtf8` and `call`. It's also observable: on a thread-safe runtime, after `JSON.parse = () => 'hijacked'`, `Value::createFromJsonUtf8` returns `'hijacked'`, and after `delete globalThis.JSON` it throws. A plain `HermesRuntime` parses normally in both cases. `TracingRuntime` relies on the current behavior. JSON parsing only shows up in traces because the default splits into traced `global`/`getProperty`/`call` calls. Forwarding alone would hand back a value from the plain runtime with no def, and replay would hit the "has no def" assert in `useObjectID`. So `TracingRuntime` now overrides `createValueFromJsonUtf8` and calls `jsi::Runtime::createValueFromJsonUtf8` explicitly. That runs the same code as before, so traces are unchanged and there's no new record type. If you'd prefer a dedicated SynthTrace record I can do that instead. A side effect: decorators that watched JSON parsing through `global`/`getProperty`/`call` now need to override `createValueFromJsonUtf8`, like every other forwarded method. For example, `TimedRuntime` no longer counts JSON parsing under its `call` timer. The other methods decorators don't forward yet are the `createError*` family (facebook/hermes#2203) and `String::length` (facebook/hermes#2205). React Native's `decorator.h` has the same gap. X-link: facebook/hermes#2206 Test Plan: New tests: - `JSITest.CreateValueFromJsonUtf8Test` checks that parsing through a `RuntimeDecorator` reaches the wrapped runtime, and keeps coverage of the jsi.cpp default through a decorator that overrides back to it. It calls `outer.createValueFromJsonUtf8` directly to check that the override remains public. - `HermesRuntimeTest.CreateValueFromJsonUtf8IgnoresGlobalJSON` replaces `JSON.parse` and checks that parsing still works, on the plain and thread-safe runtimes. - `SynthTraceReplayTest.CreateValueFromJsonUtf8Replay` parses on the tracing runtime, uses the result from native code, and replays with verification. Without the change the first two fail (the second only on the thread-safe runtime). With only the `decorator.h` part, the SynthTrace test aborts in `useObjectID`. With the full change all pass, and so does the full `APITests` binary (360 tests) in a Debug + ASan build. `APILeanTests` also passes. The direct call fails to compile with the previous header because `createValueFromJsonUtf8` is protected. With this revision, `cmake --build cmake-build-asan --target check-hermes -j 12` passes in Debug + ASan: 4,126 expected passes, 6 expected failures, and 197 unsupported tests. Node-API checks: 41 passes and 23 skips. Reviewed By: javache Differential Revision: D124177263 Pulled By: lavenzg fbshipit-source-id: 973dc8b3c69002a81e634bb6c992f5f167e13bf2
1 parent d4d0813 commit b5eec4e

14 files changed

Lines changed: 63 additions & 0 deletions

‎packages/react-native/ReactCommon/jsi/jsi/decorator.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
148148
Instrumentation& instrumentation() override {
149149
return *this;
150150
}
151+
Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
152+
return plain_.createValueFromJsonUtf8(json, length);
153+
}
151154

152155
Value createError(const String& msg) override {
153156
return plain().createError(msg);

‎packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,54 @@ TEST_P(JSITest, CreateFromUtf16Test) {
21442144
EXPECT_EQ(cp, 55357); // 0xD83D in decimal
21452145
}
21462146

2147+
TEST_P(JSITest, CreateValueFromJsonUtf8Test) {
2148+
uint8_t jsonText[] = "{\"a\": 1}";
2149+
2150+
// This Runtime Decorator is used to test the default createValueFromJsonUtf8
2151+
// implementation for VMs that do not provide their own implementation
2152+
class DefaultRD : public RuntimeDecorator<Runtime, Runtime> {
2153+
public:
2154+
explicit DefaultRD(Runtime& rt) : RuntimeDecorator(rt) {}
2155+
2156+
Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
2157+
return Runtime::createValueFromJsonUtf8(json, length);
2158+
}
2159+
};
2160+
2161+
DefaultRD defaultRd(rt);
2162+
Object obj =
2163+
Value::createFromJsonUtf8(defaultRd, jsonText, sizeof(jsonText) - 1)
2164+
.getObject(defaultRd);
2165+
EXPECT_EQ(obj.getProperty(defaultRd, "a").getNumber(), 1);
2166+
2167+
// A RuntimeDecorator must forward createValueFromJsonUtf8 to the runtime it
2168+
// decorates, like every other method.
2169+
class CountingRD : public RuntimeDecorator<Runtime, Runtime> {
2170+
public:
2171+
explicit CountingRD(Runtime& rt) : RuntimeDecorator(rt) {}
2172+
2173+
Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
2174+
++count;
2175+
return plain().createValueFromJsonUtf8(json, length);
2176+
}
2177+
2178+
int count = 0;
2179+
};
2180+
2181+
// Overrides nothing, so it should forward everything to CountingRD.
2182+
class OuterRD : public RuntimeDecorator<Runtime, Runtime> {
2183+
public:
2184+
explicit OuterRD(Runtime& rt) : RuntimeDecorator(rt) {}
2185+
};
2186+
2187+
CountingRD counting(rt);
2188+
OuterRD outer(counting);
2189+
obj = outer.createValueFromJsonUtf8(jsonText, sizeof(jsonText) - 1)
2190+
.getObject(outer);
2191+
EXPECT_EQ(counting.count, 1);
2192+
EXPECT_EQ(obj.getProperty(outer, "a").getNumber(), 1);
2193+
}
2194+
21472195
TEST_P(JSITest, GetStringDataTest) {
21482196
// This Runtime Decorator is used to test the default getStringData
21492197
// implementation for VMs that do not provide their own implementation

‎scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6198,6 +6198,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
61986198
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
61996199
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
62006200
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
6201+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
62016202
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
62026203
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
62036204
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10553,6 +10553,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
1055310553
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
1055410554
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
1055510555
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
10556+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
1055610557
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
1055710558
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
1055810559
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6047,6 +6047,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
60476047
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
60486048
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
60496049
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
6050+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
60506051
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
60516052
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
60526053
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6047,6 +6047,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
60476047
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
60486048
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
60496049
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
6050+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
60506051
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
60516052
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
60526053
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8009,6 +8009,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
80098009
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
80108010
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
80118011
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
8012+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
80128013
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
80138014
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
80148015
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12396,6 +12396,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
1239612396
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
1239712397
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
1239812398
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
12399+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
1239912400
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
1240012401
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
1240112402
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7860,6 +7860,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
78607860
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
78617861
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
78627862
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
7863+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
78637864
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
78647865
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
78657866
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

‎scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7868,6 +7868,7 @@ class facebook::jsi::RuntimeDecorator : public facebook::jsi::Runtime {
78687868
public virtual facebook::jsi::Value createSyntaxError(const facebook::jsi::String& msg) override;
78697869
public virtual facebook::jsi::Value createTypeError(const facebook::jsi::String& msg) override;
78707870
public virtual facebook::jsi::Value createURIError(const facebook::jsi::String& msg) override;
7871+
public virtual facebook::jsi::Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override;
78717872
public virtual facebook::jsi::Value evaluateJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, const std::string& sourceURL) override;
78727873
public virtual facebook::jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript>& js) override;
78737874
public virtual std::shared_ptr<const facebook::jsi::PreparedJavaScript> prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer>& buffer, std::string sourceURL) override;

0 commit comments

Comments
 (0)