@ton/sandbox 0.21.0-beta.1 → 0.21.0-debugger.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.
@@ -0,0 +1,467 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TVMDebugSession = exports.Debuggee = void 0;
7
+ const node_events_1 = __importDefault(require("node:events"));
8
+ const debugadapter_1 = require("@vscode/debugadapter");
9
+ const node_path_1 = require("node:path");
10
+ class Debuggee extends node_events_1.default {
11
+ constructor(executor, finishedCallback) {
12
+ super();
13
+ this.ptr = 0;
14
+ this.debugType = 'get';
15
+ this.sourceMap = {};
16
+ this.availableLines = {};
17
+ this.codeCells = new Map();
18
+ this.breakpoints = new Map();
19
+ this.breakpointID = 0;
20
+ this.frames = [];
21
+ this.executor = executor;
22
+ this.executor.debugLogFunc = (s) => { this.sendEvent('output', s); };
23
+ this.finishedCallback = finishedCallback;
24
+ }
25
+ setCodeCells(code) {
26
+ const q = [code];
27
+ while (q.length > 0) {
28
+ const c = q.pop();
29
+ const h = c.hash().toString('hex').toUpperCase();
30
+ this.codeCells.set(h, c);
31
+ for (const r of c.refs) {
32
+ q.push(r);
33
+ }
34
+ }
35
+ }
36
+ setSourceMap(sourceMap) {
37
+ this.sourceMap = sourceMap;
38
+ for (const di in sourceMap) {
39
+ const sem = sourceMap[di];
40
+ if (!(sem.path in this.availableLines)) {
41
+ this.availableLines[sem.path] = [];
42
+ }
43
+ this.availableLines[sem.path].push(sem.line);
44
+ }
45
+ }
46
+ getAvailableSourcePaths() {
47
+ return Object.keys(this.availableLines);
48
+ }
49
+ getAvailableLines(path) {
50
+ return this.availableLines[path] ?? [];
51
+ }
52
+ isLineAvailable(path, line) {
53
+ if (!(path in this.availableLines)) {
54
+ return false;
55
+ }
56
+ const lines = this.availableLines[path];
57
+ return lines.indexOf(line) >= 0;
58
+ }
59
+ continue() {
60
+ this.stepUntilLine(true);
61
+ }
62
+ step(stopEvent = 'stopOnStep') {
63
+ this.stepUntilLine(false, stopEvent);
64
+ }
65
+ startGetMethod(args) {
66
+ this.ptr = this.executor.sbsGetMethodSetup(args);
67
+ this.debugType = 'get';
68
+ }
69
+ startTransaction(args) {
70
+ const { emptr, res } = this.executor.sbsTransactionSetup(args);
71
+ if (res !== 1) {
72
+ throw new Error('Could not setup SBS transaction, result: ' + res);
73
+ }
74
+ this.ptr = emptr;
75
+ this.debugType = 'tx';
76
+ }
77
+ vmStep() {
78
+ switch (this.debugType) {
79
+ case 'get':
80
+ return this.executor.sbsGetMethodStep(this.ptr);
81
+ case 'tx':
82
+ return this.executor.sbsTransactionStep(this.ptr);
83
+ }
84
+ }
85
+ codePos() {
86
+ switch (this.debugType) {
87
+ case 'get':
88
+ return this.executor.sbsGetMethodCodePos(this.ptr);
89
+ case 'tx':
90
+ return this.executor.sbsTransactionCodePos(this.ptr);
91
+ }
92
+ }
93
+ getStack() {
94
+ switch (this.debugType) {
95
+ case 'get':
96
+ return this.executor.sbsGetMethodStack(this.ptr);
97
+ case 'tx':
98
+ return this.executor.sbsTransactionStack(this.ptr);
99
+ }
100
+ }
101
+ currentDebugInfoNumber() {
102
+ const codepos = this.codePos();
103
+ const cell = this.codeCells.get(codepos.hash);
104
+ if (cell !== undefined) {
105
+ try {
106
+ const s = cell.beginParse();
107
+ s.skip(codepos.offset);
108
+ const opp = s.loadUint(12);
109
+ if (opp !== 0xfef) {
110
+ return undefined;
111
+ }
112
+ const n = s.loadUint(4);
113
+ const b = s.loadBuffer(n + 1);
114
+ const bstr = b.toString('utf-8');
115
+ if (!bstr.startsWith('DI')) {
116
+ return undefined;
117
+ }
118
+ return parseInt(bstr.slice(2));
119
+ }
120
+ catch (e) { }
121
+ }
122
+ return undefined;
123
+ }
124
+ currentSourceMapEntry() {
125
+ const di = this.currentDebugInfoNumber();
126
+ if (di === undefined) {
127
+ return undefined;
128
+ }
129
+ return this.sourceMap[di];
130
+ }
131
+ breakpointKey(path, line) {
132
+ return path + ':' + line;
133
+ }
134
+ splitBreakpointKey(k) {
135
+ const i = k.lastIndexOf(':');
136
+ return {
137
+ path: k.slice(0, i),
138
+ line: parseInt(k.slice(i + 1)),
139
+ };
140
+ }
141
+ clearBreakpoints(path) {
142
+ this.breakpoints.set(path, []);
143
+ }
144
+ hasBreakpoint(path, line) {
145
+ return (this.breakpoints.get(path) ?? []).findIndex(v => v.line === line) >= 0;
146
+ }
147
+ setBreakpoint(path, line) {
148
+ let arr = this.breakpoints.get(path);
149
+ if (arr === undefined) {
150
+ arr = [];
151
+ this.breakpoints.set(path, arr);
152
+ }
153
+ const bp = {
154
+ id: this.breakpointID++,
155
+ line,
156
+ verified: this.isLineAvailable(path, line),
157
+ };
158
+ arr.push(bp);
159
+ return bp;
160
+ }
161
+ sendEvent(event, ...args) {
162
+ setTimeout(() => {
163
+ this.emit(event, ...args);
164
+ }, 0);
165
+ }
166
+ onFinished() {
167
+ this.sendEvent('end');
168
+ let r;
169
+ switch (this.debugType) {
170
+ case 'get': {
171
+ r = this.executor.sbsGetMethodResult(this.ptr);
172
+ this.executor.destroyTvmEmulator(this.ptr);
173
+ break;
174
+ }
175
+ case 'tx': {
176
+ r = this.executor.sbsTransactionResult(this.ptr);
177
+ this.executor.destroyEmulator(this.ptr);
178
+ break;
179
+ }
180
+ }
181
+ this.finishedCallback(r);
182
+ }
183
+ stepUntilLine(breakpointsOnly, stopEvent) {
184
+ while (true) {
185
+ const finished = this.vmStep();
186
+ if (finished) {
187
+ this.onFinished();
188
+ return;
189
+ }
190
+ const sme = this.currentSourceMapEntry();
191
+ if (sme !== undefined && (!breakpointsOnly || this.hasBreakpoint(sme.path, sme.line))) {
192
+ if (breakpointsOnly) {
193
+ this.sendEvent('stopOnBreakpoint');
194
+ }
195
+ else if (stopEvent !== undefined) {
196
+ this.sendEvent(stopEvent);
197
+ }
198
+ return;
199
+ }
200
+ }
201
+ }
202
+ prepareGetMethod(args, sourceMap) {
203
+ this.startGetMethod(args);
204
+ this.setCodeCells(args.code);
205
+ this.setSourceMap(sourceMap);
206
+ }
207
+ prepareTransaction(args, code, sourceMap) {
208
+ this.startTransaction(args);
209
+ this.setCodeCells(code);
210
+ this.setSourceMap(sourceMap);
211
+ }
212
+ start(debug, stopOnEntry) {
213
+ if (debug) {
214
+ if (stopOnEntry) {
215
+ this.step('stopOnEntry');
216
+ }
217
+ else {
218
+ this.continue();
219
+ }
220
+ }
221
+ else {
222
+ this.continue();
223
+ }
224
+ }
225
+ }
226
+ exports.Debuggee = Debuggee;
227
+ class TVMDebugSession extends debugadapter_1.LoggingDebugSession {
228
+ constructor(debuggee) {
229
+ super();
230
+ this.debuggee = debuggee;
231
+ this.debuggee.on('stopOnEntry', () => {
232
+ this.sendEvent(new debugadapter_1.StoppedEvent('entry', TVMDebugSession.threadID));
233
+ });
234
+ this.debuggee.on('stopOnBreakpoint', () => {
235
+ this.sendEvent(new debugadapter_1.StoppedEvent('breakpoint', TVMDebugSession.threadID));
236
+ });
237
+ this.debuggee.on('stopOnStep', () => {
238
+ this.sendEvent(new debugadapter_1.StoppedEvent('step', TVMDebugSession.threadID));
239
+ });
240
+ this.debuggee.on('end', () => {
241
+ this.sendEvent(new debugadapter_1.TerminatedEvent());
242
+ });
243
+ this.debuggee.on('output', (s) => {
244
+ this.sendEvent(new debugadapter_1.OutputEvent(s + '\n', 'stdout'));
245
+ });
246
+ }
247
+ initializeRequest(response, args) {
248
+ response.body = response.body || {};
249
+ const b = response.body;
250
+ b.supportsConfigurationDoneRequest = false;
251
+ b.supportsFunctionBreakpoints = false;
252
+ b.supportsConditionalBreakpoints = false;
253
+ b.supportsHitConditionalBreakpoints = false;
254
+ b.supportsEvaluateForHovers = false;
255
+ b.supportsStepBack = false;
256
+ b.supportsSetVariable = false;
257
+ b.supportsRestartFrame = false;
258
+ b.supportsGotoTargetsRequest = false;
259
+ b.supportsStepInTargetsRequest = false;
260
+ b.supportsCompletionsRequest = false;
261
+ b.supportsModulesRequest = false;
262
+ b.supportsRestartRequest = false;
263
+ b.supportsValueFormattingOptions = false;
264
+ b.supportsExceptionInfoRequest = false;
265
+ b.supportTerminateDebuggee = false;
266
+ b.supportSuspendDebuggee = false;
267
+ b.supportsDelayedStackTraceLoading = false;
268
+ b.supportsLoadedSourcesRequest = true;
269
+ b.supportsLogPoints = false;
270
+ b.supportsTerminateThreadsRequest = false;
271
+ b.supportsSetExpression = false;
272
+ b.supportsTerminateRequest = false;
273
+ b.supportsDataBreakpoints = false;
274
+ b.supportsReadMemoryRequest = false;
275
+ b.supportsWriteMemoryRequest = false;
276
+ b.supportsDisassembleRequest = false;
277
+ b.supportsCancelRequest = false;
278
+ b.supportsBreakpointLocationsRequest = true;
279
+ b.supportsClipboardContext = false;
280
+ b.supportsSteppingGranularity = false;
281
+ b.supportsInstructionBreakpoints = false;
282
+ b.supportsExceptionFilterOptions = false;
283
+ b.supportsSingleThreadExecutionRequests = false;
284
+ this.sendResponse(response);
285
+ this.sendEvent(new debugadapter_1.InitializedEvent());
286
+ }
287
+ loadedSourcesRequest(response, args, request) {
288
+ response.body = response.body || {};
289
+ response.body.sources = this.debuggee.getAvailableSourcePaths().map(v => ({
290
+ path: v,
291
+ name: (0, node_path_1.basename)(v),
292
+ }));
293
+ this.sendResponse(response);
294
+ }
295
+ breakpointLocationsRequest(response, args, request) {
296
+ response.body = response.body || {};
297
+ const path = args.source.path;
298
+ if (path === undefined) {
299
+ this.sendErrorResponse(response, {
300
+ id: 1001,
301
+ format: 'No path',
302
+ });
303
+ return;
304
+ }
305
+ response.body.breakpoints = this.debuggee.getAvailableLines(path).filter(l => l >= args.line && l <= (args.endLine ?? args.line)).map(l => ({
306
+ line: l,
307
+ }));
308
+ this.sendResponse(response);
309
+ }
310
+ launchRequest(response, args, request) {
311
+ debugadapter_1.logger.setup(debugadapter_1.Logger.LogLevel.Verbose);
312
+ this.debuggee.start(!args.noDebug, true);
313
+ this.sendResponse(response);
314
+ }
315
+ attachRequest(response, args, request) {
316
+ this.launchRequest(response, args, request);
317
+ }
318
+ setBreakPointsRequest(response, args, request) {
319
+ const path = args.source.path;
320
+ if (path === undefined) {
321
+ this.sendErrorResponse(response, {
322
+ id: 1001,
323
+ format: 'No path',
324
+ });
325
+ return;
326
+ }
327
+ const breakpoints = args.breakpoints;
328
+ if (breakpoints === undefined) {
329
+ this.sendErrorResponse(response, {
330
+ id: 1002,
331
+ format: 'No breakpoints',
332
+ });
333
+ return;
334
+ }
335
+ this.debuggee.clearBreakpoints(path);
336
+ const bps = [];
337
+ for (const bp of breakpoints) {
338
+ const sbp = this.debuggee.setBreakpoint(path, bp.line);
339
+ bps.push({
340
+ id: sbp.id,
341
+ line: sbp.line,
342
+ verified: sbp.verified,
343
+ });
344
+ }
345
+ response.body = {
346
+ breakpoints: bps,
347
+ };
348
+ this.sendResponse(response);
349
+ }
350
+ threadsRequest(response, request) {
351
+ response.body = {
352
+ threads: [
353
+ new debugadapter_1.Thread(TVMDebugSession.threadID, 'main'),
354
+ ],
355
+ };
356
+ this.sendResponse(response);
357
+ }
358
+ continueRequest(response, args, request) {
359
+ this.debuggee.continue();
360
+ this.sendResponse(response);
361
+ }
362
+ nextRequest(response, args, request) {
363
+ this.debuggee.step();
364
+ this.sendResponse(response);
365
+ }
366
+ stepInRequest(response, args, request) {
367
+ this.debuggee.step();
368
+ this.sendResponse(response);
369
+ }
370
+ stepOutRequest(response, args, request) {
371
+ this.debuggee.step();
372
+ this.sendResponse(response);
373
+ }
374
+ stackTraceRequest(response, args, request) {
375
+ response.body = response.body || {};
376
+ const sme = this.debuggee.currentSourceMapEntry();
377
+ if (sme === undefined) {
378
+ response.body.stackFrames = [];
379
+ response.body.totalFrames = 0;
380
+ this.sendResponse(response);
381
+ return;
382
+ }
383
+ response.body.totalFrames = 1;
384
+ if (args.startFrame ?? 0 > 0) {
385
+ response.body.stackFrames = [];
386
+ this.sendResponse(response);
387
+ return;
388
+ }
389
+ response.body.stackFrames = [{
390
+ id: TVMDebugSession.stackFrameID,
391
+ name: 'func',
392
+ line: sme.line,
393
+ column: 0,
394
+ source: {
395
+ name: (0, node_path_1.basename)(sme.path),
396
+ path: sme.path,
397
+ },
398
+ }];
399
+ this.sendResponse(response);
400
+ }
401
+ scopesRequest(response, args, request) {
402
+ response.body = response.body || {};
403
+ const sme = this.debuggee.currentSourceMapEntry();
404
+ if (sme === undefined) {
405
+ response.body.scopes = [];
406
+ this.sendResponse(response);
407
+ return;
408
+ }
409
+ response.body.scopes = [{
410
+ name: 'Locals',
411
+ variablesReference: TVMDebugSession.variablesReference,
412
+ expensive: false,
413
+ }];
414
+ this.sendResponse(response);
415
+ }
416
+ variablesRequest(response, args, request) {
417
+ response.body = response.body || {};
418
+ response.body.variables = [];
419
+ const sme = this.debuggee.currentSourceMapEntry();
420
+ if (sme === undefined) {
421
+ this.sendResponse(response);
422
+ return;
423
+ }
424
+ const stack = this.debuggee.getStack();
425
+ for (let i = 0; i < sme.variables.length; i++) {
426
+ response.body.variables.push({
427
+ name: sme.variables[i],
428
+ value: tupleItemToString(stack[i]),
429
+ type: stack[i].type,
430
+ variablesReference: 0,
431
+ });
432
+ }
433
+ response.body.variables.sort((a, b) => (a.name < b.name) ? -1 : (a.name > b.name ? 1 : 0));
434
+ this.sendResponse(response);
435
+ }
436
+ disconnectRequest(response, args, request) {
437
+ if (args.restart) {
438
+ this.sendErrorResponse(response, {
439
+ id: 1003,
440
+ format: 'Cannot restart',
441
+ });
442
+ }
443
+ else {
444
+ this.sendResponse(response);
445
+ }
446
+ }
447
+ }
448
+ exports.TVMDebugSession = TVMDebugSession;
449
+ TVMDebugSession.threadID = 1;
450
+ TVMDebugSession.stackFrameID = 1;
451
+ TVMDebugSession.variablesReference = 1;
452
+ function tupleItemToString(ti) {
453
+ switch (ti.type) {
454
+ case 'int':
455
+ return ti.value.toString();
456
+ case 'null':
457
+ return 'null';
458
+ case 'nan':
459
+ return 'NaN';
460
+ case 'cell':
461
+ case 'slice':
462
+ case 'builder':
463
+ return ti.cell.toBoc().toString('base64');
464
+ case 'tuple':
465
+ return `[${ti.items.map(v => tupleItemToString(v)).join(', ')}]`;
466
+ }
467
+ }
@@ -0,0 +1,5 @@
1
+ import { SourceMap } from "./Debuggee";
2
+ import { CompileResult } from '@ton/blueprint';
3
+ export type SourceMapCache = Map<string, SourceMap>;
4
+ export declare const defaultSourceMapCache: SourceMapCache;
5
+ export declare function registerCompiledContract(c: CompileResult): import("@ton/core").Cell;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerCompiledContract = exports.defaultSourceMapCache = void 0;
4
+ const node_path_1 = require("node:path");
5
+ exports.defaultSourceMapCache = new Map();
6
+ function registerCompiledContract(c) {
7
+ if (c.lang !== 'func') {
8
+ throw new Error('Can only register func contracts');
9
+ }
10
+ if (c.debugInfo === undefined) {
11
+ throw new Error('No debug info');
12
+ }
13
+ const sm = {};
14
+ for (let i = 0; i < c.debugInfo.length; i++) {
15
+ const di = c.debugInfo[i];
16
+ if (di.ret || di.vars === undefined)
17
+ continue;
18
+ sm[i] = {
19
+ path: (0, node_path_1.resolve)(di.file),
20
+ line: di.line,
21
+ variables: di.vars ?? [],
22
+ };
23
+ }
24
+ exports.defaultSourceMapCache.set(c.code.hash().toString('base64'), sm);
25
+ return c.code;
26
+ }
27
+ exports.registerCompiledContract = registerCompiledContract;
@@ -0,0 +1,5 @@
1
+ import { Cell } from '@ton/core';
2
+ import { GetMethodArgs, Executor, GetMethodResult, RunTransactionArgs, EmulationResult } from '../executor/Executor';
3
+ import { SourceMap } from "./Debuggee";
4
+ export declare function debugGetMethod(executor: Executor, args: GetMethodArgs, sourceMap: SourceMap): Promise<GetMethodResult>;
5
+ export declare function debugTransaction(executor: Executor, args: RunTransactionArgs, code: Cell, sourceMap: SourceMap): Promise<EmulationResult>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.debugTransaction = exports.debugGetMethod = void 0;
27
+ const Debuggee_1 = require("./Debuggee");
28
+ const Net = __importStar(require("net"));
29
+ function initDebuggee(executor) {
30
+ let dbg = null;
31
+ const promise = new Promise((resolve) => {
32
+ dbg = new Debuggee_1.Debuggee(executor, resolve);
33
+ });
34
+ return { dbg, promise };
35
+ }
36
+ async function debugGetMethod(executor, args, sourceMap) {
37
+ console.log('Launched get method debug session. Please connect using the extension.');
38
+ const { dbg, promise } = initDebuggee(executor);
39
+ dbg.prepareGetMethod(args, sourceMap);
40
+ const server = Net.createServer((socket) => {
41
+ const session = new Debuggee_1.TVMDebugSession(dbg);
42
+ session.setRunAsServer(true);
43
+ session.start(socket, socket);
44
+ }).listen(42069);
45
+ const result = await promise;
46
+ server.close();
47
+ return result;
48
+ }
49
+ exports.debugGetMethod = debugGetMethod;
50
+ async function debugTransaction(executor, args, code, sourceMap) {
51
+ console.log('Launched transaction debug session. Please connect using the extension.');
52
+ const { dbg, promise } = initDebuggee(executor);
53
+ dbg.prepareTransaction(args, code, sourceMap);
54
+ const server = Net.createServer((socket) => {
55
+ const session = new Debuggee_1.TVMDebugSession(dbg);
56
+ session.setRunAsServer(true);
57
+ session.start(socket, socket);
58
+ }).listen(42069);
59
+ const result = await promise;
60
+ server.close();
61
+ return result;
62
+ }
63
+ exports.debugTransaction = debugTransaction;
@@ -82,18 +82,37 @@ export declare class Executor implements IExecutor {
82
82
  private heap;
83
83
  private emulator?;
84
84
  private debugLogs;
85
+ debugLogFunc: (s: string) => void;
85
86
  private constructor();
87
+ private handleLog;
86
88
  static create(): Promise<Executor>;
87
89
  runGetMethod(args: GetMethodArgs): Promise<GetMethodResult>;
88
90
  private runCommon;
89
91
  runTickTock(args: RunTickTockArgs): Promise<EmulationResult>;
90
92
  runTransaction(args: RunTransactionArgs): Promise<EmulationResult>;
91
- getVersion(): {
92
- commitHash: string;
93
- commitDate: string;
94
- };
95
93
  private createEmulator;
96
94
  private getEmulatorPointer;
97
95
  invoke(method: string, args: (number | string)[]): number;
98
96
  private extractString;
97
+ sbsGetMethodSetup(args: GetMethodArgs): number;
98
+ destroyTvmEmulator(ptr: number): void;
99
+ sbsGetMethodStep(ptr: number): boolean;
100
+ sbsGetMethodStack(ptr: number): TupleItem[];
101
+ sbsGetMethodCodePos(ptr: number): {
102
+ hash: string;
103
+ offset: number;
104
+ };
105
+ sbsGetMethodResult(ptr: number): GetMethodResult;
106
+ sbsTransactionSetup(args: RunTransactionArgs): {
107
+ res: number;
108
+ emptr: number;
109
+ };
110
+ destroyEmulator(ptr: number): void;
111
+ sbsTransactionStep(ptr: number): boolean;
112
+ sbsTransactionCodePos(ptr: number): {
113
+ hash: string;
114
+ offset: number;
115
+ };
116
+ sbsTransactionStack(ptr: number): TupleItem[];
117
+ sbsTransactionResult(ptr: number): EmulationResult;
99
118
  }