(root, depth_limit)
| 490 | |
| 491 | // This function assumes the pointer fields to be ptr-size aligned. |
| 492 | function print_objects_tree(root, depth_limit) { |
| 493 | if(!is_likely_object(root)) { |
| 494 | print(`${hex(root)} doesn't look like an object`); |
| 495 | return; |
| 496 | } |
| 497 | |
| 498 | let path = []; |
| 499 | |
| 500 | function impl(obj, depth, depth_limit) { |
| 501 | const ptr_size = tagged_size(); |
| 502 | // print the current object and its map pointer |
| 503 | const this_obj = |
| 504 | `${" ".repeat(2 * depth)}${hex(obj)} : ${hex(poim(obj - 1))}`; |
| 505 | const cutoff = depth_limit && depth == depth_limit - 1; |
| 506 | print(`${this_obj}${cutoff ? " (...)" : ""}`); |
| 507 | if (cutoff) return; |
| 508 | |
| 509 | path[depth] = obj; |
| 510 | path.length = depth + 1; |
| 511 | let cur = obj - 1 + ptr_size; |
| 512 | |
| 513 | // Scan downwards until an address that is likely to be at the start of |
| 514 | // another object, in which case it's time to pop out from the recursion. |
| 515 | let iter = 0; // an arbitrary guard to avoid hanging the debugger |
| 516 | let seen = new Set(path); |
| 517 | while (!is_likely_object(cur + 1) && iter < 100) { |
| 518 | iter++; |
| 519 | let field = poim(cur); |
| 520 | if (is_likely_object(field)) { |
| 521 | if (seen.has(field)) { |
| 522 | print( |
| 523 | `${" ".repeat(2 * depth + 2)}cycle: ${hex(cur)}->${hex(field)}`); |
| 524 | } |
| 525 | else { |
| 526 | impl(field, depth + 1, depth_limit); |
| 527 | } |
| 528 | } |
| 529 | cur += ptr_size; |
| 530 | } |
| 531 | } |
| 532 | print(`===============================================`); |
| 533 | impl(root, 0, depth_limit); |
| 534 | print(`===============================================`); |
| 535 | } |
| 536 | |
| 537 | /*----------------------------------------------------------------------------- |
| 538 | Memory spaces |
nothing calls this directly
no test coverage detected
searching dependent graphs…