///|
fn json_id(id : UInt64) -> Json {
// Decimal strings preserve object IDs beyond JavaScript's 53-bit range.
Json::string(id.to_string())
}
///|
fn json_int(value : Int) -> Json {
Json::number(value.to_double())
}
///|
pub fn Value::to_json(self : Value) -> Json {
let value = match self {
Reference(id) => json_id(id)
Boolean(value) => Json::boolean(value)
Character(value) | ByteValue(value) | ShortValue(value) | IntValue(value) =>
json_int(value)
LongValue(value) => Json::string(value.to_string())
FloatValue(value) | DoubleValue(value) =>
// NaN/Infinity have no JSON numeric representation.
if value.is_nan() {
Json::string("NaN")
} else if value.is_pos_inf() {
Json::string("Infinity")
} else if value.is_neg_inf() {
Json::string("-Infinity")
} else {
Json::number(value)
}
}
Json::object({
"type": Json::string(value_type_name(self.kind()) catch { _ => "unknown" }),
"value": value,
})
}
///|
pub fn Summary::summary_json(self : Summary) -> Json {
Json::object({
"format": Json::string("JAVA PROFILE 1.0.2"),
"id_size": json_int(self.id_size),
"timestamp_ms": json_id(self.timestamp_ms),
"file_bytes": json_int(self.file_size()),
"classes": json_int(self.classes),
"instances": json_int(self.instances),
"object_arrays": json_int(self.object_arrays),
"primitive_arrays": json_int(self.primitive_arrays),
"roots": json_int(self.root_count()),
"stack_frames": json_int(self.frames.length()),
"stack_traces": json_int(self.traces.length()),
})
}
///|
pub fn HistogramRow::to_json(self : HistogramRow) -> Json {
Json::object({
"name": Json::string(self.name),
"display_name": Json::string(display_class_name(self.name)),
"class_id": json_id(self.class_id),
"loader_id": json_id(self.loader_id),
"kind": Json::string(self.kind),
"count": json_int(self.count),
"payload_bytes": Json::string(self.payload_bytes.to_string()),
"elements": Json::string(self.elements.to_string()),
})
}
///|
pub fn Summary::histogram_json(self : Summary) -> Json raise ParseError {
Json::array(self.histogram().map(row => row.to_json()))
}
///|
pub fn Summary::histogram_csv(self : Summary) -> String raise ParseError {
let out = StringBuilder()
out.write_string(
"name,class_id,loader_id,kind,count,payload_bytes,elements\r\n",
)
for row in self.histogram() {
let cells = [
row.name,
row.class_id.to_string(),
row.loader_id.to_string(),
row.kind,
row.count.to_string(),
row.payload_bytes.to_string(),
row.elements.to_string(),
]
out.write_string(cells.map(csv_cell).join(","))
out.write_string("\r\n")
}
out.to_string()
}
///|
fn field_json(field : FieldValue) -> Json {
Json::object({
"declaring_class_id": json_id(field.declaring_class_id),
"name": Json::string(field.name),
"value": field.value.to_json(),
})
}
///|
pub fn Summary::class_json(
self : Summary,
id : UInt64,
) -> Json raise ParseError {
let cls = match self.class_info(id) {
Some(cls) => cls
None => raise Invalid(0, "class not found")
}
let fields = cls.fields.map(field => {
Json::object({
"name": Json::string(self.string(field.name_id).unwrap_or("")),
"type": Json::string(value_type_name(field.kind) catch { _ => "unknown" }),
})
})
let constants = cls.constants.map(entry => {
Json::object({
"index": json_int(entry.index),
"value": entry.value.to_json(),
})
})
Json::object({
"id": json_id(id),
"name": Json::string(self.class_name(id).unwrap_or("")),
"superclass_id": json_id(cls.superclass_id),
"loader_id": json_id(cls.loader_id),
"signers_id": json_id(cls.signers_id),
"protection_domain_id": json_id(cls.protection_domain_id),
"reported_instance_size": json_int(cls.instance_size),
"fields": Json::array(fields),
"statics": Json::array(self.static_fields(id).map(field_json)),
"constants": Json::array(constants),
})
}
///|
/// Exports one object; array pages include total length and requested offset.
pub fn Summary::object_json(
self : Summary,
id : UInt64,
offset? : Int = 0,
limit? : Int = 100,
) -> Json raise ParseError {
if self.definitions.contains(id) {
return self.class_json(id)
}
let object = self.require_object(id)
let fields : Map[String, Json] = {
"id": json_id(id),
"kind": Json::string(object.kind.label()),
"class_id": json_id(object.class_id),
"stack_serial": json_id(object.stack_serial),
"payload_bytes": json_int(object.payload_bytes),
"payload_offset": json_int(object.payload_offset),
}
match object.kind {
Instance => {
fields["class_name"] = Json::string(
self.class_name(object.class_id).unwrap_or(""),
)
fields["fields"] = Json::array(self.instance_fields(id).map(field_json))
}
ObjectArray | PrimitiveArray => {
fields["length"] = json_int(object.length)
fields["offset"] = json_int(offset)
fields["values"] = Json::array(
self.array_values(id, offset~, limit~).map(value => value.to_json()),
)
if object.kind == PrimitiveArray {
fields["element_type"] = Json::string(
value_type_name(object.primitive_kind),
)
}
}
}
Json::object(fields)
}
///|
pub fn RootInfo::to_json(self : RootInfo) -> Json {
Json::object({
"kind": Json::string(self.label()),
"object_id": json_id(self.object_id),
"related_id": json_id(self.related_id),
"thread_serial": json_id(self.thread_serial),
"frame_number": json_id(self.frame_number),
})
}
///|
pub fn ReferenceEdge::to_json(self : ReferenceEdge) -> Json {
Json::object({
"source_id": json_id(self.source_id),
"target_id": json_id(self.target_id),
"label": Json::string(self.label),
})
}
///|
pub fn ResolvedFrame::to_json(self : ResolvedFrame) -> Json {
Json::object({
"id": json_id(self.id),
"class_name": Json::string(self.class_name),
"method_name": Json::string(self.method_name),
"signature": Json::string(self.signature),
"source_name": Json::string(self.source_name),
"line_number": json_int(self.line_number),
})
}