@jshookmcp/jshook 0.2.8 → 0.2.9
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.
- package/README.md +36 -5
- package/README.zh.md +36 -5
- package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-BNk-EoBt.mjs} +3 -3
- package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-Cq8q01kp.mjs} +5 -5
- package/dist/ConsoleMonitor-CPVQW1Y-.mjs +2201 -0
- package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-BNPxu0RH.mjs} +1 -1
- package/dist/DetailedDataManager-BQQcxh64.mjs +217 -0
- package/dist/EventBus-DgPmwpeu.mjs +141 -0
- package/dist/EvidenceGraphBridge-SFesNera.mjs +153 -0
- package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-CWYgw0YW.mjs} +13 -6
- package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-gzWtkKuf.mjs} +1 -1
- package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-B9gZCdFP.mjs} +3 -3
- package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-BLDH0dCv.mjs} +4 -4
- package/dist/HookGeneratorBuilders.core.generators.storage-CtcdK78Q.mjs +639 -0
- package/dist/InstrumentationSession-CvPC7Jwy.mjs +244 -0
- package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CbVdCIJF.mjs} +3 -3
- package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-BsDZbLYm.mjs} +81 -78
- package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-Bcpml6II.mjs} +44 -18
- package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-dZtA1ZGn.mjs} +14 -53
- package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-B-FjA2mJ.mjs} +1 -1
- package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-D1lzJ_VG.mjs} +2 -2
- package/dist/PageController-Bqm2kZ_X.mjs +417 -0
- package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-BOhyVsjx.mjs} +4 -4
- package/dist/PrerequisiteError-Dl33Svkz.mjs +20 -0
- package/dist/ResponseBuilder-D3iFYx2N.mjs +143 -0
- package/dist/ReverseEvidenceGraph-Dlsk94LC.mjs +269 -0
- package/dist/ScriptManager-aHHq0X7U.mjs +3000 -0
- package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-CqdIFlQl.mjs} +2 -2
- package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-DhFaPvRO.mjs} +3 -3
- package/dist/ToolCatalog-C0JGZoOm.mjs +582 -0
- package/dist/ToolError-jh9whhMd.mjs +15 -0
- package/dist/ToolProbe-oC7aPrkv.mjs +45 -0
- package/dist/ToolRegistry-BjaF4oNz.mjs +131 -0
- package/dist/ToolRouter.policy-BWV67ZK-.mjs +304 -0
- package/dist/TraceRecorder-DgxyVbdQ.mjs +519 -0
- package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-CePkipZY.mjs} +1 -1
- package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-BvKs-gxc.mjs} +2 -2
- package/dist/WorkflowEngine-CuvkZtWu.mjs +598 -0
- package/dist/analysis-CL9uACt9.mjs +463 -0
- package/dist/antidebug-CqDTB_uk.mjs +1081 -0
- package/dist/artifactRetention-CFEprwPw.mjs +591 -0
- package/dist/artifacts-Bk2-_uPq.mjs +59 -0
- package/dist/betterSqlite3-0pqusHHH.mjs +74 -0
- package/dist/binary-instrument-CXfpx6fT.mjs +979 -0
- package/dist/bind-helpers-xFfRF-qm.mjs +22 -0
- package/dist/boringssl-inspector-BH2D3VKc.mjs +180 -0
- package/dist/browser-BpOr5PEx.mjs +4082 -0
- package/dist/concurrency-Bt0yv1kJ.mjs +41 -0
- package/dist/{constants-CCvsN80K.mjs → constants-B0OANIBL.mjs} +88 -46
- package/dist/coordination-qUbyF8KU.mjs +259 -0
- package/dist/debugger-gnKxRSN0.mjs +1271 -0
- package/dist/definitions-6M-eejaT.mjs +53 -0
- package/dist/definitions-B18eyf0B.mjs +18 -0
- package/dist/definitions-B3QdlrHv.mjs +34 -0
- package/dist/definitions-B4rAvHNZ.mjs +63 -0
- package/dist/definitions-BB_4jnmy.mjs +37 -0
- package/dist/definitions-BMfYXoNC.mjs +43 -0
- package/dist/definitions-Beid2EB3.mjs +27 -0
- package/dist/definitions-C1UvM5Iy.mjs +126 -0
- package/dist/definitions-CXEI7QC72.mjs +216 -0
- package/dist/definitions-C_4r7Fo-2.mjs +14 -0
- package/dist/definitions-CkFDALoa.mjs +26 -0
- package/dist/definitions-Cke7zEb8.mjs +94 -0
- package/dist/definitions-ClJLzsJQ.mjs +25 -0
- package/dist/definitions-Cq-zroAU.mjs +28 -0
- package/dist/definitions-Cy3Sl6gV.mjs +34 -0
- package/dist/definitions-D3VsGcvz.mjs +47 -0
- package/dist/definitions-DVGfrn7y.mjs +96 -0
- package/dist/definitions-LKpC3-nL.mjs +9 -0
- package/dist/definitions-bAhHQJq9.mjs +359 -0
- package/dist/encoding-Bvz5jLRv.mjs +1065 -0
- package/dist/evidence-graph-bridge-C_fv9PuC.mjs +135 -0
- package/dist/{factory-CibqTNC8.mjs → factory-DxlGh9Xf.mjs} +37 -52
- package/dist/graphql-DYWzJ29s.mjs +1026 -0
- package/dist/handlers-9sAbfIg-.mjs +2552 -0
- package/dist/handlers-Bl8zkwz1.mjs +2716 -0
- package/dist/handlers-C67ktuRN.mjs +710 -0
- package/dist/handlers-C87g8oCe.mjs +276 -0
- package/dist/handlers-CTsDAO6p.mjs +681 -0
- package/dist/handlers-Cgyg6c0U.mjs +645 -0
- package/dist/handlers-D6j6yka7.mjs +2124 -0
- package/dist/handlers-DdFzXLvF.mjs +446 -0
- package/dist/handlers-DeLOCd5m.mjs +799 -0
- package/dist/handlers-DlCJN4Td.mjs +757 -0
- package/dist/handlers-DxGIq15_2.mjs +917 -0
- package/dist/handlers-U6L4xhuF.mjs +585 -0
- package/dist/handlers-tB9Mp9ZK.mjs +84 -0
- package/dist/handlers-tiy7EIBp.mjs +572 -0
- package/dist/handlers.impl-DS0d9fUw.mjs +761 -0
- package/dist/hooks-CzCWByww.mjs +898 -0
- package/dist/index.mjs +377 -155
- package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
- package/dist/maintenance-P7ePRXQC.mjs +830 -0
- package/dist/manifest-2ToTpjv8.mjs +106 -0
- package/dist/manifest-3g71z6Bg.mjs +79 -0
- package/dist/manifest-82baTv4U.mjs +45 -0
- package/dist/manifest-B3QVVeBS.mjs +82 -0
- package/dist/manifest-BB2J8IMJ.mjs +149 -0
- package/dist/manifest-BKbgbSiY.mjs +60 -0
- package/dist/manifest-Bcf-TJzH.mjs +848 -0
- package/dist/manifest-BmtZzQiQ2.mjs +45 -0
- package/dist/manifest-Bnd7kqEY.mjs +55 -0
- package/dist/manifest-BqQX6OQC2.mjs +65 -0
- package/dist/manifest-BqrQ4Tpj.mjs +81 -0
- package/dist/manifest-Br4RPFt5.mjs +370 -0
- package/dist/manifest-C5qDjysN.mjs +107 -0
- package/dist/manifest-C9RT5nk32.mjs +34 -0
- package/dist/manifest-CAhOuvSl.mjs +204 -0
- package/dist/manifest-CBYWCUBJ.mjs +51 -0
- package/dist/manifest-CFADCRa1.mjs +37 -0
- package/dist/manifest-CQVhavRF.mjs +114 -0
- package/dist/manifest-CT7zZBV1.mjs +48 -0
- package/dist/manifest-CV12bcrF.mjs +121 -0
- package/dist/manifest-CXsRWjjI.mjs +224 -0
- package/dist/manifest-CZLUCfG02.mjs +95 -0
- package/dist/manifest-D6phHKFd.mjs +131 -0
- package/dist/manifest-DCyjf4n2.mjs +294 -0
- package/dist/manifest-DHsnKgP6.mjs +60 -0
- package/dist/manifest-Df_dliIe.mjs +55 -0
- package/dist/manifest-Dh8WBmEW.mjs +129 -0
- package/dist/manifest-DhKRAT8_.mjs +92 -0
- package/dist/manifest-DlpTj4ic2.mjs +193 -0
- package/dist/manifest-DrbmZcFl2.mjs +253 -0
- package/dist/manifest-DuwHjUa5.mjs +70 -0
- package/dist/manifest-DzwvxPJX.mjs +38 -0
- package/dist/manifest-NXctwWQq.mjs +68 -0
- package/dist/manifest-Sc_0JQ13.mjs +418 -0
- package/dist/manifest-gZ4s_UtG.mjs +96 -0
- package/dist/manifest-qSleDqdO.mjs +1023 -0
- package/dist/modules-C184v-S9.mjs +11365 -0
- package/dist/mojo-ipc-B_H61Afw.mjs +525 -0
- package/dist/network-671Cw6hV.mjs +3346 -0
- package/dist/{artifacts-BbdOMET5.mjs → outputPaths-B1uGmrWZ.mjs} +219 -212
- package/dist/parse-args-BlRjqlkL.mjs +39 -0
- package/dist/platform-WmNn8Sxb.mjs +2070 -0
- package/dist/process-QcbIy5Zq.mjs +1401 -0
- package/dist/proxy-DqNs0bAd.mjs +170 -0
- package/dist/registry-D-6e18lB.mjs +34 -0
- package/dist/response-BQVP-xUn.mjs +28 -0
- package/dist/server/plugin-api.mjs +2 -2
- package/dist/shared-state-board-DV-dpHFJ.mjs +586 -0
- package/dist/sourcemap-Dq8ez8vS.mjs +650 -0
- package/dist/ssrf-policy-ZaUfvhq7.mjs +166 -0
- package/dist/streaming-BUQ0VJsg.mjs +725 -0
- package/dist/tool-builder-DCbIC5Eo.mjs +186 -0
- package/dist/transform-CiYJfNX0.mjs +1007 -0
- package/dist/types-Bx92KJfT.mjs +4 -0
- package/dist/wasm-DQTnHDs4.mjs +531 -0
- package/dist/workflow-f3xJOcjx.mjs +725 -0
- package/package.json +16 -16
- package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
- package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
- package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-CDe5WPSV.mjs} +0 -0
- package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-Bo4T3bz8.mjs} +0 -0
- package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-CwVLVdDM.mjs} +0 -0
- package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-DVkj9kpI.mjs} +0 -0
- package/dist/{types-BBjOqye-.mjs → types-CPhOReNX.mjs} +1 -1
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
import { t as VersionDetector } from "./VersionDetector-CwVLVdDM.mjs";
|
|
2
|
+
import { a as argString, r as argNumber } from "./parse-args-BlRjqlkL.mjs";
|
|
3
|
+
import { t as bindByDepKey } from "./bind-helpers-xFfRF-qm.mjs";
|
|
4
|
+
import { t as tool } from "./tool-builder-DCbIC5Eo.mjs";
|
|
5
|
+
//#region src/modules/v8-inspector/V8InspectorClient.ts
|
|
6
|
+
function isRecord$3(value) {
|
|
7
|
+
return typeof value === "object" && value !== null;
|
|
8
|
+
}
|
|
9
|
+
function isCDPPageLike$3(value) {
|
|
10
|
+
return isRecord$3(value) && typeof value["createCDPSession"] === "function";
|
|
11
|
+
}
|
|
12
|
+
function isCDPSessionLike(value) {
|
|
13
|
+
return isRecord$3(value) && typeof value["send"] === "function" && typeof value["detach"] === "function";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* CDP wrapper for V8 HeapProfiler operations.
|
|
17
|
+
*
|
|
18
|
+
* Provides heap snapshot capture, object inspection, and usage stats
|
|
19
|
+
* via the Chrome DevTools Protocol HeapProfiler domain.
|
|
20
|
+
*/
|
|
21
|
+
var V8InspectorClient = class {
|
|
22
|
+
session = null;
|
|
23
|
+
constructor(getPage) {
|
|
24
|
+
this.getPage = getPage;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Enable the HeapProfiler domain via CDP.
|
|
28
|
+
* Must be called before any heap profiling operations.
|
|
29
|
+
*/
|
|
30
|
+
async enableHeapProfiler() {
|
|
31
|
+
const session = await this.createSession();
|
|
32
|
+
if (!session) throw new Error("V8InspectorClient: cannot create CDP session");
|
|
33
|
+
await session.send("HeapProfiler.enable");
|
|
34
|
+
this.session = session;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Take a heap snapshot and collect all chunks via the HeapProfiler.addHeapSnapshotChunk event.
|
|
38
|
+
*
|
|
39
|
+
* @param onChunk - Callback invoked for each snapshot chunk received.
|
|
40
|
+
* @returns Total size of the snapshot in bytes.
|
|
41
|
+
*/
|
|
42
|
+
async takeHeapSnapshot(onChunk) {
|
|
43
|
+
if (!this.session) await this.enableHeapProfiler();
|
|
44
|
+
const session = this.session;
|
|
45
|
+
if (!session) throw new Error("V8InspectorClient: session not available for heap snapshot");
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const chunks = [];
|
|
48
|
+
let totalSize = 0;
|
|
49
|
+
const chunkHandler = (data) => {
|
|
50
|
+
const chunk = data?.chunk;
|
|
51
|
+
if (typeof chunk === "string") {
|
|
52
|
+
chunks.push(chunk);
|
|
53
|
+
totalSize += Buffer.byteLength(chunk, "utf8");
|
|
54
|
+
onChunk?.(chunk);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
session.on("HeapProfiler.addHeapSnapshotChunk", chunkHandler);
|
|
58
|
+
session.send("HeapProfiler.takeHeapSnapshot", { reportProgress: false }).then(() => {
|
|
59
|
+
session.off("HeapProfiler.addHeapSnapshotChunk", chunkHandler);
|
|
60
|
+
resolve(totalSize);
|
|
61
|
+
}).catch((error) => {
|
|
62
|
+
session.off("HeapProfiler.addHeapSnapshotChunk", chunkHandler);
|
|
63
|
+
reject(error);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Retrieve a heap object by its object ID.
|
|
69
|
+
*
|
|
70
|
+
* @param objectId - Heap snapshot object identifier (e.g. "1:1234").
|
|
71
|
+
* @returns The object's properties and metadata.
|
|
72
|
+
*/
|
|
73
|
+
async getObjectByObjectId(_objectId) {
|
|
74
|
+
if (!this.session) await this.enableHeapProfiler();
|
|
75
|
+
const session = this.session;
|
|
76
|
+
if (!session) return null;
|
|
77
|
+
try {
|
|
78
|
+
return await session.send("HeapProfiler.getObjectByObjectId", { objectId: _objectId });
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get current V8 heap usage statistics.
|
|
85
|
+
*
|
|
86
|
+
* @returns Object with jsHeapSizeUsed, jsHeapSizeTotal, jsHeapSizeLimit.
|
|
87
|
+
*/
|
|
88
|
+
async getHeapUsage() {
|
|
89
|
+
if (!this.session) await this.enableHeapProfiler();
|
|
90
|
+
const session = this.session;
|
|
91
|
+
if (!session) return {
|
|
92
|
+
jsHeapSizeUsed: 0,
|
|
93
|
+
jsHeapSizeTotal: 0,
|
|
94
|
+
jsHeapSizeLimit: 0
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
const response = await session.send("HeapProfiler.getHeapUsage");
|
|
98
|
+
const jsHeapSizeUsed = response["jsHeapSizeUsed"];
|
|
99
|
+
const jsHeapSizeTotal = response["jsHeapSizeTotal"];
|
|
100
|
+
const jsHeapSizeLimit = response["jsHeapSizeLimit"];
|
|
101
|
+
return {
|
|
102
|
+
jsHeapSizeUsed: typeof jsHeapSizeUsed === "number" ? jsHeapSizeUsed : 0,
|
|
103
|
+
jsHeapSizeTotal: typeof jsHeapSizeTotal === "number" ? jsHeapSizeTotal : 0,
|
|
104
|
+
jsHeapSizeLimit: typeof jsHeapSizeLimit === "number" ? jsHeapSizeLimit : 0
|
|
105
|
+
};
|
|
106
|
+
} catch {
|
|
107
|
+
try {
|
|
108
|
+
const result = (await session.send("Runtime.evaluate", {
|
|
109
|
+
expression: `
|
|
110
|
+
(() => {
|
|
111
|
+
const m = performance.memory;
|
|
112
|
+
return m ? JSON.stringify({
|
|
113
|
+
jsHeapSizeUsed: m.usedJSHeapSize,
|
|
114
|
+
jsHeapSizeTotal: m.totalJSHeapSize,
|
|
115
|
+
jsHeapSizeLimit: m.jsHeapSizeLimit
|
|
116
|
+
}) : null;
|
|
117
|
+
})()
|
|
118
|
+
`,
|
|
119
|
+
returnByValue: true
|
|
120
|
+
}))["result"];
|
|
121
|
+
if (typeof result === "string" && result !== "null") return JSON.parse(result);
|
|
122
|
+
} catch {}
|
|
123
|
+
return {
|
|
124
|
+
jsHeapSizeUsed: 0,
|
|
125
|
+
jsHeapSizeTotal: 0,
|
|
126
|
+
jsHeapSizeLimit: 0
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Detach the underlying CDP session and release resources.
|
|
132
|
+
*/
|
|
133
|
+
async dispose() {
|
|
134
|
+
if (this.session) {
|
|
135
|
+
await this.session.detach().catch(() => void 0);
|
|
136
|
+
this.session = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async createSession() {
|
|
140
|
+
if (this.session) return this.session;
|
|
141
|
+
if (!this.getPage) return null;
|
|
142
|
+
try {
|
|
143
|
+
const page = await this.getPage();
|
|
144
|
+
if (!isCDPPageLike$3(page)) return null;
|
|
145
|
+
const session = await page.createCDPSession();
|
|
146
|
+
if (!isCDPSessionLike(session)) return null;
|
|
147
|
+
return session;
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/server/domains/v8-inspector/definitions.ts
|
|
155
|
+
const v8InspectorTools = [
|
|
156
|
+
tool("v8_heap_snapshot_capture", (t) => t.desc("Capture a V8 heap snapshot from the active browser target").query()),
|
|
157
|
+
tool("v8_heap_snapshot_analyze", (t) => t.desc("Analyze a previously captured V8 heap snapshot").string("snapshotId", "Heap snapshot identifier").required("snapshotId").query()),
|
|
158
|
+
tool("v8_heap_diff", (t) => t.desc("Diff two captured V8 heap snapshots").string("beforeSnapshotId", "Baseline snapshot identifier").string("afterSnapshotId", "Updated snapshot identifier").required("beforeSnapshotId", "afterSnapshotId").query()),
|
|
159
|
+
tool("v8_object_inspect", (t) => t.desc("Inspect a V8 heap object by address").string("address", "Heap object address").required("address").query()),
|
|
160
|
+
tool("v8_heap_stats", (t) => t.desc("Return V8 heap snapshot statistics").query()),
|
|
161
|
+
tool("v8_bytecode_extract", (t) => t.desc("Extract V8 Ignition bytecode for a function").string("functionId", "CDP RemoteObjectId of the function").required("functionId").query()),
|
|
162
|
+
tool("v8_version_detect", (t) => t.desc("Detect V8 engine version and feature support").query()),
|
|
163
|
+
tool("v8_jit_inspect", (t) => t.desc("Inspect JIT-compiled code for a function").string("functionId", "CDP RemoteObjectId of the function").required("functionId").query())
|
|
164
|
+
];
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/server/domains/v8-inspector/handlers/heap-snapshot.ts
|
|
167
|
+
const snapshotCache = /* @__PURE__ */ new Map();
|
|
168
|
+
function getSnapshotCache() {
|
|
169
|
+
return snapshotCache;
|
|
170
|
+
}
|
|
171
|
+
function storeSnapshot(snapshot) {
|
|
172
|
+
snapshotCache.set(snapshot.id, snapshot);
|
|
173
|
+
return snapshot;
|
|
174
|
+
}
|
|
175
|
+
function getSnapshot(snapshotId) {
|
|
176
|
+
return snapshotCache.get(snapshotId);
|
|
177
|
+
}
|
|
178
|
+
function isRecord$2(v) {
|
|
179
|
+
return typeof v === "object" && v !== null;
|
|
180
|
+
}
|
|
181
|
+
function isCDPPageLike$2(v) {
|
|
182
|
+
return isRecord$2(v) && typeof v["createCDPSession"] === "function";
|
|
183
|
+
}
|
|
184
|
+
async function handleHeapSnapshotCapture(_args, options) {
|
|
185
|
+
const snapshotId = `snapshot_${Date.now().toString(36)}`;
|
|
186
|
+
const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
187
|
+
const chunks = [];
|
|
188
|
+
if (options.client) try {
|
|
189
|
+
const stored = storeSnapshot({
|
|
190
|
+
id: snapshotId,
|
|
191
|
+
chunks,
|
|
192
|
+
capturedAt,
|
|
193
|
+
sizeBytes: await options.client.takeHeapSnapshot((chunk) => {
|
|
194
|
+
chunks.push(chunk);
|
|
195
|
+
})
|
|
196
|
+
});
|
|
197
|
+
options.setSnapshot(snapshotId);
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
snapshotId: stored.id,
|
|
201
|
+
capturedAt: stored.capturedAt,
|
|
202
|
+
sizeBytes: stored.sizeBytes,
|
|
203
|
+
chunks: [],
|
|
204
|
+
simulated: false
|
|
205
|
+
};
|
|
206
|
+
} catch {}
|
|
207
|
+
try {
|
|
208
|
+
const page = await options.getPage();
|
|
209
|
+
if (isCDPPageLike$2(page)) {
|
|
210
|
+
const session = await page.createCDPSession();
|
|
211
|
+
const sessionSend = (method, params) => session.send(method, params);
|
|
212
|
+
const sessionDetach = () => session.detach();
|
|
213
|
+
await sessionSend("HeapProfiler.enable");
|
|
214
|
+
const response = await sessionSend("Runtime.evaluate", {
|
|
215
|
+
expression: `
|
|
216
|
+
(() => {
|
|
217
|
+
const m = performance.memory;
|
|
218
|
+
return m ? JSON.stringify({
|
|
219
|
+
jsHeapSizeUsed: m.usedJSHeapSize,
|
|
220
|
+
jsHeapSizeTotal: m.totalJSHeapSize,
|
|
221
|
+
jsHeapSizeLimit: m.jsHeapSizeLimit
|
|
222
|
+
}) : null;
|
|
223
|
+
})()
|
|
224
|
+
`,
|
|
225
|
+
returnByValue: true
|
|
226
|
+
});
|
|
227
|
+
await sessionDetach().catch(() => void 0);
|
|
228
|
+
const result = isRecord$2(response) ? response["result"] : void 0;
|
|
229
|
+
let sizeBytes = 0;
|
|
230
|
+
if (typeof result === "string" && result !== "null") sizeBytes = JSON.parse(result).jsHeapSizeUsed ?? 0;
|
|
231
|
+
const stored = storeSnapshot({
|
|
232
|
+
id: snapshotId,
|
|
233
|
+
chunks: [`{"simulated":true,"sizeBytes":${sizeBytes}}`],
|
|
234
|
+
capturedAt,
|
|
235
|
+
sizeBytes
|
|
236
|
+
});
|
|
237
|
+
options.setSnapshot(snapshotId);
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
snapshotId: stored.id,
|
|
241
|
+
capturedAt: stored.capturedAt,
|
|
242
|
+
sizeBytes: stored.sizeBytes,
|
|
243
|
+
chunks: [],
|
|
244
|
+
simulated: true
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
} catch {}
|
|
248
|
+
const stored = storeSnapshot({
|
|
249
|
+
id: snapshotId,
|
|
250
|
+
chunks: ["{}"],
|
|
251
|
+
capturedAt,
|
|
252
|
+
sizeBytes: 0
|
|
253
|
+
});
|
|
254
|
+
options.setSnapshot(snapshotId);
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
snapshotId: stored.id,
|
|
258
|
+
capturedAt: stored.capturedAt,
|
|
259
|
+
sizeBytes: stored.sizeBytes,
|
|
260
|
+
chunks: [],
|
|
261
|
+
simulated: true
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/modules/v8-inspector/BytecodeExtractor.ts
|
|
266
|
+
function isRecord$1(value) {
|
|
267
|
+
return typeof value === "object" && value !== null;
|
|
268
|
+
}
|
|
269
|
+
function isCDPPageLike$1(value) {
|
|
270
|
+
return isRecord$1(value) && typeof value["createCDPSession"] === "function";
|
|
271
|
+
}
|
|
272
|
+
function toNumber(value) {
|
|
273
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
274
|
+
}
|
|
275
|
+
function toStringValue(value) {
|
|
276
|
+
return typeof value === "string" ? value : null;
|
|
277
|
+
}
|
|
278
|
+
function toRecordArray(value) {
|
|
279
|
+
if (!Array.isArray(value)) return [];
|
|
280
|
+
return value.filter(isRecord$1);
|
|
281
|
+
}
|
|
282
|
+
function splitOperands(raw) {
|
|
283
|
+
return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
284
|
+
}
|
|
285
|
+
function inferOpcode(line) {
|
|
286
|
+
const trimmed = line.trim();
|
|
287
|
+
if (trimmed.startsWith("function ")) return {
|
|
288
|
+
opcode: "FunctionDeclaration",
|
|
289
|
+
operands: []
|
|
290
|
+
};
|
|
291
|
+
if (trimmed.startsWith("return ")) return {
|
|
292
|
+
opcode: "Return",
|
|
293
|
+
operands: [trimmed.slice(7)]
|
|
294
|
+
};
|
|
295
|
+
if (trimmed.includes("=>")) return {
|
|
296
|
+
opcode: "CreateClosure",
|
|
297
|
+
operands: []
|
|
298
|
+
};
|
|
299
|
+
if (trimmed.includes("(") && trimmed.includes(")")) {
|
|
300
|
+
const nameMatch = /^([A-Za-z_$][\w$]*)\(/u.exec(trimmed);
|
|
301
|
+
if (nameMatch?.[1]) return {
|
|
302
|
+
opcode: "Call",
|
|
303
|
+
operands: [nameMatch[1]]
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (trimmed.includes("=")) {
|
|
307
|
+
const parts = trimmed.split("=", 2);
|
|
308
|
+
const left = parts[0];
|
|
309
|
+
const right = parts[1];
|
|
310
|
+
if (left && right) return {
|
|
311
|
+
opcode: "Store",
|
|
312
|
+
operands: [left.trim(), right.trim()]
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (trimmed.startsWith("if ")) return {
|
|
316
|
+
opcode: "JumpIfTrue",
|
|
317
|
+
operands: [trimmed]
|
|
318
|
+
};
|
|
319
|
+
if (trimmed.startsWith("for ") || trimmed.startsWith("while ")) return {
|
|
320
|
+
opcode: "Loop",
|
|
321
|
+
operands: [trimmed]
|
|
322
|
+
};
|
|
323
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("const ") || trimmed.startsWith("let ")) return {
|
|
324
|
+
opcode: "LoadLiteral",
|
|
325
|
+
operands: [trimmed]
|
|
326
|
+
};
|
|
327
|
+
return {
|
|
328
|
+
opcode: "Evaluate",
|
|
329
|
+
operands: [trimmed]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function buildPseudoBytecode(source) {
|
|
333
|
+
const instructions = ["; pseudo-bytecode synthesized from script source"];
|
|
334
|
+
let offset = 0;
|
|
335
|
+
for (const line of source.split(/\r?\n/u)) {
|
|
336
|
+
const trimmed = line.trim();
|
|
337
|
+
if (trimmed.length === 0) continue;
|
|
338
|
+
const { opcode, operands } = inferOpcode(trimmed);
|
|
339
|
+
const operandText = operands.join(", ");
|
|
340
|
+
instructions.push(`${offset} ${opcode}${operandText.length > 0 ? ` ${operandText}` : ""}`);
|
|
341
|
+
offset += 1;
|
|
342
|
+
}
|
|
343
|
+
return instructions.join("\n");
|
|
344
|
+
}
|
|
345
|
+
function inferFunctionName(source, functionOffset) {
|
|
346
|
+
if (typeof functionOffset === "number" && functionOffset >= 0 && functionOffset < source.length) {
|
|
347
|
+
const start = Math.max(0, functionOffset - 120);
|
|
348
|
+
const end = Math.min(source.length, functionOffset + 120);
|
|
349
|
+
const nearby = source.slice(start, end);
|
|
350
|
+
const namedFunction = /function\s+([A-Za-z_$][\w$]*)/u.exec(nearby);
|
|
351
|
+
if (namedFunction?.[1]) return namedFunction[1];
|
|
352
|
+
const assignedFunction = /([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(/u.exec(nearby);
|
|
353
|
+
if (assignedFunction?.[1]) return assignedFunction[1];
|
|
354
|
+
}
|
|
355
|
+
return /function\s+([A-Za-z_$][\w$]*)/u.exec(source)?.[1] ?? "anonymous";
|
|
356
|
+
}
|
|
357
|
+
function findObjectLiteralProperties(source) {
|
|
358
|
+
const matches = source.matchAll(/\{([^{}]+:[^{}]+)\}/gu);
|
|
359
|
+
const results = [];
|
|
360
|
+
let index = 0;
|
|
361
|
+
for (const match of matches) {
|
|
362
|
+
const body = match[1];
|
|
363
|
+
if (!body) continue;
|
|
364
|
+
const properties = body.split(",").map((entry) => entry.trim()).map((entry) => {
|
|
365
|
+
const [key] = entry.split(":", 1);
|
|
366
|
+
return key?.trim() ?? "";
|
|
367
|
+
}).filter((entry) => entry.length > 0).filter((entry, position, list) => list.indexOf(entry) === position);
|
|
368
|
+
if (properties.length === 0) continue;
|
|
369
|
+
results.push({
|
|
370
|
+
address: `hidden-class-${index}`,
|
|
371
|
+
properties,
|
|
372
|
+
transitionMap: properties.length > 1 ? properties.join(" -> ") : void 0
|
|
373
|
+
});
|
|
374
|
+
index += 1;
|
|
375
|
+
}
|
|
376
|
+
return results;
|
|
377
|
+
}
|
|
378
|
+
var BytecodeExtractor = class {
|
|
379
|
+
versionDetector;
|
|
380
|
+
constructor(getPage) {
|
|
381
|
+
this.getPage = getPage;
|
|
382
|
+
this.versionDetector = new VersionDetector(getPage);
|
|
383
|
+
}
|
|
384
|
+
async extractBytecode(scriptId, functionOffset) {
|
|
385
|
+
const scriptSource = await this.getScriptSource(scriptId);
|
|
386
|
+
if (!scriptSource) return null;
|
|
387
|
+
const functions = await this.getCoverageFunctions(scriptId);
|
|
388
|
+
const functionByOffset = typeof functionOffset === "number" ? functions.find((candidate) => functionOffset >= candidate.startOffset && functionOffset <= candidate.endOffset) : void 0;
|
|
389
|
+
const functionName = functionByOffset?.functionName && functionByOffset.functionName.length > 0 ? functionByOffset.functionName : inferFunctionName(scriptSource, functionOffset);
|
|
390
|
+
const sourceSlice = functionByOffset && functionByOffset.endOffset > functionByOffset.startOffset ? scriptSource.slice(functionByOffset.startOffset, functionByOffset.endOffset) : scriptSource;
|
|
391
|
+
await this.versionDetector.supportsNativesSyntax();
|
|
392
|
+
return {
|
|
393
|
+
functionName,
|
|
394
|
+
bytecode: buildPseudoBytecode(sourceSlice),
|
|
395
|
+
sourcePosition: typeof functionOffset === "number" ? functionOffset : functionByOffset ? functionByOffset.startOffset : void 0
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
disassembleBytecode(bytecode) {
|
|
399
|
+
const instructions = [];
|
|
400
|
+
for (const line of bytecode.split(/\r?\n/u)) {
|
|
401
|
+
const trimmed = line.trim();
|
|
402
|
+
if (trimmed.length === 0 || trimmed.startsWith(";")) continue;
|
|
403
|
+
const match = /^(\d+)\s*@\s*([A-Za-z_][\w.]*)\s*(.*)$/u.exec(trimmed) ?? /^(?:0x[0-9a-fA-F]+\s+@)?\s*(\d+)\s*[: ]\s*([A-Za-z_][\w.]*)\s*(.*)$/u.exec(trimmed) ?? /^(\d+)\s+([A-Za-z_][\w.]*)\s*(.*)$/u.exec(trimmed);
|
|
404
|
+
if (!match) continue;
|
|
405
|
+
const offset = Number(match[1]);
|
|
406
|
+
if (!Number.isFinite(offset)) continue;
|
|
407
|
+
instructions.push({
|
|
408
|
+
offset,
|
|
409
|
+
opcode: match[2] ?? "Unknown",
|
|
410
|
+
operands: splitOperands(match[3] ?? "")
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return instructions;
|
|
414
|
+
}
|
|
415
|
+
async findHiddenClasses(scriptId) {
|
|
416
|
+
const scriptSource = await this.getScriptSource(scriptId);
|
|
417
|
+
if (!scriptSource) return [];
|
|
418
|
+
return findObjectLiteralProperties(scriptSource);
|
|
419
|
+
}
|
|
420
|
+
async getCoverageFunctions(scriptId) {
|
|
421
|
+
const session = await this.createSession();
|
|
422
|
+
if (!session) return [];
|
|
423
|
+
try {
|
|
424
|
+
await session.send("Profiler.enable");
|
|
425
|
+
await session.send("Profiler.startPreciseCoverage", {
|
|
426
|
+
callCount: true,
|
|
427
|
+
detailed: true
|
|
428
|
+
});
|
|
429
|
+
const response = await session.send("Profiler.takePreciseCoverage");
|
|
430
|
+
const targetScript = (isRecord$1(response) ? toRecordArray(response["result"]) : []).find((entry) => typeof entry["scriptId"] === "string" && entry["scriptId"] === scriptId);
|
|
431
|
+
if (!targetScript) return [];
|
|
432
|
+
const functions = toRecordArray(targetScript["functions"]);
|
|
433
|
+
const extracted = [];
|
|
434
|
+
for (const fn of functions) {
|
|
435
|
+
const primaryRange = toRecordArray(fn["ranges"])[0];
|
|
436
|
+
if (!primaryRange) continue;
|
|
437
|
+
const startOffset = toNumber(primaryRange["startOffset"]);
|
|
438
|
+
const endOffset = toNumber(primaryRange["endOffset"]);
|
|
439
|
+
if (startOffset === null || endOffset === null) continue;
|
|
440
|
+
extracted.push({
|
|
441
|
+
functionName: toStringValue(fn["functionName"]) ?? "anonymous",
|
|
442
|
+
startOffset,
|
|
443
|
+
endOffset
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return extracted;
|
|
447
|
+
} catch {
|
|
448
|
+
return [];
|
|
449
|
+
} finally {
|
|
450
|
+
await session.send("Profiler.stopPreciseCoverage").catch(() => void 0);
|
|
451
|
+
await session.send("Profiler.disable").catch(() => void 0);
|
|
452
|
+
await session.detach().catch(() => void 0);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async getScriptSource(scriptId) {
|
|
456
|
+
const session = await this.createSession();
|
|
457
|
+
if (!session) return null;
|
|
458
|
+
try {
|
|
459
|
+
await session.send("Debugger.enable");
|
|
460
|
+
const response = await session.send("Debugger.getScriptSource", { scriptId });
|
|
461
|
+
if (!isRecord$1(response)) return null;
|
|
462
|
+
const scriptSource = response["scriptSource"];
|
|
463
|
+
return typeof scriptSource === "string" ? scriptSource : null;
|
|
464
|
+
} catch {
|
|
465
|
+
return null;
|
|
466
|
+
} finally {
|
|
467
|
+
await session.send("Debugger.disable").catch(() => void 0);
|
|
468
|
+
await session.detach().catch(() => void 0);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async createSession() {
|
|
472
|
+
if (!this.getPage) return null;
|
|
473
|
+
try {
|
|
474
|
+
const page = await this.getPage();
|
|
475
|
+
if (!isCDPPageLike$1(page)) return null;
|
|
476
|
+
return await page.createCDPSession();
|
|
477
|
+
} catch {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
//#endregion
|
|
483
|
+
//#region src/modules/v8-inspector/JITInspector.ts
|
|
484
|
+
function isRecord(value) {
|
|
485
|
+
return typeof value === "object" && value !== null;
|
|
486
|
+
}
|
|
487
|
+
function isCDPPageLike(value) {
|
|
488
|
+
return isRecord(value) && typeof value["createCDPSession"] === "function";
|
|
489
|
+
}
|
|
490
|
+
function readNumber(value) {
|
|
491
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
492
|
+
if (isRecord(value) && typeof value["value"] === "number") {
|
|
493
|
+
const nested = value["value"];
|
|
494
|
+
return typeof nested === "number" && Number.isFinite(nested) ? nested : null;
|
|
495
|
+
}
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
function mapOptimizationTier(status) {
|
|
499
|
+
if (status === null) return {
|
|
500
|
+
optimized: false,
|
|
501
|
+
tier: "unknown"
|
|
502
|
+
};
|
|
503
|
+
if ((status & 128) !== 0) return {
|
|
504
|
+
optimized: true,
|
|
505
|
+
tier: "maglev"
|
|
506
|
+
};
|
|
507
|
+
if ((status & 64) !== 0) return {
|
|
508
|
+
optimized: true,
|
|
509
|
+
tier: "turbofan"
|
|
510
|
+
};
|
|
511
|
+
if ((status & 16) !== 0 || (status & 32) !== 0) return {
|
|
512
|
+
optimized: true,
|
|
513
|
+
tier: "optimized"
|
|
514
|
+
};
|
|
515
|
+
return {
|
|
516
|
+
optimized: false,
|
|
517
|
+
tier: "interpreted"
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
var JITInspector = class {
|
|
521
|
+
bytecodeExtractor;
|
|
522
|
+
versionDetector;
|
|
523
|
+
optimizedFunctionsCache = [];
|
|
524
|
+
constructor(getPage) {
|
|
525
|
+
this.getPage = getPage;
|
|
526
|
+
this.bytecodeExtractor = new BytecodeExtractor(getPage);
|
|
527
|
+
this.versionDetector = new VersionDetector(getPage);
|
|
528
|
+
}
|
|
529
|
+
async inspectJIT(scriptId) {
|
|
530
|
+
const hiddenClasses = await this.bytecodeExtractor.findHiddenClasses(scriptId);
|
|
531
|
+
const extraction = await this.bytecodeExtractor.extractBytecode(scriptId);
|
|
532
|
+
const functionNames = /* @__PURE__ */ new Set();
|
|
533
|
+
if (extraction) functionNames.add(extraction.functionName);
|
|
534
|
+
for (const hiddenClass of hiddenClasses) {
|
|
535
|
+
const candidate = hiddenClass.properties[0];
|
|
536
|
+
if (candidate) functionNames.add(candidate);
|
|
537
|
+
}
|
|
538
|
+
if (functionNames.size === 0) functionNames.add("anonymous");
|
|
539
|
+
const supportsNativesSyntax = await this.versionDetector.supportsNativesSyntax();
|
|
540
|
+
const results = [];
|
|
541
|
+
for (const functionName of functionNames) {
|
|
542
|
+
const { optimized, tier } = mapOptimizationTier(supportsNativesSyntax ? await this.getOptimizationStatus(functionName) : null);
|
|
543
|
+
results.push({
|
|
544
|
+
functionName,
|
|
545
|
+
optimized,
|
|
546
|
+
tier
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
this.optimizedFunctionsCache = results;
|
|
550
|
+
return results;
|
|
551
|
+
}
|
|
552
|
+
async forceDeoptimization(functionRef) {
|
|
553
|
+
if (!await this.versionDetector.supportsNativesSyntax()) return;
|
|
554
|
+
const session = await this.createSession();
|
|
555
|
+
if (!session) return;
|
|
556
|
+
try {
|
|
557
|
+
await session.send("Runtime.evaluate", {
|
|
558
|
+
expression: `
|
|
559
|
+
(() => {
|
|
560
|
+
try {
|
|
561
|
+
const candidate = eval(${JSON.stringify(functionRef)});
|
|
562
|
+
if (typeof candidate === 'function') {
|
|
563
|
+
%DeoptimizeFunction(candidate);
|
|
564
|
+
}
|
|
565
|
+
} catch (error) {
|
|
566
|
+
return undefined;
|
|
567
|
+
}
|
|
568
|
+
return undefined;
|
|
569
|
+
})()
|
|
570
|
+
`,
|
|
571
|
+
returnByValue: true,
|
|
572
|
+
awaitPromise: false
|
|
573
|
+
});
|
|
574
|
+
} finally {
|
|
575
|
+
await session.detach().catch(() => void 0);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async getOptimizedFunctions() {
|
|
579
|
+
return [...this.optimizedFunctionsCache];
|
|
580
|
+
}
|
|
581
|
+
async getOptimizationStatus(functionName) {
|
|
582
|
+
const session = await this.createSession();
|
|
583
|
+
if (!session) return null;
|
|
584
|
+
try {
|
|
585
|
+
const response = await session.send("Runtime.evaluate", {
|
|
586
|
+
expression: `
|
|
587
|
+
(() => {
|
|
588
|
+
try {
|
|
589
|
+
const candidate = globalThis[${JSON.stringify(functionName)}];
|
|
590
|
+
if (typeof candidate !== 'function') {
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
return %GetOptimizationStatus(candidate);
|
|
594
|
+
} catch (error) {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
})()
|
|
598
|
+
`,
|
|
599
|
+
returnByValue: true,
|
|
600
|
+
awaitPromise: false
|
|
601
|
+
});
|
|
602
|
+
if (!isRecord(response)) return null;
|
|
603
|
+
return readNumber(response["result"]);
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
} finally {
|
|
607
|
+
await session.detach().catch(() => void 0);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
async createSession() {
|
|
611
|
+
if (!this.getPage) return null;
|
|
612
|
+
try {
|
|
613
|
+
const page = await this.getPage();
|
|
614
|
+
if (!isCDPPageLike(page)) return null;
|
|
615
|
+
return await page.createCDPSession();
|
|
616
|
+
} catch {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region src/server/domains/v8-inspector/handlers/bytecode-extract.ts
|
|
623
|
+
async function handleBytecodeExtract(args, runtime) {
|
|
624
|
+
const scriptId = argString(args, "scriptId", "").trim();
|
|
625
|
+
const functionOffset = argNumber(args, "functionOffset");
|
|
626
|
+
if (scriptId.length === 0) return {
|
|
627
|
+
success: false,
|
|
628
|
+
error: "scriptId is required"
|
|
629
|
+
};
|
|
630
|
+
const extractor = new BytecodeExtractor(runtime?.getPage);
|
|
631
|
+
const extraction = await extractor.extractBytecode(scriptId, functionOffset ?? void 0);
|
|
632
|
+
if (!extraction) return {
|
|
633
|
+
success: false,
|
|
634
|
+
error: `Unable to extract bytecode for scriptId "${scriptId}"`
|
|
635
|
+
};
|
|
636
|
+
return {
|
|
637
|
+
success: true,
|
|
638
|
+
scriptId,
|
|
639
|
+
functionOffset: functionOffset ?? null,
|
|
640
|
+
extraction,
|
|
641
|
+
disassembly: extractor.disassembleBytecode(extraction.bytecode),
|
|
642
|
+
hiddenClasses: await extractor.findHiddenClasses(scriptId)
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/server/domains/v8-inspector/handlers/jit-inspect.ts
|
|
647
|
+
async function handleJitInspect(args, runtime) {
|
|
648
|
+
const scriptId = argString(args, "scriptId", "").trim();
|
|
649
|
+
if (scriptId.length === 0) return {
|
|
650
|
+
success: false,
|
|
651
|
+
error: "scriptId is required"
|
|
652
|
+
};
|
|
653
|
+
return {
|
|
654
|
+
success: true,
|
|
655
|
+
scriptId,
|
|
656
|
+
functions: await new JITInspector(runtime?.getPage).inspectJIT(scriptId)
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region src/server/domains/v8-inspector/handlers/impl.ts
|
|
661
|
+
function requireStringArg(args, key) {
|
|
662
|
+
const value = args[key];
|
|
663
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
664
|
+
return value;
|
|
665
|
+
}
|
|
666
|
+
function requirePageController(ctx) {
|
|
667
|
+
const pageController = ctx.pageController;
|
|
668
|
+
if (!pageController) throw new Error("PageController not available");
|
|
669
|
+
return pageController;
|
|
670
|
+
}
|
|
671
|
+
var V8InspectorHandlers = class {
|
|
672
|
+
currentSnapshotId = null;
|
|
673
|
+
constructor(deps) {
|
|
674
|
+
this.deps = deps;
|
|
675
|
+
}
|
|
676
|
+
async handle(toolName, args) {
|
|
677
|
+
const handler = {
|
|
678
|
+
v8_heap_snapshot_capture: (toolArgs) => this.v8_heap_snapshot_capture(toolArgs),
|
|
679
|
+
v8_heap_snapshot_analyze: (toolArgs) => this.v8_heap_snapshot_analyze(toolArgs),
|
|
680
|
+
v8_heap_diff: (toolArgs) => this.v8_heap_diff(toolArgs),
|
|
681
|
+
v8_object_inspect: (toolArgs) => this.v8_object_inspect(toolArgs),
|
|
682
|
+
v8_heap_stats: (toolArgs) => this.v8_heap_stats(toolArgs),
|
|
683
|
+
v8_bytecode_extract: (toolArgs) => this.v8_bytecode_extract(toolArgs),
|
|
684
|
+
v8_version_detect: (toolArgs) => this.v8_version_detect(toolArgs),
|
|
685
|
+
v8_jit_inspect: (toolArgs) => this.v8_jit_inspect(toolArgs)
|
|
686
|
+
}[toolName];
|
|
687
|
+
if (!handler) throw new Error(`Unknown v8-inspector tool: ${toolName}`);
|
|
688
|
+
return handler(args);
|
|
689
|
+
}
|
|
690
|
+
async v8_heap_snapshot_capture(args) {
|
|
691
|
+
requirePageController(this.deps.ctx);
|
|
692
|
+
const pageController = this.deps.ctx.pageController;
|
|
693
|
+
const result = await handleHeapSnapshotCapture(args, {
|
|
694
|
+
getPage: () => Promise.resolve(pageController),
|
|
695
|
+
getSnapshot: () => this.currentSnapshotId,
|
|
696
|
+
setSnapshot: (id) => {
|
|
697
|
+
this.currentSnapshotId = id;
|
|
698
|
+
},
|
|
699
|
+
client: this.deps.client
|
|
700
|
+
});
|
|
701
|
+
if (result.success && result.snapshotId) this.deps.ctx.eventBus.emit("v8:heap_captured", {
|
|
702
|
+
snapshotId: result.snapshotId,
|
|
703
|
+
sizeBytes: result.sizeBytes,
|
|
704
|
+
timestamp: result.capturedAt
|
|
705
|
+
});
|
|
706
|
+
return result;
|
|
707
|
+
}
|
|
708
|
+
async v8_heap_snapshot_analyze(args) {
|
|
709
|
+
const snapshotId = requireStringArg(args, "snapshotId");
|
|
710
|
+
const snapshot = getSnapshot(snapshotId);
|
|
711
|
+
if (!snapshot) throw new Error(`Snapshot ${snapshotId} not found`);
|
|
712
|
+
return {
|
|
713
|
+
success: true,
|
|
714
|
+
snapshotId,
|
|
715
|
+
summary: {
|
|
716
|
+
chunkCount: snapshot.chunks.length,
|
|
717
|
+
sizeBytes: snapshot.sizeBytes
|
|
718
|
+
},
|
|
719
|
+
objectAddress: `0x${snapshot.sizeBytes.toString(16)}`
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
async v8_heap_diff(args) {
|
|
723
|
+
const beforeSnapshotId = typeof args.beforeSnapshotId === "string" ? args.beforeSnapshotId : void 0;
|
|
724
|
+
const afterSnapshotId = typeof args.afterSnapshotId === "string" ? args.afterSnapshotId : void 0;
|
|
725
|
+
if (!beforeSnapshotId || !afterSnapshotId) throw new Error("Both beforeSnapshotId and afterSnapshotId are required");
|
|
726
|
+
const beforeSnapshot = getSnapshot(beforeSnapshotId);
|
|
727
|
+
if (!beforeSnapshot) throw new Error(`Snapshot ${beforeSnapshotId} not found`);
|
|
728
|
+
const afterSnapshot = getSnapshot(afterSnapshotId);
|
|
729
|
+
if (!afterSnapshot) throw new Error(`Snapshot ${afterSnapshotId} not found`);
|
|
730
|
+
return {
|
|
731
|
+
success: true,
|
|
732
|
+
beforeSnapshotId,
|
|
733
|
+
afterSnapshotId,
|
|
734
|
+
sizeDeltaBytes: afterSnapshot.sizeBytes - beforeSnapshot.sizeBytes
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
async v8_object_inspect(args) {
|
|
738
|
+
const address = requireStringArg(args, "address");
|
|
739
|
+
let objectData;
|
|
740
|
+
try {
|
|
741
|
+
objectData = await this.deps.client.getObjectByObjectId(address) ?? void 0;
|
|
742
|
+
} catch {}
|
|
743
|
+
return {
|
|
744
|
+
success: true,
|
|
745
|
+
address,
|
|
746
|
+
...objectData ? { objectData } : {}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
async v8_heap_stats(_args) {
|
|
750
|
+
requirePageController(this.deps.ctx);
|
|
751
|
+
let heapUsage;
|
|
752
|
+
try {
|
|
753
|
+
heapUsage = await this.deps.client.getHeapUsage();
|
|
754
|
+
} catch {}
|
|
755
|
+
return {
|
|
756
|
+
success: true,
|
|
757
|
+
snapshotCount: getSnapshotCache().size,
|
|
758
|
+
...heapUsage ? { heapUsage } : {}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
async v8_bytecode_extract(args) {
|
|
762
|
+
const pageController = this.deps.ctx.pageController;
|
|
763
|
+
return handleBytecodeExtract(args, { getPage: pageController ? () => Promise.resolve(pageController) : void 0 });
|
|
764
|
+
}
|
|
765
|
+
async v8_version_detect(_args) {
|
|
766
|
+
const pageController = this.deps.ctx.pageController;
|
|
767
|
+
if (!pageController) return {
|
|
768
|
+
success: false,
|
|
769
|
+
error: "PageController not available"
|
|
770
|
+
};
|
|
771
|
+
const { VersionDetector } = await import("./VersionDetector-CwVLVdDM.mjs").then((n) => n.n);
|
|
772
|
+
return {
|
|
773
|
+
success: true,
|
|
774
|
+
version: await new VersionDetector(() => Promise.resolve(pageController)).detectV8Version(),
|
|
775
|
+
features: {}
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
async v8_jit_inspect(args) {
|
|
779
|
+
const pageController = this.deps.ctx.pageController;
|
|
780
|
+
return handleJitInspect(args, { getPage: pageController ? () => Promise.resolve(pageController) : void 0 });
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
const registrations = v8InspectorTools.map((toolDef) => ({
|
|
784
|
+
tool: toolDef,
|
|
785
|
+
domain: "v8-inspector",
|
|
786
|
+
bind: bindByDepKey("v8InspectorHandlers", (handlers, args) => handlers.handle(toolDef.name, args))
|
|
787
|
+
}));
|
|
788
|
+
async function ensure(ctx) {
|
|
789
|
+
if (!ctx.pageController) throw new Error("v8-inspector: PageController not available");
|
|
790
|
+
const handlers = new V8InspectorHandlers({
|
|
791
|
+
ctx,
|
|
792
|
+
client: new V8InspectorClient(() => Promise.resolve(ctx.pageController))
|
|
793
|
+
});
|
|
794
|
+
ctx.v8InspectorHandlers = handlers;
|
|
795
|
+
return handlers;
|
|
796
|
+
}
|
|
797
|
+
const manifest = {
|
|
798
|
+
kind: "domain-manifest",
|
|
799
|
+
version: 1,
|
|
800
|
+
domain: "v8-inspector",
|
|
801
|
+
depKey: "v8InspectorHandlers",
|
|
802
|
+
profiles: ["workflow", "full"],
|
|
803
|
+
registrations,
|
|
804
|
+
ensure,
|
|
805
|
+
prerequisites: {
|
|
806
|
+
v8_heap_snapshot_capture: [{
|
|
807
|
+
condition: "Browser must be connected",
|
|
808
|
+
fix: "Call browser_launch or browser_attach first"
|
|
809
|
+
}],
|
|
810
|
+
v8_heap_snapshot_analyze: [{
|
|
811
|
+
condition: "A snapshotId must be provided",
|
|
812
|
+
fix: "Capture a heap snapshot before analysis"
|
|
813
|
+
}],
|
|
814
|
+
v8_heap_diff: [{
|
|
815
|
+
condition: "Both snapshot identifiers are required",
|
|
816
|
+
fix: "Capture before/after snapshots before diffing"
|
|
817
|
+
}]
|
|
818
|
+
},
|
|
819
|
+
toolDependencies: [{
|
|
820
|
+
from: "v8_heap_snapshot_capture",
|
|
821
|
+
to: "browser_attach",
|
|
822
|
+
relation: "requires",
|
|
823
|
+
weight: .8
|
|
824
|
+
}, {
|
|
825
|
+
from: "v8_object_inspect",
|
|
826
|
+
to: "v8_heap_snapshot_analyze",
|
|
827
|
+
relation: "precedes",
|
|
828
|
+
weight: .6
|
|
829
|
+
}],
|
|
830
|
+
workflowRule: {
|
|
831
|
+
patterns: [
|
|
832
|
+
/v8.*heap/i,
|
|
833
|
+
/heap.*snapshot/i,
|
|
834
|
+
/jit/i,
|
|
835
|
+
/object.*address/i
|
|
836
|
+
],
|
|
837
|
+
priority: 80,
|
|
838
|
+
tools: [
|
|
839
|
+
"v8_heap_snapshot_capture",
|
|
840
|
+
"v8_heap_snapshot_analyze",
|
|
841
|
+
"v8_object_inspect",
|
|
842
|
+
"v8_heap_stats"
|
|
843
|
+
],
|
|
844
|
+
hint: "Capture a heap snapshot, analyze it, then inspect interesting objects by address."
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
//#endregion
|
|
848
|
+
export { V8InspectorHandlers, manifest as default };
|