Skip to content

Commit

Permalink
Add GetPrototypeRecord and SetPrototypeRecord in Synth Traces
Browse files Browse the repository at this point in the history
Summary:
Add GetPrototypeRecord and SetPrototypeRecord in Synth Traces, so we can
replay them.

Reviewed By: neildhar

Differential Revision: D66890201

fbshipit-source-id: 9d79222b0279009586e59be17f2e3f6edf69835c
  • Loading branch information
tsaichien authored and facebook-github-bot committed Dec 12, 2024
1 parent d032809 commit 3fb98ed
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 0 deletions.
11 changes: 11 additions & 0 deletions API/hermes/SynthTrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,17 @@ void SynthTrace::SetPropertyRecord::toJSONInternal(JSONEmitter &json) const {
json.emitKeyValue("value", encode(value_));
}

void SynthTrace::SetPrototypeRecord::toJSONInternal(JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("objID", objID_);
json.emitKeyValue("value", encode(value_));
}

void SynthTrace::GetPrototypeRecord::toJSONInternal(JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("objID", objID_);
}

void SynthTrace::HasPropertyRecord::toJSONInternal(JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("objID", objID_);
Expand Down
44 changes: 44 additions & 0 deletions API/hermes/SynthTrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ class SynthTrace {
RECORD(Utf8) \
RECORD(Utf16) \
RECORD(GetStringData) \
RECORD(GetPrototype) \
RECORD(SetPrototype) \
RECORD(Global)

/// RecordType is a tag used to differentiate which type of record it is.
Expand Down Expand Up @@ -885,6 +887,48 @@ class SynthTrace {
}
};

/// A SetPrototypeRecord is an event where native code sets the prototype of a
/// JS Object
struct SetPrototypeRecord : public Record {
static constexpr RecordType type{RecordType::SetPrototype};
/// The ObjectID of the object that was accessed for its prototype.
const ObjectID objID_;
/// The custom prototype being assigned
const TraceValue value_;
SetPrototypeRecord(TimeSinceStart time, ObjectID objID, TraceValue value)
: Record(time), objID_(objID), value_(value) {}

void toJSONInternal(::hermes::JSONEmitter &json) const override;

RecordType getType() const override {
return type;
}
std::vector<ObjectID> uses() const override {
std::vector<ObjectID> uses{objID_};
pushIfTrackedValue(value_, uses);
return uses;
}
};

/// A GetPrototypeRecord is an event where native code gets the prototype of a
/// JS Object
struct GetPrototypeRecord : public Record {
static constexpr RecordType type{RecordType::GetPrototype};
/// The ObjectID of the object that was accessed for its prototype.
const ObjectID objID_;
GetPrototypeRecord(TimeSinceStart time, ObjectID objID)
: Record(time), objID_(objID) {}

void toJSONInternal(::hermes::JSONEmitter &json) const override;

RecordType getType() const override {
return type;
}
std::vector<ObjectID> uses() const override {
return {objID_};
}
};

/// A CreateArrayRecord is an event where a new array is created of a specific
/// length.
struct CreateArrayRecord final : public Record {
Expand Down
11 changes: 11 additions & 0 deletions API/hermes/SynthTraceParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,17 @@ SynthTrace getTrace(
jsonStringToU16String(*strData));
break;
}
case RecordType::SetPrototype: {
trace.emplace_back<SynthTrace::SetPrototypeRecord>(
timeFromStart,
objID->getValue(),
SynthTrace::decode(propValue->c_str()));
break;
}
case RecordType::GetPrototype:
trace.emplace_back<SynthTrace::GetPrototypeRecord>(
timeFromStart, objID->getValue());
break;
case RecordType::Global: {
trace.emplace_back<SynthTrace::GlobalRecord>(
timeFromStart, objID->getValue());
Expand Down
15 changes: 15 additions & 0 deletions API/hermes/TraceInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,21 @@ void TraceInterpreter::executeRecords() {
}
break;
}
case RecordType::SetPrototype: {
const auto &record =
static_cast<const SynthTrace::SetPrototypeRecord &>(*rec);
auto obj = getJSIValueForUse(record.objID_).getObject(rt_);
obj.setPrototype(rt_, traceValueToJSIValue(record.value_));
break;
}
case RecordType::GetPrototype: {
const auto &record =
static_cast<const SynthTrace::GetPrototypeRecord &>(*rec);
auto obj = getJSIValueForUse(record.objID_).getObject(rt_);
auto prototype = obj.getPrototype(rt_);
retval = std::move(prototype);
break;
}
case RecordType::HasProperty: {
const auto &hpr =
static_cast<const SynthTrace::HasPropertyRecord &>(*rec);
Expand Down
18 changes: 18 additions & 0 deletions API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,16 @@ jsi::Value TracingRuntime::getProperty(
return value;
}

jsi::Value TracingRuntime::getPrototypeOf(const jsi::Object &object) {
trace_.emplace_back<SynthTrace::GetPrototypeRecord>(
getTimeSinceStart(), useObjectID(object));

auto prototype = RD::getPrototypeOf(object);
trace_.emplace_back<SynthTrace::ReturnToNativeRecord>(
getTimeSinceStart(), defTraceValue(prototype));
return prototype;
}

bool TracingRuntime::hasProperty(
const jsi::Object &obj,
const jsi::String &name) {
Expand Down Expand Up @@ -789,6 +799,14 @@ void TracingRuntime::setPropertyValue(
RD::setPropertyValue(obj, name, value);
}

void TracingRuntime::setPrototypeOf(
const jsi::Object &object,
const jsi::Value &prototype) {
trace_.emplace_back<SynthTrace::SetPrototypeRecord>(
getTimeSinceStart(), useObjectID(object), useTraceValue(prototype));
RD::setPrototypeOf(object, prototype);
}

jsi::Array TracingRuntime::getPropertyNames(const jsi::Object &o) {
trace_.emplace_back<SynthTrace::GetPropertyNamesRecord>(
getTimeSinceStart(), useObjectID(o));
Expand Down
4 changes: 4 additions & 0 deletions API/hermes/TracingRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ class TracingRuntime : public jsi::RuntimeDecorator<jsi::Runtime> {
const jsi::PropNameID &name,
const jsi::Value &value) override;

void setPrototypeOf(const jsi::Object &object, const jsi::Value &prototype)
override;
jsi::Value getPrototypeOf(const jsi::Object &object) override;

jsi::Array getPropertyNames(const jsi::Object &o) override;

jsi::WeakObject createWeakObject(const jsi::Object &o) override;
Expand Down
59 changes: 59 additions & 0 deletions unittests/API/SynthTraceParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,63 @@ TEST_F(SynthTraceParserTest, ParseGetStringDataRecord) {
ASSERT_EQ(record1.objID_, SynthTrace::encodePropNameID(1111));
}

TEST_F(SynthTraceParserTest, ParseSetAndGetPrototypeRecord) {
const char *src = R"(
{
"version": 5,
"globalObjID": 258,
"runtimeConfig": {
"gcConfig": {
"initHeapSize": 33554432,
"maxHeapSize": 536870912
}
},
"trace": [
{
"type": "SetPrototypeRecord",
"time": 1234,
"objID": 1,
"value": "null:"
},
{
"type": "SetPrototypeRecord",
"time": 12,
"objID": 2,
"value": "object:1"
},
{
"type": "GetPrototypeRecord",
"time": 123,
"objID": 1
},
{
"type": "GetPrototypeRecord",
"time": 1234,
"objID": 2
},
]
}
)";
auto parseResult = parseSynthTrace(bufFromStr(src));
SynthTrace &trace = std::get<0>(parseResult);

auto record0 = dynamic_cast<const SynthTrace::SetPrototypeRecord &>(
*trace.records().at(0));
ASSERT_EQ(record0.objID_, 1);
ASSERT_EQ(record0.value_, SynthTrace::encodeNull());

auto record1 = dynamic_cast<const SynthTrace::SetPrototypeRecord &>(
*trace.records().at(1));
ASSERT_EQ(record1.objID_, 2);
ASSERT_EQ(record1.value_, SynthTrace::encodeObject(1));

auto record2 = dynamic_cast<const SynthTrace::GetPrototypeRecord &>(
*trace.records().at(2));
ASSERT_EQ(record2.objID_, 1);

auto record3 = dynamic_cast<const SynthTrace::GetPrototypeRecord &>(
*trace.records().at(3));
ASSERT_EQ(record3.objID_, 2);
}

} // namespace
18 changes: 18 additions & 0 deletions unittests/API/SynthTraceSerializationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,22 @@ TEST_F(SynthTraceSerializationTest, GetStringDataRecord) {
to_string(SynthTrace::GetStringDataRecord(
dummyTime, SynthTrace::encodePropNameID(111), u"\xd83d")));
}

TEST_F(SynthTraceSerializationTest, SetPrototypeTest) {
EXPECT_EQ(
R"({"type":"SetPrototypeRecord","time":0,"objID":1,"value":"null:"})",
to_string(SynthTrace::SetPrototypeRecord(
dummyTime, 1, SynthTrace::encodeNull())));
EXPECT_EQ(
R"({"type":"SetPrototypeRecord","time":0,"objID":2,"value":"object:1"})",
to_string(SynthTrace::SetPrototypeRecord(
dummyTime, 2, SynthTrace::encodeObject(1))));
}

TEST_F(SynthTraceSerializationTest, GetPrototypeTest) {
EXPECT_EQ(
R"({"type":"GetPrototypeRecord","time":0,"objID":1})",
to_string(SynthTrace::GetPrototypeRecord(dummyTime, 1)));
}

} // namespace
44 changes: 44 additions & 0 deletions unittests/API/SynthTraceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,50 @@ TEST_F(SynthTraceReplayTest, SetPropertyReplay) {
}
}

TEST_F(SynthTraceReplayTest, SetPrototypeReplay) {
{
auto &rt = *traceRt;
jsi::Object prototypeObj(rt);
prototypeObj.setProperty(rt, "someProperty", 123);
jsi::Value prototype(rt, prototypeObj);

jsi::Object child1(rt);
child1.setPrototype(rt, prototype);
rt.global().setProperty(rt, "child1", child1);

auto prototype1 = child1.getPrototype(rt);
rt.global().setProperty(rt, "prototype1", prototype1);

jsi::Object child2(rt);
child2.setPrototype(rt, jsi::Value::null());
rt.global().setProperty(rt, "child2", child2);

auto prototype2 = child2.getPrototype(rt);
rt.global().setProperty(rt, "prototype2", prototype2);
}
replay();
{
auto &rt = *replayRt;
auto child1 = rt.global().getProperty(rt, "child1").getObject(rt);
EXPECT_EQ(child1.getProperty(rt, "someProperty").asNumber(), 123);
EXPECT_EQ(
child1.getPrototype(rt)
.getObject(rt)
.getProperty(rt, "someProperty")
.getNumber(),
123);

auto prototype1 = rt.global().getProperty(rt, "prototype1").getObject(rt);
EXPECT_EQ(prototype1.getProperty(rt, "someProperty").asNumber(), 123);

auto child2 = rt.global().getProperty(rt, "child2").getObject(rt);
EXPECT_TRUE(child2.getPrototype(rt).isNull());

auto prototype2 = rt.global().getProperty(rt, "prototype2");
EXPECT_TRUE(prototype2.isNull());
}
}

TEST_F(SynthTraceReplayTest, BigIntCreate) {
{
auto &rt = *traceRt;
Expand Down

0 comments on commit 3fb98ed

Please sign in to comment.