diff --git a/src/evaluator.ts b/src/evaluator.ts index eb5771c..5638d5e 100644 --- a/src/evaluator.ts +++ b/src/evaluator.ts @@ -144,11 +144,6 @@ function ` + functionName + `(` + argsString + `) returns (bool) { return; } - if (this._runtime._ongoingEvaluation !== null) { - // TODO: improve this - return; - } - expression = expression + (expression.endsWith(';') ? '' : ';'); let contract = this._runtime._contractsByAddress.get(this._runtime._stepData.contractAddress)!; let file = this._runtime._files.get(contract.sourcePath)!; @@ -290,20 +285,30 @@ function ` + functionName + `(` + argsString + `) returns (bool) { // find last jumpdest thats within the source location - this._runtime._ongoingEvaluation = new LibSdbTypes.Evaluation(); - this._runtime._ongoingEvaluation.functionName = functionInsert.name; - this._runtime._ongoingEvaluation.callback = callback; - this._runtime._ongoingEvaluation.returnVariable.originalType = returnTypeString; + let ongoingEvaluation = new LibSdbTypes.Evaluation(); + ongoingEvaluation.functionName = functionInsert.name; + ongoingEvaluation.returnVariable.originalType = returnTypeString; // TODO: handle referenceVars in result? const compilationProcessor = new LibSdbCompilationProcessor(); const contractProcessor = new ContractProcessor(compilationProcessor, contract); - this._runtime._ongoingEvaluation.returnVariable.applyType("default", "ParameterList", contractProcessor); - this._runtime._ongoingEvaluation.contractAddress = this._runtime._stepData.contractAddress; + ongoingEvaluation.returnVariable.applyType("default", "ParameterList", contractProcessor); + ongoingEvaluation.contractAddress = this._runtime._stepData.contractAddress; //this._runtime.continue(false, "stopOnEvalBreakpoint"); if (newStartPc !== null && newEndPc !== null) { const evalRequest = new LibSdbTypes.EvaluationRequest(evaluationBytecode.object, newStartPc, newEndPc, contract.runtimeBytecode, this._runtime._stepData.vmData.pc); - await this._runtime._interface.requestEvaluation(evalRequest); + const vmData = await this._runtime._interface.requestEvaluation(evalRequest); + + ongoingEvaluation.returnVariable.position = vmData.stack.length - 1; + + let returnValue; + if (ongoingEvaluation.returnVariable.detail === null) { + returnValue = null; + } + else { + returnValue = await ongoingEvaluation.returnVariable.detail.decode(vmData.stack, vmData.memory, this._runtime._interface, ongoingEvaluation.contractAddress); + } + callback(returnValue); } } else { diff --git a/src/interface.ts b/src/interface.ts index 8b453df..9b953c7 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -62,7 +62,7 @@ export class LibSdbInterface { } } - public requestInjectCode(bytecode: string, pc: number): Promise { + public requestInjectCode(bytecode: string, pc: number, vmData: any = undefined): Promise { return new Promise((resolve, reject) => { const msgId = uuidv4(); @@ -76,6 +76,14 @@ export class LibSdbInterface { } }; + if (vmData !== undefined) { + request.content.state = { + "stack": vmData.stack, + "memory": vmData.memory, + "gasLeft": vmData.gasLeft + } + } + this._debuggerMessages.set(msgId, resolve); if (this.evm !== undefined) { @@ -84,7 +92,7 @@ export class LibSdbInterface { }); } - public requestRuntUntilPc(pc: number): Promise { + public requestRunUntilPc(pc: number): Promise { return new Promise((resolve, reject) => { const msgId = uuidv4(); @@ -93,10 +101,12 @@ export class LibSdbInterface { "messageType": "request", "content": { "type": "runUntilPc", + "stepId": this._runtime._stepData!.debuggerMessageId, "pc": pc } }; + this._debuggerMessages.delete(this._runtime._stepData!.debuggerMessageId); this._debuggerMessages.set(msgId, resolve); if (this.evm !== undefined) { @@ -105,16 +115,14 @@ export class LibSdbInterface { }); } - public async requestEvaluation(evalRequest: LibSdbTypes.EvaluationRequest): Promise { + public async requestEvaluation(evalRequest: LibSdbTypes.EvaluationRequest): Promise { await this.requestInjectCode(evalRequest.evaluationBytecode, evalRequest.evaluationStartPc); - await this.requestRuntUntilPc(evalRequest.evaluationEndPc); + const vmData = await this.requestRunUntilPc(evalRequest.evaluationEndPc); - // TODO: get current state and get return variable + await this.requestInjectCode(evalRequest.runtimeBytecode, evalRequest.runtimePc, this._runtime._stepData!.vmData); - await this.requestInjectCode(evalRequest.runtimeBytecode, evalRequest.runtimePc); - - return new LibSdbTypes.Variable(); + return vmData; } public async requestStorage(address: any, position: any): Promise { @@ -384,6 +392,14 @@ export class LibSdbInterface { debuggerMessage(data.content); } this._debuggerMessages.delete(data.id); + + if (triggerType === "runUntilPc") { + // the step data id gets modified due to changes in sdbhook + this._runtime._stepData!.debuggerMessageId = data.id; + this._debuggerMessages.set(data.id, (message) => { + this.evm.handleMessage(message); + }); + } } } diff --git a/src/runtime.ts b/src/runtime.ts index a1a4dae..dd83e4c 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -8,6 +8,7 @@ import { LibSdbBreakpoints } from "./breakpoints"; import { LibSdbEvaluator } from "./evaluator"; import { ValueDetail } from "./types/barrel"; import { LibSdbConstants } from "./utils/constants"; +import { BN } from "bn.js"; const CircularJSON = require("circular-json"); @@ -23,8 +24,6 @@ export class LibSdbRuntime extends EventEmitter { public _callStack: LibSdbTypes.StackFrame[]; public _priorUiCallStack: LibSdbTypes.StackFrame[] | null; - public _ongoingEvaluation: LibSdbTypes.Evaluation | null; - public _files: LibSdbTypes.FileMap; public _filesById: LibSdbTypes.FileByIdMap; public _contractsByName: LibSdbTypes.ContractMap; @@ -58,8 +57,6 @@ export class LibSdbRuntime extends EventEmitter { this._callStack = []; this._priorUiCallStack = []; - - this._ongoingEvaluation = null; } public static instance(): LibSdbRuntime { @@ -114,30 +111,6 @@ export class LibSdbRuntime extends EventEmitter { private async processJumpOut(contract: LibSdbTypes.Contract, stack: any, memory: any): Promise { // jump out, we should be at a JUMPDEST currently - if (this._priorStepData) { - const node = LibSdbUtils.SourceMappingDecoder.findNodeAtSourceLocation("FunctionDefinition", this._priorStepData.source, { AST: contract.ast }); - if (node !== null) { - const functionName = node.attributes.name; - if (this._ongoingEvaluation !== null && this._ongoingEvaluation.functionName === functionName) { - // get variable at top of stack - // TODO: add support for multiple variable evaluations - - this._ongoingEvaluation.returnVariable.position = stack.length - 1; - - let returnValue; - if (this._ongoingEvaluation.returnVariable.detail === null) { - returnValue = null; - } - else { - returnValue = await this._ongoingEvaluation.returnVariable.detail.decode(stack, memory, this._interface, this._ongoingEvaluation.contractAddress); - } - this._ongoingEvaluation.callback(returnValue); - - this._ongoingEvaluation = null; - } - } - } - this._callStack.shift(); } @@ -257,7 +230,12 @@ export class LibSdbRuntime extends EventEmitter { this._stepData.source = sourceLocation; this._stepData.location = currentLocation; this._stepData.contractAddress = address; - this._stepData.vmData = data.content; + this._stepData.vmData = CircularJSON.parse(CircularJSON.stringify(data.content)); // make a deep copy TODO: make this better + this._stepData.vmData.gasLeft = new BN(data.content.gasLeft); + this._stepData.vmData.stack = []; + for (let i = 0; i < data.content.stack.length; i++) { + this._stepData.vmData.stack.push(new BN(data.content.stack[i])); + } this._stepData.scope = currentScope; this._stepData.events = data.content.specialEvents; if (data.exceptionError !== undefined) { diff --git a/src/types/evaluation.ts b/src/types/evaluation.ts index 3e07b4b..b7bdb15 100644 --- a/src/types/evaluation.ts +++ b/src/types/evaluation.ts @@ -4,7 +4,6 @@ export class Evaluation { functionName: string; returnVariable: Variable; contractAddress: string; - callback: Function; constructor() { this.returnVariable = new Variable(); @@ -17,8 +16,6 @@ export class Evaluation { clone.contractAddress = this.contractAddress; - clone.callback = this.callback; - return clone; } } diff --git a/src/types/stepData.ts b/src/types/stepData.ts index 357559d..91b6e96 100644 --- a/src/types/stepData.ts +++ b/src/types/stepData.ts @@ -1,4 +1,5 @@ import { AstScope } from "./astScope"; +import { BN } from "bn.js"; const CircularJSON = require("circular-json"); @@ -28,7 +29,12 @@ export class StepData { clone.contractAddress = this.contractAddress; - clone.vmData = CircularJSON.parse(CircularJSON.stringify(this.vmData)); + clone.vmData = CircularJSON.parse(CircularJSON.stringify(this.vmData)); // TODO: make this better + clone.vmData.gasLeft = new BN(this.vmData.gasLeft); + clone.vmData.stack = []; + for (let i = 0; i < this.vmData.stack.length; i++) { + clone.vmData.stack.push(new BN(this.vmData.stack[i])); + } for (let i = 0; i < this.scope.length; i++) { clone.scope.push(this.scope[i].clone());