Skip to content

Commit

Permalink
Implement queueMicrotask in Hermes
Browse files Browse the repository at this point in the history
Summary: This adds a basic implementation for `jsi::Runtime::queueMicrotask` in Hermes that adds the callbacks to the existing microtask queue in Hermes (used in `drainMicrotasks`).

Differential Revision: D54302535
  • Loading branch information
rubennorte authored and facebook-github-bot committed Feb 28, 2024
1 parent f746601 commit 1040144
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 3 deletions.
15 changes: 15 additions & 0 deletions API/hermes/SynthTrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ bool SynthTrace::CreateObjectRecord::operator==(const Record &that) const {
return objID_ == thatCasted.objID_;
}

bool SynthTrace::QueueMicrotaskRecord::operator==(const Record &that) const {
if (!Record::operator==(that)) {
return false;
}
const auto &thatCasted = dynamic_cast<const QueueMicrotaskRecord &>(that);
return callbackID_ == thatCasted.callbackID_;
}

bool SynthTrace::DrainMicrotasksRecord::operator==(const Record &that) const {
if (!Record::operator==(that)) {
return false;
Expand Down Expand Up @@ -634,6 +642,11 @@ void SynthTrace::HasPropertyRecord::toJSONInternal(JSONEmitter &json) const {
#endif
}

void SynthTrace::QueueMicrotaskRecord::toJSONInternal(JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("callbackID", callbackID_);
}

void SynthTrace::DrainMicrotasksRecord::toJSONInternal(
JSONEmitter &json) const {
Record::toJSONInternal(json);
Expand Down Expand Up @@ -831,6 +844,7 @@ llvh::raw_ostream &operator<<(
CASE(CreatePropNameID);
CASE(CreateHostObject);
CASE(CreateHostFunction);
CASE(QueueMicrotask);
CASE(DrainMicrotasks);
CASE(GetProperty);
CASE(SetProperty);
Expand Down Expand Up @@ -877,6 +891,7 @@ std::istream &operator>>(std::istream &is, SynthTrace::RecordType &type) {
CASE(CreatePropNameID)
CASE(CreateHostObject)
CASE(CreateHostFunction)
CASE(QueueMicrotask)
CASE(DrainMicrotasks)
CASE(GetProperty)
CASE(SetProperty)
Expand Down
17 changes: 17 additions & 0 deletions API/hermes/SynthTrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ class SynthTrace {
CreatePropNameID,
CreateHostObject,
CreateHostFunction,
QueueMicrotask,
DrainMicrotasks,
GetProperty,
SetProperty,
Expand Down Expand Up @@ -723,6 +724,22 @@ class SynthTrace {
void toJSONInternal(::hermes::JSONEmitter &json) const override;
};

struct QueueMicrotaskRecord : public Record {
static constexpr RecordType type{RecordType::QueueMicrotask};
const ObjectID callbackID_;

QueueMicrotaskRecord(TimeSinceStart time, ObjectID callbackID)
: Record(time), callbackID_(callbackID) {}

bool operator==(const Record &that) const final;

RecordType getType() const override {
return type;
}

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

struct DrainMicrotasksRecord : public Record {
static constexpr RecordType type{RecordType::DrainMicrotasks};
int maxMicrotasksHint_;
Expand Down
7 changes: 7 additions & 0 deletions API/hermes/SynthTraceParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ SynthTrace getTrace(JSONArray *array, SynthTrace::ObjectID globalObjID) {
trace.emplace_back<SynthTrace::CreateObjectRecord>(
timeFromStart, objID->getValue());
break;
case RecordType::QueueMicrotask: {
int callbackID =
getNumberAs<SynthTrace::ObjectID>(obj->get("callbackID"));
trace.emplace_back<SynthTrace::QueueMicrotaskRecord>(
timeFromStart, callbackID);
break;
}
case RecordType::DrainMicrotasks: {
int maxMicrotasksHint = getNumberAs<int>(obj->get("maxMicrotasksHint"));
trace.emplace_back<SynthTrace::DrainMicrotasksRecord>(
Expand Down
5 changes: 4 additions & 1 deletion API/hermes/TimerStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ class TimedRuntime final : public jsi::RuntimeDecorator<jsi::Runtime> {
return RD::callAsConstructor(func, args, count);
}

void queueMicrotask(const jsi::Function & /*callback*/) override {}
void queueMicrotask(const jsi::Function &callback) override {
auto timer = rts_.incomingTimer("queueMicrotask");
return RD::queueMicrotask(callback);
}

bool drainMicrotasks(int maxMicrotasksHint) override {
auto timer = rts_.incomingTimer("drainMicrotasks");
Expand Down
8 changes: 8 additions & 0 deletions API/hermes/TraceInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,14 @@ Value TraceInterpreter::execFunction(
locals);
break;
}
case RecordType::QueueMicrotask: {
const auto &queueRecord =
static_cast<const SynthTrace::QueueMicrotaskRecord &>(*rec);
const auto &callback =
getObjForUse(queueRecord.callbackID_).asFunction(rt_);
rt_.queueMicrotask(callback);
break;
}
case RecordType::DrainMicrotasks: {
const auto &drainRecord =
static_cast<const SynthTrace::DrainMicrotasksRecord &>(*rec);
Expand Down
6 changes: 5 additions & 1 deletion API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,11 @@ jsi::Value TracingRuntime::evaluateJavaScript(
return res;
}

void TracingRuntime::queueMicrotask(const jsi::Function &callback) {}
void TracingRuntime::queueMicrotask(const jsi::Function &callback) {
RD::queueMicrotask(callback);
trace_.emplace_back<SynthTrace::QueueMicrotaskRecord>(
getTimeSinceStart(), getUniqueID(callback));
}

bool TracingRuntime::drainMicrotasks(int maxMicrotasksHint) {
auto res = RD::drainMicrotasks(maxMicrotasksHint);
Expand Down
8 changes: 7 additions & 1 deletion API/hermes/hermes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1503,7 +1503,13 @@ jsi::Value HermesRuntimeImpl::evaluateJavaScript(
return evaluateJavaScriptWithSourceMap(buffer, nullptr, sourceURL);
}

void HermesRuntimeImpl::queueMicrotask(const jsi::Function & /*callback*/) {}
void HermesRuntimeImpl::queueMicrotask(const jsi::Function &callback) {
if (runtime_.hasMicrotaskQueue()) {
vm::Handle<vm::Callable> handle =
vm::Handle<vm::Callable>::vmcast(&phv(callback));
runtime_.enqueueJob(handle.get());
}
}

bool HermesRuntimeImpl::drainMicrotasks(int maxMicrotasksHint) {
if (runtime_.hasMicrotaskQueue()) {
Expand Down
36 changes: 36 additions & 0 deletions unittests/API/SynthTraceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,42 @@ TEST_F(SynthTraceTest, CallObjectGetProp) {
*records[8]);
}

TEST_F(SynthTraceTest, QueueMicrotask) {
SynthTrace::ObjectID callbackID;
SynthTrace::ObjectID callbackID2;

{
jsi::Function func = jsi::Function::createFromHostFunction(
*rt,
jsi::PropNameID::forAscii(*rt, "func"),
0,
[](jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
size_t count) -> jsi::Value { return jsi::Value::undefined(); });
callbackID = rt->getUniqueID(func);

jsi::Function func2 = jsi::Function::createFromHostFunction(
*rt,
jsi::PropNameID::forAscii(*rt, "func"),
0,
[](jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
size_t count) -> jsi::Value { return jsi::Value::undefined(); });
callbackID2 = rt->getUniqueID(func2);

rt->queueMicrotask(func);
rt->queueMicrotask(func2);
}
const auto &records = rt->trace().records();
EXPECT_EQ(6, records.size());
EXPECT_EQ_RECORD(
SynthTrace::QueueMicrotaskRecord(dummyTime, callbackID), *records[4]);
EXPECT_EQ_RECORD(
SynthTrace::QueueMicrotaskRecord(dummyTime, callbackID2), *records[5]);
}

TEST_F(SynthTraceTest, DrainMicrotasks) {
{
rt->drainMicrotasks();
Expand Down

0 comments on commit 1040144

Please sign in to comment.