Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,14 @@ jsi::String TracingRuntime::createStringFromUtf16(
return res;
}

jsi::Value TracingRuntime::createValueFromJsonUtf8(
const uint8_t *json,
size_t length) {
// Deliberately not RD::createValueFromJsonUtf8, which would parse in the
// plain runtime and return a value that was never recorded. See the header.
return jsi::Runtime::createValueFromJsonUtf8(json, length);
}

jsi::PropNameID TracingRuntime::createPropNameIDFromAscii(
const char *str,
size_t length) {
Expand Down
7 changes: 7 additions & 0 deletions API/hermes/TracingRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ class TracingRuntime : public jsi::RuntimeDecorator<jsi::Runtime>
jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override;
jsi::String createStringFromUtf16(const char16_t *utf16, size_t length)
override;
/// Parse JSON with the JS-based default implementation in jsi::Runtime
/// rather than forwarding to the plain runtime. The default calls back into
/// this runtime for global(), getProperty() and call(), so parsing is
/// recorded as ordinary records and the returned value gets a def, without
/// needing a dedicated record type.
jsi::Value createValueFromJsonUtf8(const uint8_t *json, size_t length)
override;
std::string utf8(const jsi::PropNameID &) override;

jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length)
Expand Down
3 changes: 3 additions & 0 deletions API/jsi/jsi/decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
Instrumentation& instrumentation() override {
return *this;
}
Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
return plain_.createValueFromJsonUtf8(json, length);
}

protected:
// plain is generally going to be a reference to an object managed
Expand Down
48 changes: 48 additions & 0 deletions API/jsi/jsi/test/testlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1926,6 +1926,54 @@ TEST_P(JSITest, CreateFromUtf16Test) {
EXPECT_EQ(cp, 55357); // 0xD83D in decimal
}

TEST_P(JSITest, CreateValueFromJsonUtf8Test) {
uint8_t jsonText[] = "{\"a\": 1}";

// This Runtime Decorator is used to test the default createValueFromJsonUtf8
// implementation for VMs that do not provide their own implementation
class DefaultRD : public RuntimeDecorator<Runtime, Runtime> {
public:
explicit DefaultRD(Runtime& rt) : RuntimeDecorator(rt) {}

Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
return Runtime::createValueFromJsonUtf8(json, length);
}
};

DefaultRD defaultRd(rt);
Object obj =
Value::createFromJsonUtf8(defaultRd, jsonText, sizeof(jsonText) - 1)
.getObject(defaultRd);
EXPECT_EQ(obj.getProperty(defaultRd, "a").getNumber(), 1);

// A RuntimeDecorator must forward createValueFromJsonUtf8 to the runtime it
// decorates, like every other method.
class CountingRD : public RuntimeDecorator<Runtime, Runtime> {
public:
explicit CountingRD(Runtime& rt) : RuntimeDecorator(rt) {}

Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
++count;
return plain().createValueFromJsonUtf8(json, length);
}

int count = 0;
};

// Overrides nothing, so it should forward everything to CountingRD.
class OuterRD : public RuntimeDecorator<Runtime, Runtime> {
public:
explicit OuterRD(Runtime& rt) : RuntimeDecorator(rt) {}
};

CountingRD counting(rt);
OuterRD outer(counting);
obj = outer.createValueFromJsonUtf8(jsonText, sizeof(jsonText) - 1)
.getObject(outer);
EXPECT_EQ(counting.count, 1);
EXPECT_EQ(obj.getProperty(outer, "a").getNumber(), 1);
}

TEST_P(JSITest, GetStringDataTest) {
// This Runtime Decorator is used to test the default getStringData
// implementation for VMs that do not provide their own implementation
Expand Down
10 changes: 10 additions & 0 deletions unittests/API/APITest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ TEST_P(HermesRuntimeTest, StrictHostFunctionBindTest) {
.getBool());
}

TEST_P(HermesRuntimeTest, CreateValueFromJsonUtf8IgnoresGlobalJSON) {
// Hermes parses JSON natively, so replacing the global JSON.parse must not
// change the result, also when the runtime is decorated (e.g. thread-safe).
eval("JSON.parse = function() { return 'hijacked'; }");
uint8_t json[] = "{\"a\": 1}";
Value v = Value::createFromJsonUtf8(*rt, json, sizeof(json) - 1);
ASSERT_TRUE(v.isObject());
EXPECT_EQ(v.getObject(*rt).getProperty(*rt, "a").getNumber(), 1);
}

TEST_P(HermesRuntimeTest, DrainMicrotasksThrowsFinalizationRegistryError) {
eval(R"(
var target = {};
Expand Down
20 changes: 20 additions & 0 deletions unittests/API/SynthTraceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2259,6 +2259,26 @@ TEST_F(SynthTraceReplayTest, CreateJSErrorReplay) {
}
}

/// JSON parsed from native code must be recorded, so that the tracing runtime
/// knows the resulting object when native code uses it afterwards.
TEST_F(SynthTraceReplayTest, CreateValueFromJsonUtf8Replay) {
{
auto &rt = *traceRt;
uint8_t json[] = "{\"a\": 1, \"b\": \"str\"}";
jsi::Object obj = jsi::Value::createFromJsonUtf8(rt, json, sizeof(json) - 1)
.getObject(rt);
rt.global().setProperty(rt, "a", obj.getProperty(rt, "a"));
rt.global().setProperty(rt, "parsed", obj);
}
replay();
{
auto &rt = *replayRt;
EXPECT_EQ(rt.global().getProperty(rt, "a").getNumber(), 1);
jsi::Object parsed = rt.global().getPropertyAsObject(rt, "parsed");
EXPECT_EQ(parsed.getProperty(rt, "b").getString(rt).utf8(rt), "str");
}
}

/// This test is here to make sure that the replayed string match is happening
/// during replay.
TEST_F(SynthTraceReplayTest, PropNameIDUtf8) {
Expand Down
Loading