Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ Each node shows the call with its argument values, such as
`towerOfHanoi(2, "B", "C", "A")`. Methods drop their class name and the hidden
`this` pointer, so a LeetCode `Solution::` method reads like a plain function.
Nodes widen to fit the arguments (up to 32 characters); hover a node for its full label.
A pointer argument names the heap object it points to, as the Memory tab does,
followed by that object's first plain field: `pre_order(a3:1)` is a call on the node
whose `val` is 1. An unexpected number there, such as a never-set `val`, shows up
without opening Memory.

Calls branch in invocation order and retain recorded return values. L/R labels
mean first/second child calls, not necessarily `left`/`right` fields. The active
Expand Down
17 changes: 14 additions & 3 deletions web/src/callHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ export type CallNode = {id: string; parent: string | null; children: string[]; f
first: number; last: number; state: 'active' | 'waiting' | 'completed' | 'interrupted'; label: string;
returned: boolean; returnValue: string | null};

/** A heap object by name and, when it has one, its value: `a3:1` for a node whose first
* plain field (val, data, key...) holds 1. The value is what the reader recognises;
* the name is what the Memory tab calls the same box. */
function objectName(stop: Snapshot, address: string) {
const object = stop.heap[address];
if (!object) return 'ptr';
const field = object.fields?.find(item => !item.state && !item.type?.includes('*') && item.value != null);
const value = field && shortValue(field.value!).text;
return value && value.length <= 11 ? `${object.allocation_id}:${value}` : object.allocation_id;
}

/** An argument as the reader thinks of it: a pointer names the object it points
* at (a3, null) rather than an address that changes on every run. */
* at (a3:1, null) rather than an address that changes on every run. */
function argument(local: Local, stop: Snapshot) {
const pointer = ownPointer(local);
if (pointer) {
if (pointer.state === 'null') return 'null';
if (pointer.state === 'heap' && pointer.target) return stop.heap[pointer.target]?.allocation_id ?? 'ptr';
if (pointer.state === 'heap' && pointer.target) return objectName(stop, pointer.target);
if (pointer.state === 'dangling') return 'dangling';
return local.value ?? '?';
}
Expand All @@ -23,7 +34,7 @@ function argument(local: Local, stop: Snapshot) {
function returned(value: string | null, stop: Snapshot) {
if (value === null) return null;
const address = value.match(/^(?:\([^)]*\)\s*)?(0x[0-9a-f]+)$/)?.[1];
if (address) return address === '0x0' ? 'null' : stop.heap[address]?.allocation_id ?? value;
if (address) return address === '0x0' ? 'null' : stop.heap[address] ? objectName(stop, address) : value;
return shortValue(value).text;
}

Expand Down
Loading