Skip to content

Commit

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

Reviewed By: neildhar

Differential Revision: D66890202

fbshipit-source-id: 8070c5a5172a64c3fc26666d6effbddd32758579
  • Loading branch information
tsaichien authored and facebook-github-bot committed Dec 12, 2024
1 parent 3fb98ed commit f541af7
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 0 deletions.
7 changes: 7 additions & 0 deletions API/hermes/SynthTrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,13 @@ void SynthTrace::CreateObjectRecord::toJSONInternal(JSONEmitter &json) const {
json.emitKeyValue("objID", objID_);
}

void SynthTrace::CreateObjectWithPrototypeRecord::toJSONInternal(
::hermes::JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("objID", objID_);
json.emitKeyValue("prototype", encode(prototype_));
}

static std::string createBigIntMethodToString(
SynthTrace::CreateBigIntRecord::Method m) {
switch (m) {
Expand Down
26 changes: 26 additions & 0 deletions API/hermes/SynthTrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class SynthTrace {
RECORD(EndExecJS) \
RECORD(Marker) \
RECORD(CreateObject) \
RECORD(CreateObjectWithPrototype) \
RECORD(CreateString) \
RECORD(CreatePropNameID) \
RECORD(CreateHostObject) \
Expand Down Expand Up @@ -660,6 +661,31 @@ class SynthTrace {
}
};

struct CreateObjectWithPrototypeRecord : public Record {
static constexpr RecordType type{RecordType::CreateObjectWithPrototype};
const ObjectID objID_;
/// The prototype being assigned
const TraceValue prototype_;

CreateObjectWithPrototypeRecord(
TimeSinceStart time,
ObjectID objID,
TraceValue prototype)
: Record(time), objID_(objID), prototype_(prototype) {}

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(prototype_, uses);
return uses;
}
};

struct CreateHostObjectRecord final : public CreateObjectRecord {
static constexpr RecordType type{RecordType::CreateHostObject};
using CreateObjectRecord::CreateObjectRecord;
Expand Down
9 changes: 9 additions & 0 deletions API/hermes/SynthTraceParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,15 @@ SynthTrace getTrace(
trace.emplace_back<SynthTrace::CreateObjectRecord>(
timeFromStart, objID->getValue());
break;
case RecordType::CreateObjectWithPrototype: {
auto *prototype =
llvh::dyn_cast_or_null<JSONString>(obj->get("prototype"));
trace.emplace_back<SynthTrace::CreateObjectWithPrototypeRecord>(
timeFromStart,
objID->getValue(),
SynthTrace::decode(prototype->c_str()));
break;
}
case RecordType::QueueMicrotask: {
auto callbackID =
getNumberAs<SynthTrace::ObjectID>(obj->get("callbackID"));
Expand Down
10 changes: 10 additions & 0 deletions API/hermes/TraceInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,16 @@ void TraceInterpreter::executeRecords() {
addToObjectMap(cor.objID_, Object(rt_), currentExecIndex);
break;
}
case RecordType::CreateObjectWithPrototype: {
const auto &record =
static_cast<const SynthTrace::CreateObjectWithPrototypeRecord &>(
*rec);
addToObjectMap(
record.objID_,
Object::create(rt_, traceValueToJSIValue(record.prototype_)),
currentExecIndex);
break;
}
case RecordType::CreateBigInt: {
const auto &cbr =
static_cast<const SynthTrace::CreateBigIntRecord &>(*rec);
Expand Down
8 changes: 8 additions & 0 deletions API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ jsi::Object TracingRuntime::createObject() {
return obj;
}

jsi::Object TracingRuntime::createObjectWithPrototype(
const jsi::Value &prototype) {
auto obj = RD::createObjectWithPrototype(prototype);
trace_.emplace_back<SynthTrace::CreateObjectWithPrototypeRecord>(
getTimeSinceStart(), defObjectID(obj), useTraceValue(prototype));
return obj;
}

jsi::Object TracingRuntime::createObject(std::shared_ptr<jsi::HostObject> ho) {
class TracingHostObject : public jsi::DecoratedHostObject {
public:
Expand Down
1 change: 1 addition & 0 deletions API/hermes/TracingRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class TracingRuntime : public jsi::RuntimeDecorator<jsi::Runtime> {
jsi::Object global() override;

jsi::Object createObject() override;
jsi::Object createObjectWithPrototype(const jsi::Value &prototype) override;
jsi::Object createObject(std::shared_ptr<jsi::HostObject> ho) override;

// Note that the NativeState methods do not need to be traced since they
Expand Down
43 changes: 43 additions & 0 deletions unittests/API/SynthTraceParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,47 @@ TEST_F(SynthTraceParserTest, ParseSetAndGetPrototypeRecord) {
ASSERT_EQ(record3.objID_, 2);
}

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

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

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

} // namespace
10 changes: 10 additions & 0 deletions unittests/API/SynthTraceSerializationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,14 @@ TEST_F(SynthTraceSerializationTest, GetPrototypeTest) {
to_string(SynthTrace::GetPrototypeRecord(dummyTime, 1)));
}

TEST_F(SynthTraceSerializationTest, CreateObjectWithPrototypeRecord) {
EXPECT_EQ(
R"({"type":"CreateObjectWithPrototypeRecord","time":0,"objID":1,"prototype":"null:"})",
to_string(SynthTrace::CreateObjectWithPrototypeRecord(
dummyTime, 1, SynthTrace::encodeNull())));
EXPECT_EQ(
R"({"type":"CreateObjectWithPrototypeRecord","time":0,"objID":2,"prototype":"object:1"})",
to_string(SynthTrace::CreateObjectWithPrototypeRecord(
dummyTime, 2, SynthTrace::encodeObject(1))));
}
} // namespace
15 changes: 15 additions & 0 deletions unittests/API/SynthTraceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,15 @@ TEST_F(SynthTraceReplayTest, CreateObjectReplay) {
auto obj = jsi::Object(rt);
obj.setProperty(rt, "bar", 5);
rt.global().setProperty(rt, "foo", obj);

jsi::Object prototypeObj(rt);
prototypeObj.setProperty(rt, "someProperty", 123);
jsi::Value prototype(rt, prototypeObj);
jsi::Object child1 = jsi::Object::create(rt, prototype);
rt.global().setProperty(rt, "child1", child1);

jsi::Object child2 = jsi::Object::create(rt, jsi::Value::null());
rt.global().setProperty(rt, "child2", child2);
}
replay();
{
Expand All @@ -1460,6 +1469,12 @@ TEST_F(SynthTraceReplayTest, CreateObjectReplay) {
.getProperty(rt, "bar")
.asNumber(),
5);

auto child1 = rt.global().getProperty(rt, "child1").asObject(rt);
EXPECT_EQ(child1.getProperty(rt, "someProperty").asNumber(), 123);

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

Expand Down

0 comments on commit f541af7

Please sign in to comment.