Skip to content

Commit

Permalink
feat: stack frames
Browse files Browse the repository at this point in the history
  • Loading branch information
krigga committed Aug 19, 2024
1 parent a68d98b commit 2b355fd
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 55 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ton/sandbox",
"version": "0.21.0-debugger.2",
"version": "0.21.0-debugger.3",
"description": "TON transaction emulator",
"main": "dist/index.js",
"license": "MIT",
Expand All @@ -13,7 +13,7 @@
"url": "git+https://github.com/ton-org/sandbox"
},
"devDependencies": {
"@ton/blueprint": "0.23.0-debugger.1",
"@ton/blueprint": "0.23.0-debugger.2",
"@ton/core": "^0.56.0",
"@ton/crypto": "3.2.0",
"@ton/test-utils": "^0.3.1",
Expand All @@ -35,6 +35,7 @@
"wasm:copy": "cp src/executor/emulator-emscripten.js src/executor/emulator-emscripten.wasm.js ./dist/executor",
"test": "yarn wasm:pack && yarn jest src",
"build": "rm -rf dist && yarn wasm:pack && yarn test && tsc && yarn wasm:copy",
"build:notest": "rm -rf dist && yarn wasm:pack && tsc && yarn wasm:copy",
"config:pack": "ts-node ./scripts/pack-config.ts"
},
"packageManager": "[email protected]",
Expand Down
23 changes: 20 additions & 3 deletions src/debugger/DebugInfoCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,29 @@ export function registerCompiledContract(c: CompileResult) {

for (let i = 0; i < locations.length; i++) {
const di = locations[i];
if (di.ret || di.vars === undefined) continue;
sm[i] = {
const common = {
path: resolve(di.file),
line: di.line,
variables: di.vars ?? [],
function: di.func,
};
if (di.ret) {
sm[i] = {
...common,
type: 'return',
};
} else if (di.is_catch) {
sm[i] = {
...common,
type: 'catch',
};
} else {
sm[i] = {
...common,
type: 'statement',
variables: di.vars ?? [],
firstStatement: di.first_stmt,
};
}
}

defaultDebugInfoCache.set(c.code.hash().toString('base64'), {
Expand Down
135 changes: 117 additions & 18 deletions src/debugger/Debuggee.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import EventEmitter from 'node:events';
import { Executor, GetMethodArgs, RunTransactionArgs } from '../executor/Executor';
import { Cell, TupleItem } from '@ton/core';
import { InitializedEvent, Logger, logger, LoggingDebugSession, OutputEvent, StoppedEvent, TerminatedEvent, Thread } from '@vscode/debugadapter';
import { DebugProtocol } from '@vscode/debugprotocol';
import { basename } from 'node:path';

export type SourceMapEntry = {
export type SourceMapEntry = ({
path: string;
line: number;
function: string;
}) & ({
type: 'statement';
variables: string[];
};
firstStatement?: true;
} | {
type: 'return';
} | {
type: 'catch';
});

export type SourceMap = {
[k: number]: SourceMapEntry;
Expand All @@ -35,6 +40,12 @@ export type Variable = {
value: TupleItem;
};

type StackFrame = {
function: string;
path: string;
line: number;
};

export class Debuggee extends EventEmitter {
executor: Executor;
ptr: number = 0;
Expand All @@ -44,7 +55,7 @@ export class Debuggee extends EventEmitter {
codeCells: Map<string, Cell> = new Map();
breakpoints: Map<string, Breakpoint[]> = new Map();
breakpointID: number = 0;
frames: string[] = [];
frames: StackFrame[] = [];
globals: GlobalEntry[] = [];
finishedCallback: (v: any) => void;

Expand Down Expand Up @@ -104,11 +115,19 @@ export class Debuggee extends EventEmitter {
}

continue() {
this.stepUntilLine(true);
this.stepUntil({ type: 'breakpoint' });
}

stepIn() {
this.stepUntil({ type: 'any-line', stopEvent: 'stopOnStep' });
}

step(stopEvent = 'stopOnStep') {
this.stepUntilLine(false, stopEvent);
stepOver() {
this.stepUntil({ type: 'next-line' });
}

stepOut() {
this.stepUntil({ type: 'out' });
}

startGetMethod(args: GetMethodArgs) {
Expand Down Expand Up @@ -161,9 +180,27 @@ export class Debuggee extends EventEmitter {
}
}

getContParam() {
switch (this.debugType) {
case 'get':
return this.executor.sbsGetMethodGetContParam(this.ptr);
case 'tx':
return this.executor.sbsTransactionGetContParam(this.ptr);
}
}

setContParam(param: number) {
switch (this.debugType) {
case 'get':
return this.executor.sbsGetMethodSetContParam(this.ptr, param);
case 'tx':
return this.executor.sbsTransactionSetContParam(this.ptr, param);
}
}

getLocalVariables(): Variable[] | undefined {
const sme = this.currentSourceMapEntry();
if (sme === undefined) {
if (sme === undefined || sme.type !== 'statement') {
return undefined;
}

Expand Down Expand Up @@ -297,21 +334,83 @@ export class Debuggee extends EventEmitter {
this.finishedCallback(r)
}

stepUntilLine(breakpointsOnly: boolean, stopEvent?: string) {
stackFrames(): StackFrame[] {
return this.frames
}

stepUntil(what: { type: 'breakpoint' } | { type: 'any-line', stopEvent: 'stopOnBreakpoint' | 'stopOnStep' } | { type: 'next-line' } | { type: 'out' }) {
let until: { type: 'breakpoint' } | { type: 'any-line', stopEvent: 'stopOnBreakpoint' | 'stopOnStep' } | { type: 'next-line', depth: number } | { type: 'out', depth: number }
switch (what.type) {
case 'next-line':
case 'out': {
until = { type: what.type, depth: this.frames.length }
break
}
default:
until = what
}
while (true) {
const finished = this.vmStep()
if (finished) {
this.onFinished()
return
}
const sme = this.currentSourceMapEntry()
if (sme !== undefined && (!breakpointsOnly || this.hasBreakpoint(sme.path, sme.line))) {
if (breakpointsOnly) {
this.sendEvent('stopOnBreakpoint')
} else if (stopEvent !== undefined) {
this.sendEvent(stopEvent)
if (sme !== undefined) {
switch (sme.type) {
case 'statement': {
if (sme.firstStatement) {
this.frames.push({
function: sme.function,
path: sme.path,
line: sme.line,
})
this.setContParam(this.frames.length)
}

this.frames[this.frames.length-1].line = sme.line

switch (until.type) {
case 'breakpoint': {
if (this.hasBreakpoint(sme.path, sme.line)) {
this.sendEvent('stopOnBreakpoint')
return
}
break
}
case 'any-line': {
this.sendEvent(until.stopEvent)
return
}
case 'next-line': {
if (this.frames.length <= until.depth) {
this.sendEvent('stopOnStep')
return
}
break
}
case 'out': {
if (this.frames.length < until.depth) {
this.sendEvent('stopOnStep')
return
}
break
}
}

break
}
case 'return': {
this.frames.pop()

break
}
case 'catch': {
this.frames = this.frames.slice(0, this.getContParam())

break
}
}
return
}
}
}
Expand All @@ -331,7 +430,7 @@ export class Debuggee extends EventEmitter {
start(debug: boolean, stopOnEntry: boolean) {
if (debug) {
if (stopOnEntry) {
this.step('stopOnEntry');
this.stepUntil({ type: 'any-line', stopEvent: 'stopOnBreakpoint' });
} else {
this.continue();
}
Expand Down
45 changes: 30 additions & 15 deletions src/debugger/TVMDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,17 +172,17 @@ export class TVMDebugSession extends LoggingDebugSession {
}

protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments, request?: DebugProtocol.Request | undefined): void {
this.debuggee.step();
this.debuggee.stepOver();
this.sendResponse(response);
}

protected stepInRequest(response: DebugProtocol.StepInResponse, args: DebugProtocol.StepInArguments, request?: DebugProtocol.Request | undefined): void {
this.debuggee.step();
this.debuggee.stepIn();
this.sendResponse(response);
}

protected stepOutRequest(response: DebugProtocol.StepOutResponse, args: DebugProtocol.StepOutArguments, request?: DebugProtocol.Request | undefined): void {
this.debuggee.step();
this.debuggee.stepOut();
this.sendResponse(response);
}

Expand All @@ -197,31 +197,46 @@ export class TVMDebugSession extends LoggingDebugSession {
return;
}

response.body.totalFrames = 1;
const frames = this.debuggee.stackFrames();

if (args.startFrame ?? 0 > 0) {
response.body.totalFrames = frames.length;

if (args.startFrame ?? 0 >= frames.length) {
response.body.stackFrames = [];
this.sendResponse(response);
return;
}

response.body.stackFrames = [{
id: TVMDebugSession.stackFrameID,
name: 'func',
line: sme.line,
column: 0,
source: {
name: basename(sme.path),
path: sme.path,
},
}];
response.body.stackFrames = [];

for (let i = args.startFrame ?? 0; i < frames.length; i++) {
const frame = frames[i];
response.body.stackFrames.push({
id: i === frames.length - 1 ? TVMDebugSession.stackFrameID : 0,
name: frame.function,
line: frame.line,
column: 0,
source: {
name: basename(frame.path),
path: frame.path,
},
});
}

response.body.stackFrames.reverse();

this.sendResponse(response);
}

protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments, request?: DebugProtocol.Request | undefined): void {
response.body = response.body || {};

if (args.frameId !== TVMDebugSession.stackFrameID) {
response.body.scopes = [];
this.sendResponse(response);
return;
}

const sme = this.debuggee.currentSourceMapEntry();
if (sme === undefined) {
response.body.scopes = [];
Expand Down
26 changes: 26 additions & 0 deletions src/executor/Executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,19 @@ export class Executor implements IExecutor {
return parseTuple(beginCell().storeUint(1, 24).storeRef(Cell.EMPTY).storeSlice(Cell.fromBase64(resp).beginParse()).endCell())[0]
}

sbsGetMethodGetContParam(ptr: number) {
return this.invoke('_sbs_get_cont_param', [
ptr
])
}

sbsGetMethodSetContParam(ptr: number, param: number) {
return this.invoke('_sbs_set_cont_param', [
ptr,
param
])
}

sbsGetMethodCodePos(ptr: number) {
const resp = this.extractString(this.invoke('_sbs_get_code_pos', [
ptr
Expand Down Expand Up @@ -523,6 +536,19 @@ export class Executor implements IExecutor {
return parseTuple(beginCell().storeUint(1, 24).storeRef(Cell.EMPTY).storeSlice(Cell.fromBase64(resp).beginParse()).endCell())[0]
}

sbsTransactionGetContParam(ptr: number) {
return this.invoke('_em_sbs_get_cont_param', [
ptr
])
}

sbsTransactionSetContParam(ptr: number, param: number) {
return this.invoke('_em_sbs_set_cont_param', [
ptr,
param
])
}

sbsTransactionResult(ptr: number): EmulationResult {
const result = JSON.parse(this.extractString(this.invoke('_em_sbs_result', [
ptr
Expand Down
2 changes: 1 addition & 1 deletion src/executor/emulator-emscripten.js

Large diffs are not rendered by default.

Binary file modified src/executor/emulator-emscripten.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion src/executor/emulator-emscripten.wasm.js

Large diffs are not rendered by default.

Loading

0 comments on commit 2b355fd

Please sign in to comment.