Skip to content

Commit

Permalink
Add BigInt JSI API support
Browse files Browse the repository at this point in the history
Summary: This change adds SynthTrace support to the new BigInt JSI APIs

Reviewed By: neildhar

Differential Revision: D38010529

fbshipit-source-id: e653d050aa89740f8318ff5b147d8bc755624417
  • Loading branch information
jpporto authored and facebook-github-bot committed Jul 29, 2022
1 parent e8cc573 commit 4d64e61
Show file tree
Hide file tree
Showing 10 changed files with 485 additions and 8 deletions.
65 changes: 64 additions & 1 deletion API/hermes/SynthTrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ SynthTrace::TraceValue SynthTrace::encodeObject(ObjectID objID) {
return TraceValue::encodeObjectValue(objID);
}

SynthTrace::TraceValue SynthTrace::encodeBigInt(ObjectID objID) {
return TraceValue::encodeBigIntValue(objID);
}

SynthTrace::TraceValue SynthTrace::encodeString(ObjectID objID) {
return TraceValue::encodeStringValue(objID);
}
Expand All @@ -241,6 +245,8 @@ std::string SynthTrace::encode(TraceValue value) {
return "null:";
} else if (value.isObject()) {
return std::string("object:") + std::to_string(value.getUID());
} else if (value.isBigInt()) {
return std::string("bigint:") + std::to_string(value.getUID());
} else if (value.isString()) {
return std::string("string:") + std::to_string(value.getUID());
} else if (value.isPropNameID()) {
Expand Down Expand Up @@ -278,6 +284,8 @@ SynthTrace::TraceValue SynthTrace::decode(const std::string &str) {
return encodeObject(decodeID(rest));
} else if (tag == "string") {
return encodeString(decodeID(rest));
} else if (tag == "bigint") {
return encodeBigInt(decodeID(rest));
} else if (tag == "propNameID") {
return encodePropNameID(decodeID(rest));
} else if (tag == "symbol") {
Expand Down Expand Up @@ -337,6 +345,24 @@ bool SynthTrace::DrainMicrotasksRecord::operator==(const Record &that) const {
return maxMicrotasksHint_ == thatCasted.maxMicrotasksHint_;
}

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

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

bool SynthTrace::CreateStringRecord::operator==(const Record &that) const {
if (!Record::operator==(that)) {
return false;
Expand Down Expand Up @@ -516,6 +542,36 @@ void SynthTrace::CreateObjectRecord::toJSONInternal(JSONEmitter &json) const {
json.emitKeyValue("objID", objID_);
}

static std::string createBigIntMethodToString(
SynthTrace::CreateBigIntRecord::Method m) {
switch (m) {
case SynthTrace::CreateBigIntRecord::Method::FromInt64:
return "FromInt64";
case SynthTrace::CreateBigIntRecord::Method::FromUint64:
return "FromUint64";
default:
llvm_unreachable("No other valid CreateBigInt::Method.");
}
}

void SynthTrace::CreateBigIntRecord::toJSONInternal(
::hermes::JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("objID", objID_);
json.emitKeyValue("method", createBigIntMethodToString(method_));
// bits is potentially an invalid number (i.e., > 53 bits), so emit it
// as a string.
json.emitKeyValue("bits", std::to_string(bits_));
}

void SynthTrace::BigIntToStringRecord::toJSONInternal(
::hermes::JSONEmitter &json) const {
Record::toJSONInternal(json);
json.emitKeyValue("strID", strID_);
json.emitKeyValue("bigintID", bigintID_);
json.emitKeyValue("radix", radix_);
}

static std::string encodingName(bool isASCII) {
return isASCII ? "ASCII" : "UTF-8";
}
Expand Down Expand Up @@ -802,6 +858,8 @@ llvh::raw_ostream &operator<<(
CASE(SetPropertyNativeReturn);
CASE(GetNativePropertyNames);
CASE(GetNativePropertyNamesReturn);
CASE(CreateBigInt);
CASE(BigIntToString);
}
#undef CASE
// This only exists to appease gcc.
Expand Down Expand Up @@ -845,8 +903,13 @@ std::istream &operator>>(std::istream &is, SynthTrace::RecordType &type) {
CASE(SetPropertyNativeReturn)
CASE(GetNativePropertyNames)
CASE(GetNativePropertyNamesReturn)
CASE(CreateBigInt)
CASE(BigIntToString)
#undef CASE
return is;

llvm_unreachable(
"failed to parse SynthTrace::RecordType. Make sure all enum values are "
"handled.");
}

} // namespace tracing
Expand Down
86 changes: 84 additions & 2 deletions API/hermes/SynthTrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class SynthTrace {
return tag_ == Tag::Object;
}

bool isBigInt() const {
return tag_ == Tag::BigInt;
}

bool isString() const {
return tag_ == Tag::String;
}
Expand All @@ -78,7 +82,8 @@ class SynthTrace {
}

bool isUID() const {
return isObject() || isString() || isPropNameID() || isSymbol();
return isObject() || isBigInt() || isString() || isPropNameID() ||
isSymbol();
}

static TraceValue encodeUndefinedValue() {
Expand All @@ -101,6 +106,10 @@ class SynthTrace {
return TraceValue(Tag::Object, uid);
}

static TraceValue encodeBigIntValue(uint64_t uid) {
return TraceValue(Tag::BigInt, uid);
}

static TraceValue encodeStringValue(uint64_t uid) {
return TraceValue(Tag::String, uid);
}
Expand Down Expand Up @@ -139,7 +148,8 @@ class SynthTrace {
Object,
String,
PropNameID,
Symbol
Symbol,
BigInt,
};

explicit TraceValue(Tag tag) : tag_(tag) {}
Expand Down Expand Up @@ -197,6 +207,8 @@ class SynthTrace {
SetPropertyNativeReturn,
GetNativePropertyNames,
GetNativePropertyNamesReturn,
CreateBigInt,
BigIntToString,
};

/// A Record is one element of a trace.
Expand Down Expand Up @@ -290,6 +302,8 @@ class SynthTrace {
static TraceValue encodeNumber(double value);
/// Encodes an object for the trace as a unique id.
static TraceValue encodeObject(ObjectID objID);
/// Encodes a bigint for the trace as a unique id.
static TraceValue encodeBigInt(ObjectID objID);
/// Encodes a string for the trace as a unique id.
static TraceValue encodeString(ObjectID objID);
/// Encodes a PropNameID for the trace as a unique id.
Expand Down Expand Up @@ -461,6 +475,74 @@ class SynthTrace {
}
};

/// A CreateBigIntRecord is an event where a jsi::BigInt (and thus a
/// Hermes BigIntPrimitive) is created by the native code.
struct CreateBigIntRecord : public Record {
static constexpr RecordType type{RecordType::CreateBigInt};
const ObjectID objID_;
enum class Method {
FromInt64,
FromUint64,
};
Method method_;
uint64_t bits_;

CreateBigIntRecord(
TimeSinceStart time,
ObjectID objID,
Method m,
uint64_t bits)
: Record(time), objID_(objID), method_(m), bits_(bits) {}

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

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

RecordType getType() const override {
return type;
}

std::vector<ObjectID> defs() const override {
return {objID_};
}

std::vector<ObjectID> uses() const override {
return {};
}
};

/// A BigIntToString is an event where a jsi::BigInt is converted to a
/// string by native code
struct BigIntToStringRecord : public Record {
static constexpr RecordType type{RecordType::BigIntToString};
const ObjectID strID_;
const ObjectID bigintID_;
int radix_;

BigIntToStringRecord(
TimeSinceStart time,
ObjectID strID,
ObjectID bigintID,
int radix)
: Record(time), strID_(strID), bigintID_(bigintID), radix_(radix) {}

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

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

RecordType getType() const override {
return type;
}

std::vector<ObjectID> defs() const override {
return {strID_};
}

std::vector<ObjectID> uses() const override {
return {bigintID_};
}
};

/// A CreateStringRecord is an event where a jsi::String (and thus a
/// Hermes StringPrimitive) is created by the native code.
struct CreateStringRecord : public Record {
Expand Down
49 changes: 49 additions & 0 deletions API/hermes/SynthTraceParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,29 @@ NumericType getNumberAs(const JSONValue *val, NumericType dflt) {
}
}

/// Parses the json \p value (which must be a string) as a uint64_t. Throws if
/// std::stoull throws, or if it fails to parse the entire string.
uint64_t jsonStringToUint64(const ::hermes::parser::JSONValue *val) {
if (!val) {
throw std::invalid_argument("value doesn't exist");
}

auto str = llvh::dyn_cast<JSONString>(val);
if (!str) {
throw std::invalid_argument("value is not a string");
}

llvh::StringRef r = str->str();
std::size_t numProcessed;
uint64_t ret = std::stoull(std::string{r.data(), r.size()}, &numProcessed);
if (numProcessed < r.size()) {
throw std::invalid_argument(
std::string("failed to convert jsonString '") + r.data() +
"' to uint64_t");
}
return ret;
}

::hermes::vm::GCConfig::Builder getGCConfig(JSONObject *rtConfig) {
// This function should extract all fields from GCConfig that can affect
// performance metrics. Configs for debugging can be ignored.
Expand Down Expand Up @@ -308,6 +331,32 @@ SynthTrace getTrace(JSONArray *array, SynthTrace::ObjectID globalObjID) {
timeFromStart, maxMicrotasksHint);
break;
}
case RecordType::CreateBigInt: {
auto method = llvh::dyn_cast<JSONString>(obj->get("method"));
SynthTrace::CreateBigIntRecord::Method creationMethod =
SynthTrace::CreateBigIntRecord::Method::FromUint64;
if (method->str() == "FromInt64") {
creationMethod = SynthTrace::CreateBigIntRecord::Method::FromInt64;
} else {
assert(method->str() == "FromUint64");
}
trace.emplace_back<SynthTrace::CreateBigIntRecord>(
timeFromStart,
objID->getValue(),
creationMethod,
jsonStringToUint64(obj->get("bits")));
break;
}
case RecordType::BigIntToString: {
auto *strID = llvh::dyn_cast<JSONNumber>(obj->get("strID"));
auto *bigintID = llvh::dyn_cast<JSONNumber>(obj->get("bigintID"));
trace.emplace_back<SynthTrace::BigIntToStringRecord>(
timeFromStart,
strID->getValue(),
bigintID->getValue(),
getNumberAs<int>(obj->get("radix")));
break;
}
case RecordType::CreateString: {
auto encoding =
llvh::dyn_cast_or_null<JSONString>(obj->get("encoding"));
Expand Down
40 changes: 38 additions & 2 deletions API/hermes/TraceInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ Value traceValueToJSIValue(
if (value.isBool()) {
return Value(value.getBool());
}
if (value.isObject() || value.isString() || value.isSymbol()) {
if (value.isObject() || value.isBigInt() || value.isString() ||
value.isSymbol()) {
return getJSIValueForUse(value.getUID());
}
llvm_unreachable("Unrecognized value type encountered");
Expand Down Expand Up @@ -1020,7 +1021,9 @@ Value TraceInterpreter::execFunction(
if (it != locals.end()) {
// Satisfiable locally
Value val{rt_, it->second};
assert(val.isObject() || val.isString() || val.isSymbol());
assert(
val.isObject() || val.isBigInt() || val.isString() ||
val.isSymbol());
// If it was the last local use, delete that object id from locals.
auto defAndUse = call.locals.find(obj);
if (defAndUse != call.locals.end() &&
Expand Down Expand Up @@ -1162,6 +1165,39 @@ Value TraceInterpreter::execFunction(
call, cor.objID_, globalRecordNum, Object(rt_), locals);
break;
}
case RecordType::CreateBigInt: {
const auto &cbr =
static_cast<const SynthTrace::CreateBigIntRecord &>(*rec);
Value bigint;
switch (cbr.method_) {
case SynthTrace::CreateBigIntRecord::Method::FromInt64:
bigint =
BigInt::fromInt64(rt_, static_cast<int64_t>(cbr.bits_));
assert(
bigint.asBigInt(rt_).getInt64(rt_) ==
static_cast<int64_t>(cbr.bits_));
break;
case SynthTrace::CreateBigIntRecord::Method::FromUint64:
bigint = BigInt::fromUint64(rt_, cbr.bits_);
assert(bigint.asBigInt(rt_).getUint64(rt_) == cbr.bits_);
break;
}
addJSIValueToDefs(
call, cbr.objID_, globalRecordNum, std::move(bigint), locals);
break;
}
case RecordType::BigIntToString: {
const auto &bts =
static_cast<const SynthTrace::BigIntToStringRecord &>(*rec);
BigInt obj = getJSIValueForUse(bts.bigintID_).asBigInt(rt_);
addJSIValueToDefs(
call,
bts.strID_,
globalRecordNum,
obj.toString(rt_, bts.radix_),
locals);
break;
}
case RecordType::CreateString: {
const auto &csr =
static_cast<const SynthTrace::CreateStringRecord &>(*rec);
Expand Down
Loading

0 comments on commit 4d64e61

Please sign in to comment.