warpo 2.5.0-alpha-2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/asconfig.schema.json +13 -0
  2. package/dist/heap_analyzer/src/classResolver.d.ts +34 -0
  3. package/dist/heap_analyzer/src/classResolver.js +206 -0
  4. package/dist/heap_analyzer/src/classResolver.js.map +1 -0
  5. package/dist/heap_analyzer/src/constants.d.ts +21 -0
  6. package/dist/heap_analyzer/src/constants.js +30 -0
  7. package/dist/heap_analyzer/src/constants.js.map +1 -0
  8. package/dist/heap_analyzer/src/dominator.d.ts +17 -0
  9. package/dist/heap_analyzer/src/dominator.js +149 -0
  10. package/dist/heap_analyzer/src/dominator.js.map +1 -0
  11. package/dist/heap_analyzer/src/dumpReader.d.ts +2 -0
  12. package/dist/heap_analyzer/src/dumpReader.js +25 -0
  13. package/dist/heap_analyzer/src/dumpReader.js.map +1 -0
  14. package/dist/heap_analyzer/src/dwarfParser.d.ts +117 -0
  15. package/dist/heap_analyzer/src/dwarfParser.js +366 -0
  16. package/dist/heap_analyzer/src/dwarfParser.js.map +1 -0
  17. package/dist/heap_analyzer/src/referenceScanner.d.ts +11 -0
  18. package/dist/heap_analyzer/src/referenceScanner.js +143 -0
  19. package/dist/heap_analyzer/src/referenceScanner.js.map +1 -0
  20. package/dist/heap_analyzer/src/retainedSize.d.ts +9 -0
  21. package/dist/heap_analyzer/src/retainedSize.js +78 -0
  22. package/dist/heap_analyzer/src/retainedSize.js.map +1 -0
  23. package/dist/heap_analyzer/src/roots.d.ts +13 -0
  24. package/dist/heap_analyzer/src/roots.js +45 -0
  25. package/dist/heap_analyzer/src/roots.js.map +1 -0
  26. package/dist/heap_analyzer/src/snapshot.d.ts +2 -0
  27. package/dist/heap_analyzer/src/snapshot.js +170 -0
  28. package/dist/heap_analyzer/src/snapshot.js.map +1 -0
  29. package/dist/heap_analyzer/src/tlsf.d.ts +18 -0
  30. package/dist/heap_analyzer/src/tlsf.js +48 -0
  31. package/dist/heap_analyzer/src/tlsf.js.map +1 -0
  32. package/dist/heap_analyzer/src/types.d.ts +70 -0
  33. package/dist/heap_analyzer/src/types.js +4 -0
  34. package/dist/heap_analyzer/src/types.js.map +1 -0
  35. package/dist/heap_analyzer/tests/classResolver.test.d.ts +1 -0
  36. package/dist/heap_analyzer/tests/classResolver.test.js +110 -0
  37. package/dist/heap_analyzer/tests/classResolver.test.js.map +1 -0
  38. package/dist/heap_analyzer/tests/dominator.test.d.ts +1 -0
  39. package/dist/heap_analyzer/tests/dominator.test.js +438 -0
  40. package/dist/heap_analyzer/tests/dominator.test.js.map +1 -0
  41. package/dist/heap_analyzer/tests/dumpReader.test.d.ts +1 -0
  42. package/dist/heap_analyzer/tests/dumpReader.test.js +51 -0
  43. package/dist/heap_analyzer/tests/dumpReader.test.js.map +1 -0
  44. package/dist/heap_analyzer/tests/dwarfParser.test.d.ts +1 -0
  45. package/dist/heap_analyzer/tests/dwarfParser.test.js +280 -0
  46. package/dist/heap_analyzer/tests/dwarfParser.test.js.map +1 -0
  47. package/dist/heap_analyzer/tests/fixture/dwarfFixture.d.ts +2 -0
  48. package/dist/heap_analyzer/tests/fixture/dwarfFixture.js +266 -0
  49. package/dist/heap_analyzer/tests/fixture/dwarfFixture.js.map +1 -0
  50. package/dist/heap_analyzer/tests/retainedSize.test.d.ts +1 -0
  51. package/dist/heap_analyzer/tests/retainedSize.test.js +57 -0
  52. package/dist/heap_analyzer/tests/retainedSize.test.js.map +1 -0
  53. package/dist/heap_analyzer/tests/roots.test.d.ts +1 -0
  54. package/dist/heap_analyzer/tests/roots.test.js +71 -0
  55. package/dist/heap_analyzer/tests/roots.test.js.map +1 -0
  56. package/dist/heap_analyzer/tests/snapshot.test.d.ts +1 -0
  57. package/dist/heap_analyzer/tests/snapshot.test.js +102 -0
  58. package/dist/heap_analyzer/tests/snapshot.test.js.map +1 -0
  59. package/dist/heap_analyzer/tests/testHelper.d.ts +18 -0
  60. package/dist/heap_analyzer/tests/testHelper.js +68 -0
  61. package/dist/heap_analyzer/tests/testHelper.js.map +1 -0
  62. package/dist/heap_analyzer/tests/tlsf.test.d.ts +1 -0
  63. package/dist/heap_analyzer/tests/tlsf.test.js +98 -0
  64. package/dist/heap_analyzer/tests/tlsf.test.js.map +1 -0
  65. package/dist/heap_analyzer/tests/wasmExecutor.d.ts +9 -0
  66. package/dist/heap_analyzer/tests/wasmExecutor.js +49 -0
  67. package/dist/heap_analyzer/tests/wasmExecutor.js.map +1 -0
  68. package/package.json +10 -5
  69. package/types/std/index.d.ts +0 -4
  70. package/types/warpo/index.d.ts +8 -0
@@ -0,0 +1,57 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { BLOCK_OVERHEAD } from "../src/constants.js";
4
+ import { buildDominatorTree, VIRTUAL_ROOT } from "../src/dominator.js";
5
+ import { computeRetainedSizes } from "../src/retainedSize.js";
6
+ function makeObject(payloadPtr, blockSize) {
7
+ return {
8
+ mmInfo: blockSize,
9
+ rtId: 1,
10
+ rtSize: blockSize,
11
+ payloadPtr,
12
+ gcColor: 0,
13
+ };
14
+ }
15
+ describe("computeRetainedSizes", () => {
16
+ it("computes retained sizes for a simple chain", () => {
17
+ const graph = new Map([
18
+ [10, [20]],
19
+ [20, [30]],
20
+ [30, []],
21
+ ]);
22
+ const domTree = buildDominatorTree(graph, new Set([10]));
23
+ const objects = [makeObject(10, 16), makeObject(20, 24), makeObject(30, 32)];
24
+ const retained = computeRetainedSizes(objects, domTree);
25
+ const shallow10 = BLOCK_OVERHEAD + 16;
26
+ const shallow20 = BLOCK_OVERHEAD + 24;
27
+ const shallow30 = BLOCK_OVERHEAD + 32;
28
+ assert.strictEqual(retained.get(30), shallow30);
29
+ assert.strictEqual(retained.get(20), shallow20 + shallow30);
30
+ assert.strictEqual(retained.get(10), shallow10 + shallow20 + shallow30);
31
+ });
32
+ it("handles a diamond where the join is dominated by the root", () => {
33
+ const graph = new Map([
34
+ [10, [20, 30]],
35
+ [20, [40]],
36
+ [30, [40]],
37
+ [40, []],
38
+ ]);
39
+ const domTree = buildDominatorTree(graph, new Set([10]));
40
+ const objects = [makeObject(10, 16), makeObject(20, 16), makeObject(30, 16), makeObject(40, 16)];
41
+ const retained = computeRetainedSizes(objects, domTree);
42
+ const shallow = BLOCK_OVERHEAD + 16;
43
+ assert.strictEqual(domTree.get(10), VIRTUAL_ROOT);
44
+ assert.strictEqual(domTree.get(20), 10);
45
+ assert.strictEqual(domTree.get(30), 10);
46
+ assert.strictEqual(domTree.get(40), 10);
47
+ assert.strictEqual(retained.get(20), shallow);
48
+ assert.strictEqual(retained.get(30), shallow);
49
+ assert.strictEqual(retained.get(40), shallow);
50
+ assert.strictEqual(retained.get(10), shallow * 4);
51
+ });
52
+ it("throws if a dominator-tree node has no matching object header", () => {
53
+ const domTree = new Map([[10, VIRTUAL_ROOT]]);
54
+ assert.throws(() => computeRetainedSizes([], domTree), /Missing object header for dominator-tree node 10/);
55
+ });
56
+ });
57
+ //# sourceMappingURL=retainedSize.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retainedSize.test.js","sourceRoot":"","sources":["../../../tools/heap_analyzer/tests/retainedSize.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import assert from "node:assert/strict";
2
+ import { before, it } from "node:test";
3
+ import { GC_COLOR_TRANSPARENT } from "../src/constants.js";
4
+ import { parseDumpFile } from "../src/dumpReader.js";
5
+ import { findRoots } from "../src/roots.js";
6
+ import { walkBlocks } from "../src/tlsf.js";
7
+ import { describeIntegration } from "./testHelper.js";
8
+ function loadFixture(ctx) {
9
+ return parseDumpFile(ctx.loadFixtureDumpBuffer());
10
+ }
11
+ describeIntegration("findRoots", (ctx) => {
12
+ let dump;
13
+ let objects;
14
+ let roots;
15
+ before(() => {
16
+ ctx.compileFixture();
17
+ ctx.generateFixtureDump();
18
+ dump = loadFixture(ctx);
19
+ objects = walkBlocks(dump.memory, dump.rtGlobals.heapBase);
20
+ roots = findRoots(dump.memory, dump.rtGlobals, objects);
21
+ });
22
+ it("only reports local roots for the current fixture", () => {
23
+ const rootTypes = new Set(roots.map((root) => root.rootType));
24
+ assert.deepStrictEqual([...rootTypes].toSorted((lhs, rhs) => lhs.localeCompare(rhs)), ["local"]);
25
+ });
26
+ it("finds at least one shadow-stack root", () => {
27
+ const localRoots = roots.filter((root) => root.rootType === "local");
28
+ assert.ok(localRoots.length > 0);
29
+ });
30
+ it("all local roots come from the shadow-stack range", () => {
31
+ for (const root of roots) {
32
+ if (root.rootType !== "local") {
33
+ continue;
34
+ }
35
+ assert.ok(root.sourceAddress >= dump.rtGlobals.stackPointer);
36
+ assert.ok(root.sourceAddress < dump.rtGlobals.heapBase);
37
+ }
38
+ });
39
+ it("returns all transparent objects as pinned roots", () => {
40
+ const pinnedObjectPtrs = new Set(objects.filter((obj) => obj.gcColor === GC_COLOR_TRANSPARENT).map((obj) => obj.payloadPtr));
41
+ const pinnedRoots = roots.filter((root) => root.rootType === "pinned");
42
+ assert.strictEqual(pinnedRoots.length, pinnedObjectPtrs.size);
43
+ for (const root of pinnedRoots) {
44
+ assert.strictEqual(root.sourceAddress, 0);
45
+ assert.ok(pinnedObjectPtrs.has(root.objectPtr));
46
+ }
47
+ });
48
+ it("only reports roots that point to valid object payloads", () => {
49
+ const validPtrs = new Set(objects.map((obj) => obj.payloadPtr));
50
+ for (const root of roots) {
51
+ assert.ok(validPtrs.has(root.objectPtr));
52
+ }
53
+ });
54
+ it("does not attempt GC global-root detection yet", () => {
55
+ assert.strictEqual(roots.some((root) => root.rootType === "global"), false);
56
+ });
57
+ });
58
+ it("reports transparent-color objects as pinned roots", () => {
59
+ const memory = new DataView(new ArrayBuffer(64));
60
+ const objects = [
61
+ { mmInfo: 24, rtId: 1, rtSize: 8, payloadPtr: 16, gcColor: 0 },
62
+ { mmInfo: 24, rtId: 2, rtSize: 8, payloadPtr: 32, gcColor: GC_COLOR_TRANSPARENT },
63
+ ];
64
+ memory.setUint32(0, 16, true);
65
+ const roots = findRoots(memory, { dataEnd: 0, stackPointer: 0, heapBase: 4 }, objects);
66
+ const pinnedRoots = roots.filter((root) => root.rootType === "pinned");
67
+ const localRoots = roots.filter((root) => root.rootType === "local");
68
+ assert.deepStrictEqual(localRoots, [{ objectPtr: 16, className: "", rootType: "local", sourceAddress: 0 }]);
69
+ assert.deepStrictEqual(pinnedRoots, [{ objectPtr: 32, className: "", rootType: "pinned", sourceAddress: 0 }]);
70
+ });
71
+ //# sourceMappingURL=roots.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roots.test.js","sourceRoot":"","sources":["../../../tools/heap_analyzer/tests/roots.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,102 @@
1
+ import assert from "node:assert/strict";
2
+ import { before, it } from "node:test";
3
+ import { parseDumpFile } from "../src/dumpReader.js";
4
+ import { analyzeHeap } from "../src/snapshot.js";
5
+ import { CLASS_PREFIX, describeIntegration } from "./testHelper.js";
6
+ function loadFixture(ctx) {
7
+ return parseDumpFile(ctx.loadFixtureDumpBuffer());
8
+ }
9
+ describeIntegration("analyzeHeap", (ctx) => {
10
+ let dump;
11
+ let snapshot;
12
+ let objectCounts;
13
+ before(() => {
14
+ ctx.compileFixture();
15
+ ctx.generateFixtureDump();
16
+ dump = loadFixture(ctx);
17
+ snapshot = analyzeHeap(dump.memory, dump.rtGlobals, ctx.loadFixtureWasm());
18
+ objectCounts = new Map();
19
+ for (const obj of snapshot.objects) {
20
+ objectCounts.set(obj.className, (objectCounts.get(obj.className) ?? 0) + 1);
21
+ }
22
+ });
23
+ it("matches the fixture's live-object and root counts", () => {
24
+ assert.strictEqual(snapshot.objectCount, 89);
25
+ assert.strictEqual(snapshot.objects.length, 89);
26
+ assert.strictEqual(snapshot.roots.length, 23);
27
+ });
28
+ it("reports only local roots for the current fixture", () => {
29
+ const rootTypeCounts = {};
30
+ for (const root of snapshot.roots) {
31
+ rootTypeCounts[root.rootType] = (rootTypeCounts[root.rootType] ?? 0) + 1;
32
+ }
33
+ assert.deepStrictEqual(rootTypeCounts, { local: 23 });
34
+ });
35
+ it("propagates local rootType to all live objects", () => {
36
+ const objectRootTypeCounts = {};
37
+ for (const obj of snapshot.objects) {
38
+ objectRootTypeCounts[obj.rootType] = (objectRootTypeCounts[obj.rootType] ?? 0) + 1;
39
+ }
40
+ assert.deepStrictEqual(objectRootTypeCounts, { local: 89 });
41
+ });
42
+ it("matches expected class counts for key fixture types", () => {
43
+ assert.strictEqual(objectCounts.get(`${CLASS_PREFIX}TreeNode`), 31);
44
+ assert.strictEqual(objectCounts.get("~lib/arraybuffer/ArrayBuffer"), 18);
45
+ assert.strictEqual(objectCounts.get(`${CLASS_PREFIX}Item`), 10);
46
+ assert.strictEqual(objectCounts.get(`${CLASS_PREFIX}Vector2`), 6);
47
+ assert.strictEqual(objectCounts.get(`${CLASS_PREFIX}ListNode`), 5);
48
+ assert.strictEqual(objectCounts.get(`${CLASS_PREFIX}NPC`), 3);
49
+ assert.strictEqual(objectCounts.get(`${CLASS_PREFIX}Player`), 2);
50
+ assert.strictEqual(objectCounts.get("~lib/string/String"), 1);
51
+ });
52
+ it("keeps summary sorted by retained size and matches key summary rows", () => {
53
+ for (let index = 1; index < snapshot.summary.length; index++) {
54
+ assert.ok(snapshot.summary[index - 1].totalRetainedSize >= snapshot.summary[index].totalRetainedSize);
55
+ }
56
+ assert.deepStrictEqual(snapshot.summary[0], {
57
+ className: `${CLASS_PREFIX}TreeNode`,
58
+ classId: snapshot.summary[0].classId,
59
+ count: 31,
60
+ totalShallowSize: 992,
61
+ totalRetainedSize: 4128,
62
+ });
63
+ const players = snapshot.summary.find((entry) => entry.className === `${CLASS_PREFIX}Player`);
64
+ const npc = snapshot.summary.find((entry) => entry.className === `${CLASS_PREFIX}NPC`);
65
+ const strings = snapshot.summary.find((entry) => entry.className === "~lib/string/String");
66
+ assert.deepStrictEqual(players && {
67
+ count: players.count,
68
+ shallow: players.totalShallowSize,
69
+ retained: players.totalRetainedSize,
70
+ }, { count: 2, shallow: 96, retained: 544 });
71
+ assert.deepStrictEqual(npc && {
72
+ count: npc.count,
73
+ shallow: npc.totalShallowSize,
74
+ retained: npc.totalRetainedSize,
75
+ }, { count: 3, shallow: 144, retained: 240 });
76
+ assert.deepStrictEqual(strings && {
77
+ count: strings.count,
78
+ shallow: strings.totalShallowSize,
79
+ retained: strings.totalRetainedSize,
80
+ }, { count: 1, shallow: 304, retained: 304 });
81
+ });
82
+ it("computes totals consistent with the object and summary rows", () => {
83
+ const totalLiveSize = snapshot.objects.reduce((sum, obj) => sum + obj.shallowSize, 0);
84
+ const totalCount = snapshot.summary.reduce((sum, entry) => sum + entry.count, 0);
85
+ const totalShallowSize = snapshot.summary.reduce((sum, entry) => sum + entry.totalShallowSize, 0);
86
+ assert.strictEqual(snapshot.totalLiveSize, totalLiveSize);
87
+ assert.strictEqual(totalCount, snapshot.objects.length);
88
+ assert.strictEqual(totalShallowSize, snapshot.totalLiveSize);
89
+ assert.ok(snapshot.totalHeapSize >= snapshot.totalLiveSize);
90
+ assert.strictEqual(snapshot.totalFreeSize, snapshot.totalHeapSize - snapshot.totalLiveSize);
91
+ assert.ok(snapshot.totalFreeSize >= 0);
92
+ });
93
+ it("enriches every root with a resolved class name present in the object set", () => {
94
+ const objectAddresses = new Set(snapshot.objects.map((obj) => obj.address));
95
+ assert.ok(snapshot.roots.length > 0);
96
+ for (const root of snapshot.roots) {
97
+ assert.notStrictEqual(root.className, "");
98
+ assert.ok(objectAddresses.has(root.objectPtr));
99
+ }
100
+ });
101
+ });
102
+ //# sourceMappingURL=snapshot.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.test.js","sourceRoot":"","sources":["../../../tools/heap_analyzer/tests/snapshot.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export declare const PROJECT_ROOT: string;
2
+ export declare const WARPO_ASC: string;
3
+ export declare const FIXTURE_SRC: string;
4
+ export declare const CLASS_PREFIX = "tools/heap_analyzer/tests/fixture/dwarfFixture/";
5
+ export declare const canRunIntegration: boolean;
6
+ export interface FixtureContext {
7
+ compileFixture(): void;
8
+ loadFixtureWasm(): Uint8Array;
9
+ generateFixtureDump(): void;
10
+ loadFixtureDumpBuffer(): ArrayBuffer;
11
+ }
12
+ /**
13
+ * Always registers the test suite. If warpo_asc is missing, the suite
14
+ * fails immediately instead of being silently skipped.
15
+ *
16
+ * Each suite gets its own build directory to allow parallel execution.
17
+ */
18
+ export declare function describeIntegration(name: string, fn: (ctx: FixtureContext) => void): void;
@@ -0,0 +1,68 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { platform } from "node:os";
6
+ import { before, after, describe } from "node:test";
7
+ import { executeFixture } from "./wasmExecutor.js";
8
+ const TESTS_DIR = dirname(fileURLToPath(import.meta.url));
9
+ export const PROJECT_ROOT = resolve(TESTS_DIR, "../../..");
10
+ const WARPO_ASC_NAME = platform() === "win32" ? "warpo_asc.exe" : "warpo_asc";
11
+ export const WARPO_ASC = resolve(PROJECT_ROOT, "build/warpo", WARPO_ASC_NAME);
12
+ export const FIXTURE_SRC = resolve(TESTS_DIR, "fixture/dwarfFixture.ts");
13
+ export const CLASS_PREFIX = "tools/heap_analyzer/tests/fixture/dwarfFixture/";
14
+ export const canRunIntegration = existsSync(WARPO_ASC);
15
+ // The fixture wasm hardcodes the dump output path as
16
+ // "./tools/heap_analyzer/tests/fixture/build/example.dump" (relative to PROJECT_ROOT).
17
+ // All suites share this path because the dump content is deterministic.
18
+ const SHARED_DUMP_PATH = resolve(TESTS_DIR, "fixture/build/example.dump");
19
+ function createFixtureContext(suiteName) {
20
+ const buildDir = resolve(TESTS_DIR, `fixture/build-${suiteName}`);
21
+ const wasmPath = resolve(buildDir, "dwarfFixture.wasm");
22
+ function ensureBuildDir() {
23
+ if (!existsSync(buildDir)) {
24
+ mkdirSync(buildDir, { recursive: true });
25
+ }
26
+ }
27
+ return {
28
+ compileFixture() {
29
+ ensureBuildDir();
30
+ execFileSync(WARPO_ASC, [FIXTURE_SRC, "-o", wasmPath, "--debug", "--exportRuntime"], {
31
+ cwd: PROJECT_ROOT,
32
+ stdio: "pipe",
33
+ });
34
+ },
35
+ loadFixtureWasm() {
36
+ return new Uint8Array(readFileSync(wasmPath));
37
+ },
38
+ generateFixtureDump() {
39
+ executeFixture(wasmPath, PROJECT_ROOT);
40
+ },
41
+ loadFixtureDumpBuffer() {
42
+ const buf = readFileSync(SHARED_DUMP_PATH);
43
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
44
+ },
45
+ };
46
+ }
47
+ /**
48
+ * Always registers the test suite. If warpo_asc is missing, the suite
49
+ * fails immediately instead of being silently skipped.
50
+ *
51
+ * Each suite gets its own build directory to allow parallel execution.
52
+ */
53
+ export function describeIntegration(name, fn) {
54
+ const buildDir = resolve(TESTS_DIR, `fixture/build-${name}`);
55
+ const ctx = createFixtureContext(name);
56
+ describe(name, () => {
57
+ before(() => {
58
+ if (!canRunIntegration) {
59
+ throw new Error(`Integration test "${name}" requires warpo_asc at ${WARPO_ASC}. ` + `Please build with: npm run build:cpp`);
60
+ }
61
+ });
62
+ after(() => {
63
+ rmSync(buildDir, { recursive: true, force: true });
64
+ });
65
+ fn(ctx);
66
+ });
67
+ }
68
+ //# sourceMappingURL=testHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testHelper.js","sourceRoot":"","sources":["../../../tools/heap_analyzer/tests/testHelper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,98 @@
1
+ import assert from "node:assert/strict";
2
+ import { before, it } from "node:test";
3
+ import { parseDumpFile } from "../src/dumpReader.js";
4
+ import { walkBlocks } from "../src/tlsf.js";
5
+ import { FREE, TAGS_MASK, TOTAL_OVERHEAD, BLOCK_OVERHEAD, AL_MASK, ROOT_SIZE, COLOR_MASK } from "../src/constants.js";
6
+ import { describeIntegration } from "./testHelper.js";
7
+ function loadFixture(ctx) {
8
+ return parseDumpFile(ctx.loadFixtureDumpBuffer());
9
+ }
10
+ describeIntegration("walkBlocks", (ctx) => {
11
+ let dump;
12
+ let blocks;
13
+ before(() => {
14
+ ctx.compileFixture();
15
+ ctx.generateFixtureDump();
16
+ dump = loadFixture(ctx);
17
+ blocks = walkBlocks(dump.memory, dump.rtGlobals.heapBase);
18
+ });
19
+ it("returns 110 used blocks from the fixture", () => {
20
+ assert.strictEqual(blocks.length, 110);
21
+ });
22
+ it("all returned blocks are used (not free)", () => {
23
+ for (const b of blocks) {
24
+ assert.strictEqual(b.mmInfo & FREE, 0);
25
+ }
26
+ });
27
+ it("all blocks have positive size", () => {
28
+ for (const b of blocks) {
29
+ assert.ok((b.mmInfo & ~TAGS_MASK) > 0);
30
+ }
31
+ });
32
+ it("payloadPtr increases monotonically", () => {
33
+ for (let i = 1; i < blocks.length; i++) {
34
+ assert.ok(blocks[i].payloadPtr > blocks[i - 1].payloadPtr);
35
+ }
36
+ });
37
+ it("all payloadPtrs are within memory bounds", () => {
38
+ for (const b of blocks) {
39
+ assert.ok(b.payloadPtr < dump.memory.byteLength);
40
+ }
41
+ });
42
+ it("gcColor is in range [0, 3]", () => {
43
+ for (const b of blocks) {
44
+ assert.ok(b.gcColor >= 0);
45
+ assert.ok(b.gcColor <= COLOR_MASK);
46
+ }
47
+ });
48
+ it("first block payloadPtr matches computed firstBlock + TOTAL_OVERHEAD", () => {
49
+ const heapBase = dump.rtGlobals.heapBase;
50
+ const tlsfRoot = (heapBase + AL_MASK) & ~AL_MASK;
51
+ const firstBlock = ((tlsfRoot + ROOT_SIZE + BLOCK_OVERHEAD + AL_MASK) & ~AL_MASK) - BLOCK_OVERHEAD;
52
+ assert.strictEqual(blocks[0].payloadPtr, firstBlock + TOTAL_OVERHEAD);
53
+ });
54
+ });
55
+ describeIntegration("object header parsing", (ctx) => {
56
+ let blocks;
57
+ before(() => {
58
+ ctx.compileFixture();
59
+ ctx.generateFixtureDump();
60
+ const dump = loadFixture(ctx);
61
+ blocks = walkBlocks(dump.memory, dump.rtGlobals.heapBase);
62
+ });
63
+ it("contains expected rtId values", () => {
64
+ const rtIds = new Set(blocks.map((b) => b.rtId));
65
+ assert.ok(rtIds.has(1));
66
+ assert.ok(rtIds.has(2));
67
+ assert.ok(rtIds.has(13));
68
+ });
69
+ it("rtId distribution matches fixture", () => {
70
+ const counts = {};
71
+ for (const b of blocks) {
72
+ counts[b.rtId] = (counts[b.rtId] || 0) + 1;
73
+ }
74
+ assert.strictEqual(counts[1], 29);
75
+ assert.strictEqual(counts[2], 4);
76
+ assert.strictEqual(counts[13], 31);
77
+ });
78
+ it("block at index 4 has rtId 14, rtSize 12, blockSize 28", () => {
79
+ assert.strictEqual(blocks[4].rtId, 14);
80
+ assert.strictEqual(blocks[4].rtSize, 12);
81
+ assert.strictEqual(blocks[4].mmInfo & ~TAGS_MASK, 28);
82
+ });
83
+ it("blocks with rtId 2 have non-zero rtSize", () => {
84
+ const strings = blocks.filter((b) => b.rtId === 2);
85
+ assert.strictEqual(strings.length, 4);
86
+ for (const s of strings) {
87
+ assert.ok(s.rtSize > 0);
88
+ }
89
+ });
90
+ it("blocks with rtId 10 all have rtSize 8", () => {
91
+ const items = blocks.filter((b) => b.rtId === 10);
92
+ assert.strictEqual(items.length, 13);
93
+ for (const item of items) {
94
+ assert.strictEqual(item.rtSize, 8);
95
+ }
96
+ });
97
+ });
98
+ //# sourceMappingURL=tlsf.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tlsf.test.js","sourceRoot":"","sources":["../../../tools/heap_analyzer/tests/tlsf.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Instantiate a compiled wasm fixture and run its `_start` entry point.
3
+ * The injected `MemoryDump.dumpMemoryRegion` import writes a dump
4
+ * file whose path is decoded (UTF-8) from linear memory.
5
+ *
6
+ * The fixture may abort after the dump is written (e.g. debug assertions);
7
+ * such aborts are silently ignored.
8
+ */
9
+ export declare function executeFixture(wasmPath: string, projectRoot: string): void;
@@ -0,0 +1,49 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ const DUMP_MAGIC = new Uint8Array([0x41, 0x53, 0x48, 0x44]); // "A S H D"
4
+ const DUMP_VERSION = 1;
5
+ const HEADER_SIZE = 24;
6
+ function writeDump(exports, outputPath) {
7
+ const raw = new Uint8Array(exports.memory.buffer);
8
+ const dump = new Uint8Array(HEADER_SIZE + raw.byteLength);
9
+ const view = new DataView(dump.buffer);
10
+ dump.set(DUMP_MAGIC, 0);
11
+ view.setUint32(4, DUMP_VERSION, true);
12
+ view.setUint32(8, Number(exports.__data_end.value), true);
13
+ view.setUint32(12, Number(exports.__heap_base.value), true);
14
+ view.setUint32(16, Number(exports.__stack_pointer.value), true);
15
+ dump.set(raw, HEADER_SIZE);
16
+ mkdirSync(dirname(outputPath), { recursive: true });
17
+ writeFileSync(outputPath, dump);
18
+ }
19
+ /**
20
+ * Instantiate a compiled wasm fixture and run its `_start` entry point.
21
+ * The injected `MemoryDump.dumpMemoryRegion` import writes a dump
22
+ * file whose path is decoded (UTF-8) from linear memory.
23
+ *
24
+ * The fixture may abort after the dump is written (e.g. debug assertions);
25
+ * such aborts are silently ignored.
26
+ */
27
+ export function executeFixture(wasmPath, projectRoot) {
28
+ const wasmBytes = readFileSync(wasmPath);
29
+ // eslint-disable-next-line prefer-const
30
+ let exports;
31
+ const imports = {
32
+ env: {
33
+ abort(message, fileName, line, column) {
34
+ throw new Error(`abort at ${line}:${column} (msg=${message}, file=${fileName})`);
35
+ },
36
+ },
37
+ MemoryDump: {
38
+ dumpMemoryRegion(offset, size) {
39
+ const guestPath = Buffer.from(exports.memory.buffer, offset, size).toString("utf8");
40
+ writeDump(exports, resolve(projectRoot, guestPath));
41
+ },
42
+ },
43
+ };
44
+ const mod = new WebAssembly.Module(wasmBytes);
45
+ const instance = new WebAssembly.Instance(mod, imports);
46
+ exports = instance.exports;
47
+ exports._start();
48
+ }
49
+ //# sourceMappingURL=wasmExecutor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasmExecutor.js","sourceRoot":"","sources":["../../../tools/heap_analyzer/tests/wasmExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY;AACzE,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,WAAW,GAAG,EAAE,CAAC;AAUvB,SAAS,SAAS,CAAC,OAAmB,EAAE,UAAkB;IACxD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,WAAW,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEvC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACxB,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAChE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAE3B,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,WAAmB;IAClE,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAEzC,wCAAwC;IACxC,IAAI,OAAmB,CAAC;IAExB,MAAM,OAAO,GAAwB;QACnC,GAAG,EAAE;YACH,KAAK,CAAC,OAAe,EAAE,QAAgB,EAAE,IAAY,EAAE,MAAc;gBACnE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,IAAI,MAAM,SAAS,OAAO,UAAU,QAAQ,GAAG,CAAC,CAAC;YACnF,CAAC;SACF;QACD,UAAU,EAAE;YACV,gBAAgB,CAAC,MAAc,EAAE,IAAY;gBAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACpF,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;YACtD,CAAC;SACF;KACF,CAAC;IAEF,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxD,OAAO,GAAG,QAAQ,CAAC,OAAqB,CAAC;IAEzC,OAAO,CAAC,MAAM,EAAE,CAAC;AACnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warpo",
3
- "version": "2.5.0-alpha-2",
3
+ "version": "3.0.0",
4
4
  "description": "next generation AssemblyScript compiler with optimizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "scripts": {
10
10
  "update": "cmake -S . -B build && cmake --build build --parallel --target warpo_binaryen_enums_gen && ./build/bin/warpo_binaryen_enums_gen",
11
11
  "build:ts": "tsc -p tools/tsconfig.json",
12
- "build:cpp": "cmake -S . -B build && cmake --build build --parallel",
12
+ "build:parser:ts": "node assemblyscript/scripts/build-parser.js",
13
+ "build:cpp": "cmake -S . -B build && cmake --build build --parallel 4",
13
14
  "build": "npm run build:ts && npm run build:cpp",
14
15
  "test:as:ut": "node dist/warpo.js test",
15
16
  "test:cpp:ut": "cd build && ctest --output-on-failure",
@@ -20,6 +21,7 @@
20
21
  "test:debug_symbol": "cross-env-shell ./build/tests/dwarf/TestDebugSymbol",
21
22
  "test:debug_symbol:update": "cross-env-shell build/tests/dwarf/TestDebugSymbol --update-fixtures",
22
23
  "test:debug_server": "node --import tsx --test tools/debug_server/tests/**/*.test.ts",
24
+ "test:heap_analyzer": "cd tools/heap_analyzer && npm test",
23
25
  "test:driver": "node tests/driver/index.mjs",
24
26
  "test": "npm run test:as:ut && npm run test:cpp:ut && npm run test:as:snapshot && npm run test:opt:snapshot && npm run test:driver && npm run test:debug_symbol",
25
27
  "test:all": "npm run test && npm run test:bootstrap:debug && npm run test:bootstrap:release",
@@ -28,7 +30,7 @@
28
30
  "docs:build": "vitepress build docs",
29
31
  "prettier": "prettier --check .",
30
32
  "prettier:fix": "prettier --write .",
31
- "lint": "npx eslint --config tools/debug_server/eslint.config.mjs tools/debug_server/ && npx eslint --config debugger/eslint.config.mjs debugger/src/",
33
+ "lint": "npx eslint --config ./eslint.config.mjs tools/debug_server/ tools/heap_analyzer/ debugger/src/",
32
34
  "cspell": "cspell ."
33
35
  },
34
36
  "license": "Apache-2.0",
@@ -44,6 +46,7 @@
44
46
  },
45
47
  "files": [
46
48
  "asconfig.schema.json",
49
+ "assemblyscript/build-parser/**/*",
47
50
  "assemblyscript/std/assembly/index.d.ts",
48
51
  "create",
49
52
  "dist",
@@ -51,7 +54,6 @@
51
54
  "types"
52
55
  ],
53
56
  "devDependencies": {
54
- "@assemblyscript/loader": "^0.28.9",
55
57
  "@eslint/js": "^9.0.0",
56
58
  "@types/fs-extra": "^11.0.4",
57
59
  "@types/node": "^22.15.21",
@@ -63,6 +65,7 @@
63
65
  "cross-env": "^10.1.0",
64
66
  "cspell": "^9.3.0",
65
67
  "diff": "^8.0.0",
68
+ "esbuild": "^0.27.0",
66
69
  "eslint": "^9.0.0",
67
70
  "eslint-import-resolver-typescript": "^4.4.4",
68
71
  "eslint-plugin-import": "^2.32.0",
@@ -80,6 +83,7 @@
80
83
  "vitepress": "^2.0.0-alpha.15"
81
84
  },
82
85
  "dependencies": {
86
+ "@assemblyscript/loader": "^0.28.9",
83
87
  "@vscode/debugadapter": "^1.68.0",
84
88
  "@vscode/debugprotocol": "^1.68.0",
85
89
  "chalk": "^5.6.2",
@@ -87,6 +91,7 @@
87
91
  "fs-extra": "^11.3.3",
88
92
  "glob": "^13.0.0",
89
93
  "ignore": "^7.0.5",
90
- "source-map": "^0.7.6"
94
+ "source-map": "^0.7.6",
95
+ "wasmparser": "^5.11.1"
91
96
  }
92
97
  }
@@ -1521,10 +1521,6 @@ declare namespace f64x2 {
1521
1521
  export function relaxed_max(a: v128, b: v128): v128;
1522
1522
  }
1523
1523
 
1524
- declare namespace ffi {
1525
- function set_ffi_closure_env(value: i32): void;
1526
- }
1527
-
1528
1524
  declare abstract class i31 {
1529
1525
  /** Creates a new 31-bit integer reference from the specified integer value. */
1530
1526
  static new(value: i32): ref_i31;
@@ -1,5 +1,13 @@
1
1
  /// <reference path="../std/index.d.ts" />
2
2
 
3
+ declare module "warpo/ffi" {
4
+ namespace ffi {
5
+ function set_ffi_closure_env(value: i32): void;
6
+ function multi_return_to_tuple<T>(value: MultiReturn<T>): T;
7
+ class MultiReturn<T> {}
8
+ }
9
+ }
10
+
3
11
  declare module "rt/index" {
4
12
  // allocate new object with size and rtid
5
13
  function __new(size: usize, id: u32): usize;