Merge pull request #995 from ethereum/move_debugger
move debugger logic from remix-ide to remix-debugpull/5370/head
commit
bb6d206801
@ -0,0 +1,65 @@ |
||||
var remixLib = require('remix-lib') |
||||
var EventManager = remixLib.EventManager |
||||
|
||||
var localDecoder = require('../solidity-decoder/localDecoder') |
||||
var StorageViewer = require('../storage/storageViewer') |
||||
|
||||
class DebuggerSolidityLocals { |
||||
|
||||
constructor (tx, _stepManager, _traceManager, _internalTreeCall) { |
||||
this.event = new EventManager() |
||||
this.stepManager = _stepManager |
||||
this.internalTreeCall = _internalTreeCall |
||||
this.storageResolver = null |
||||
this.traceManager = _traceManager |
||||
this.tx = tx |
||||
} |
||||
|
||||
init (sourceLocation) { |
||||
const self = this |
||||
var decodeTimeout = null |
||||
if (!this.storageResolver) { |
||||
return self.event.trigger('solidityLocalsMessage', ['storage not ready']) |
||||
} |
||||
if (decodeTimeout) { |
||||
window.clearTimeout(decodeTimeout) |
||||
} |
||||
self.event.trigger('solidityLocalsUpdating') |
||||
decodeTimeout = setTimeout(function () { |
||||
self.decode(sourceLocation) |
||||
}, 500) |
||||
} |
||||
|
||||
decode (sourceLocation) { |
||||
const self = this |
||||
self.event.trigger('solidityLocalsMessage', ['']) |
||||
self.traceManager.waterfall([ |
||||
self.traceManager.getStackAt, |
||||
self.traceManager.getMemoryAt, |
||||
self.traceManager.getCurrentCalledAddressAt], |
||||
self.stepManager.currentStepIndex, |
||||
(error, result) => { |
||||
if (error) { |
||||
return console.log(error) |
||||
} |
||||
var stack = result[0].value |
||||
var memory = result[1].value |
||||
try { |
||||
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: result[2].value }, self.storageResolver, self.traceManager) |
||||
localDecoder.solidityLocals(self.stepManager.currentStepIndex, self.internalTreeCall, stack, memory, storageViewer, sourceLocation).then((locals) => { |
||||
if (!locals.error) { |
||||
self.event.trigger('solidityLocals', [locals]) |
||||
} |
||||
if (!Object.keys(locals).length) { |
||||
self.event.trigger('solidityLocalsMessage', ['no locals']) |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
self.event.trigger('solidityLocalsMessage', [e.message]) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
} |
||||
|
||||
module.exports = DebuggerSolidityLocals |
@ -0,0 +1,241 @@ |
||||
var remixLib = require('remix-lib') |
||||
var EventManager = remixLib.EventManager |
||||
var ui = remixLib.helpers.ui |
||||
var StorageResolver = require('../storage/storageResolver') |
||||
var StorageViewer = require('../storage/storageViewer') |
||||
|
||||
var DebuggerSolidityState = require('./solidityState') |
||||
var DebuggerSolidityLocals = require('./solidityLocals') |
||||
|
||||
class VmDebuggerLogic { |
||||
|
||||
constructor (_debugger, tx, _stepManager, _traceManager, _codeManager, _solidityProxy, _callTree) { |
||||
this.event = new EventManager() |
||||
this.debugger = _debugger |
||||
this.stepManager = _stepManager |
||||
this._traceManager = _traceManager |
||||
this._codeManager = _codeManager |
||||
this._solidityProxy = _solidityProxy |
||||
this._callTree = _callTree |
||||
this.storageResolver = null |
||||
this.tx = tx |
||||
|
||||
this.debuggerSolidityState = new DebuggerSolidityState(tx, _stepManager, _traceManager, _codeManager, _solidityProxy) |
||||
this.debuggerSolidityLocals = new DebuggerSolidityLocals(tx, _stepManager, _traceManager, _callTree) |
||||
} |
||||
|
||||
start () { |
||||
this.listenToEvents() |
||||
this.listenToCodeManagerEvents() |
||||
this.listenToTraceManagerEvents() |
||||
this.listenToFullStorageChanges() |
||||
this.listenToNewChanges() |
||||
|
||||
this.listenToSolidityStateEvents() |
||||
this.listenToSolidityLocalsEvents() |
||||
} |
||||
|
||||
listenToEvents () { |
||||
const self = this |
||||
this.debugger.event.register('traceUnloaded', function () { |
||||
self.event.trigger('traceUnloaded') |
||||
}) |
||||
|
||||
this.debugger.event.register('newTraceLoaded', function () { |
||||
self.event.trigger('newTraceLoaded') |
||||
}) |
||||
} |
||||
|
||||
listenToCodeManagerEvents () { |
||||
const self = this |
||||
this._codeManager.event.register('changed', function (code, address, index) { |
||||
self.event.trigger('codeManagerChanged', [code, address, index]) |
||||
}) |
||||
} |
||||
|
||||
listenToTraceManagerEvents () { |
||||
const self = this |
||||
|
||||
this.event.register('indexChanged', this, function (index) { |
||||
if (index < 0) return |
||||
if (self.stepManager.currentStepIndex !== index) return |
||||
|
||||
self.event.trigger('indexUpdate', [index]) |
||||
|
||||
self._traceManager.getCallDataAt(index, function (error, calldata) { |
||||
if (error) { |
||||
console.log(error) |
||||
self.event.trigger('traceManagerCallDataUpdate', [{}]) |
||||
} else if (self.stepManager.currentStepIndex === index) { |
||||
self.event.trigger('traceManagerCallDataUpdate', [calldata]) |
||||
} |
||||
}) |
||||
|
||||
self._traceManager.getMemoryAt(index, function (error, memory) { |
||||
if (error) { |
||||
console.log(error) |
||||
self.event.trigger('traceManagerMemoryUpdate', [{}]) |
||||
} else if (self.stepManager.currentStepIndex === index) { |
||||
self.event.trigger('traceManagerMemoryUpdate', [ui.formatMemory(memory, 16)]) |
||||
} |
||||
}) |
||||
|
||||
self._traceManager.getCallStackAt(index, function (error, callstack) { |
||||
if (error) { |
||||
console.log(error) |
||||
self.event.trigger('traceManagerCallStackUpdate', [{}]) |
||||
} else if (self.stepManager.currentStepIndex === index) { |
||||
self.event.trigger('traceManagerCallStackUpdate', [callstack]) |
||||
} |
||||
}) |
||||
|
||||
self._traceManager.getStackAt(index, function (error, callstack) { |
||||
if (error) { |
||||
console.log(error) |
||||
self.event.trigger('traceManagerStackUpdate', [{}]) |
||||
} else if (self.stepManager.currentStepIndex === index) { |
||||
self.event.trigger('traceManagerStackUpdate', [callstack]) |
||||
} |
||||
}) |
||||
|
||||
self._traceManager.getCurrentCalledAddressAt(index, (error, address) => { |
||||
if (error) return |
||||
if (!self.storageResolver) return |
||||
|
||||
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: address }, self.storageResolver, self._traceManager) |
||||
|
||||
storageViewer.storageRange((error, storage) => { |
||||
if (error) { |
||||
console.log(error) |
||||
self.event.trigger('traceManagerStorageUpdate', [{}]) |
||||
} else if (self.stepManager.currentStepIndex === index) { |
||||
var header = storageViewer.isComplete(address) ? 'completely loaded' : 'partially loaded...' |
||||
self.event.trigger('traceManagerStorageUpdate', [storage, header]) |
||||
} |
||||
}) |
||||
}) |
||||
|
||||
self._traceManager.getCurrentStep(index, function (error, step) { |
||||
self.event.trigger('traceCurrentStepUpdate', [error, step]) |
||||
}) |
||||
|
||||
self._traceManager.getMemExpand(index, function (error, addmem) { |
||||
self.event.trigger('traceMemExpandUpdate', [error, addmem]) |
||||
}) |
||||
|
||||
self._traceManager.getStepCost(index, function (error, gas) { |
||||
self.event.trigger('traceStepCostUpdate', [error, gas]) |
||||
}) |
||||
|
||||
self._traceManager.getCurrentCalledAddressAt(index, function (error, address) { |
||||
self.event.trigger('traceCurrentCalledAddressAtUpdate', [error, address]) |
||||
}) |
||||
|
||||
self._traceManager.getRemainingGas(index, function (error, remaining) { |
||||
self.event.trigger('traceRemainingGasUpdate', [error, remaining]) |
||||
}) |
||||
|
||||
self._traceManager.getReturnValue(index, function (error, returnValue) { |
||||
if (error) { |
||||
self.event.trigger('traceReturnValueUpdate', [[error]]) |
||||
} else if (self.stepManager.currentStepIndex === index) { |
||||
self.event.trigger('traceReturnValueUpdate', [[returnValue]]) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
listenToFullStorageChanges () { |
||||
const self = this |
||||
|
||||
this.address = [] |
||||
this.traceLength = 0 |
||||
|
||||
self.debugger.event.register('newTraceLoaded', function (length) { |
||||
self._traceManager.getAddresses(function (error, addresses) { |
||||
if (error) return |
||||
self.event.trigger('traceAddressesUpdate', [addresses]) |
||||
self.addresses = addresses |
||||
}) |
||||
|
||||
self._traceManager.getLength(function (error, length) { |
||||
if (error) return |
||||
self.event.trigger('traceLengthUpdate', [length]) |
||||
self.traceLength = length |
||||
}) |
||||
}) |
||||
|
||||
self.debugger.event.register('indexChanged', this, function (index) { |
||||
if (index < 0) return |
||||
if (self.stepManager.currentStepIndex !== index) return |
||||
if (!self.storageResolver) return |
||||
|
||||
if (index !== self.traceLength - 1) { |
||||
return self.event.trigger('traceLengthUpdate', [{}]) |
||||
} |
||||
var storageJSON = {} |
||||
for (var k in self.addresses) { |
||||
var address = self.addresses[k] |
||||
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: address }, self.storageResolver, self._traceManager) |
||||
storageViewer.storageRange(function (error, result) { |
||||
if (!error) { |
||||
storageJSON[address] = result |
||||
self.event.trigger('traceLengthUpdate', [storageJSON]) |
||||
} |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
listenToNewChanges () { |
||||
const self = this |
||||
self.debugger.event.register('newTraceLoaded', this, function () { |
||||
self.storageResolver = new StorageResolver({web3: self.debugger.web3}) |
||||
self.debuggerSolidityState.storageResolver = self.storageResolver |
||||
self.debuggerSolidityLocals.storageResolver = self.storageResolver |
||||
self.event.trigger('newTrace', []) |
||||
}) |
||||
|
||||
self.debugger.event.register('callTreeReady', function () { |
||||
if (self.debugger.callTree.reducedTrace.length) { |
||||
return self.event.trigger('newCallTree', []) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
listenToSolidityStateEvents () { |
||||
const self = this |
||||
this.event.register('indexChanged', this.debuggerSolidityState.init.bind(this.debuggerSolidityState)) |
||||
this.debuggerSolidityState.event.register('solidityState', function (state) { |
||||
self.event.trigger('solidityState', [state]) |
||||
}) |
||||
this.debuggerSolidityState.event.register('solidityStateMessage', function (message) { |
||||
self.event.trigger('solidityStateMessage', [message]) |
||||
}) |
||||
this.debuggerSolidityState.event.register('solidityStateUpdating', function () { |
||||
self.event.trigger('solidityStateUpdating', []) |
||||
}) |
||||
this.event.register('traceUnloaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState)) |
||||
this.event.register('newTraceLoaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState)) |
||||
} |
||||
|
||||
listenToSolidityLocalsEvents () { |
||||
const self = this |
||||
this.event.register('sourceLocationChanged', this.debuggerSolidityLocals.init.bind(this.debuggerSolidityLocals)) |
||||
this.debuggerSolidityLocals.event.register('solidityLocals', function (state) { |
||||
self.event.trigger('solidityLocals', [state]) |
||||
}) |
||||
this.debuggerSolidityLocals.event.register('solidityLocalsMessage', function (message) { |
||||
self.event.trigger('solidityLocalsMessage', [message]) |
||||
}) |
||||
this.debuggerSolidityLocals.event.register('solidityLocalsUpdating', function () { |
||||
self.event.trigger('solidityLocalsUpdating', []) |
||||
}) |
||||
this.debuggerSolidityLocals.event.register('traceReturnValueUpdate', function (data, header) { |
||||
self.event.trigger('traceReturnValueUpdate', [data, header]) |
||||
}) |
||||
} |
||||
|
||||
} |
||||
|
||||
module.exports = VmDebuggerLogic |
@ -0,0 +1,131 @@ |
||||
'use strict' |
||||
var Ethdebugger = require('../Ethdebugger') |
||||
var remixLib = require('remix-lib') |
||||
var EventManager = remixLib.EventManager |
||||
var traceHelper = remixLib.helpers.trace |
||||
|
||||
var StepManager = require('./stepManager') |
||||
var VmDebuggerLogic = require('./VmDebugger') |
||||
|
||||
function Debugger (options) { |
||||
var self = this |
||||
this.event = new EventManager() |
||||
|
||||
this.offsetToLineColumnConverter = options.offsetToLineColumnConverter |
||||
this.compiler = options.compiler |
||||
|
||||
this.debugger = new Ethdebugger({ |
||||
web3: options.web3, |
||||
compilationResult: () => { |
||||
var compilationResult = this.compiler.lastCompilationResult |
||||
if (compilationResult) { |
||||
return compilationResult.data |
||||
} |
||||
return null |
||||
} |
||||
}) |
||||
|
||||
this.breakPointManager = new remixLib.code.BreakpointManager(this.debugger, (sourceLocation) => { |
||||
return self.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, this.compiler.lastCompilationResult.source.sources, this.compiler.lastCompilationResult.data.sources) |
||||
}, (step) => { |
||||
self.event.trigger('breakpointStep', [step]) |
||||
}) |
||||
|
||||
this.debugger.setBreakpointManager(this.breakPointManager) |
||||
|
||||
this.debugger.event.register('newTraceLoaded', this, function () { |
||||
self.event.trigger('debuggerStatus', [true]) |
||||
}) |
||||
|
||||
this.debugger.event.register('traceUnloaded', this, function () { |
||||
self.event.trigger('debuggerStatus', [false]) |
||||
}) |
||||
|
||||
this.event.register('breakpointStep', function (step) { |
||||
self.step_manager.jumpTo(step) |
||||
}) |
||||
} |
||||
|
||||
Debugger.prototype.registerAndHighlightCodeItem = function (index) { |
||||
const self = this |
||||
// register selected code item, highlight the corresponding source location
|
||||
if (!self.compiler.lastCompilationResult) return |
||||
self.debugger.traceManager.getCurrentCalledAddressAt(index, (error, address) => { |
||||
if (error) return console.log(error) |
||||
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, index, self.compiler.lastCompilationResult.data.contracts, function (error, rawLocation) { |
||||
if (!error && self.compiler.lastCompilationResult && self.compiler.lastCompilationResult.data) { |
||||
var lineColumnPos = self.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, self.compiler.lastCompilationResult.source.sources, self.compiler.lastCompilationResult.data.sources) |
||||
self.event.trigger('newSourceLocation', [lineColumnPos, rawLocation]) |
||||
} else { |
||||
self.event.trigger('newSourceLocation', [null]) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
Debugger.prototype.updateWeb3 = function (web3) { |
||||
this.debugger.web3 = web3 |
||||
} |
||||
|
||||
Debugger.prototype.debug = function (blockNumber, txNumber, tx, loadingCb) { |
||||
const self = this |
||||
let web3 = this.debugger.web3 |
||||
|
||||
if (this.debugger.traceManager.isLoading) { |
||||
return |
||||
} |
||||
|
||||
if (tx) { |
||||
if (!tx.to) { |
||||
tx.to = traceHelper.contractCreationToken('0') |
||||
} |
||||
return self.debugTx(tx, loadingCb) |
||||
} |
||||
|
||||
try { |
||||
if (txNumber.indexOf('0x') !== -1) { |
||||
return web3.eth.getTransaction(txNumber, function (_error, result) { |
||||
let tx = result |
||||
self.debugTx(tx, loadingCb) |
||||
}) |
||||
} |
||||
web3.eth.getTransactionFromBlock(blockNumber, txNumber, function (_error, result) { |
||||
let tx = result |
||||
self.debugTx(tx, loadingCb) |
||||
}) |
||||
} catch (e) { |
||||
console.error(e.message) |
||||
} |
||||
} |
||||
|
||||
Debugger.prototype.debugTx = function (tx, loadingCb) { |
||||
const self = this |
||||
this.step_manager = new StepManager(this.debugger, this.debugger.traceManager) |
||||
|
||||
this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => { |
||||
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.step_manager.currentStepIndex, this.debugger.solidityProxy.contracts, (error, sourceLocation) => { |
||||
if (!error) { |
||||
self.vmDebuggerLogic.event.trigger('sourceLocationChanged', [sourceLocation]) |
||||
} |
||||
}) |
||||
}) |
||||
|
||||
this.vmDebuggerLogic = new VmDebuggerLogic(this.debugger, tx, this.step_manager, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree) |
||||
|
||||
this.step_manager.event.register('stepChanged', this, function (stepIndex) { |
||||
self.debugger.codeManager.resolveStep(stepIndex, tx) |
||||
self.step_manager.event.trigger('indexChanged', [stepIndex]) |
||||
self.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex]) |
||||
self.registerAndHighlightCodeItem(stepIndex) |
||||
}) |
||||
|
||||
loadingCb() |
||||
this.debugger.debug(tx) |
||||
} |
||||
|
||||
Debugger.prototype.unload = function () { |
||||
this.debugger.unLoad() |
||||
this.event.trigger('debuggerUnloaded') |
||||
} |
||||
|
||||
module.exports = Debugger |
@ -0,0 +1,80 @@ |
||||
var remixLib = require('remix-lib') |
||||
var EventManager = remixLib.EventManager |
||||
var stateDecoder = require('../solidity-decoder/stateDecoder') |
||||
var StorageViewer = require('../storage/storageViewer') |
||||
|
||||
class DebuggerSolidityState { |
||||
|
||||
constructor (tx, _stepManager, _traceManager, _codeManager, _solidityProxy) { |
||||
this.event = new EventManager() |
||||
this.storageResolver = null |
||||
this.stepManager = _stepManager |
||||
this.traceManager = _traceManager |
||||
this.codeManager = _codeManager |
||||
this.solidityProxy = _solidityProxy |
||||
this.stateVariablesByAddresses = {} |
||||
this.tx = tx |
||||
} |
||||
|
||||
init (index) { |
||||
var self = this |
||||
var decodeTimeout = null |
||||
if (index < 0) { |
||||
return self.event.trigger('solidityStateMessage', ['invalid step index']) |
||||
} |
||||
|
||||
if (self.stepManager.currentStepIndex !== index) return |
||||
if (!self.solidityProxy.loaded()) { |
||||
return self.event.trigger('solidityStateMessage', ['invalid step index']) |
||||
} |
||||
|
||||
if (!self.storageResolver) { |
||||
return |
||||
} |
||||
if (decodeTimeout) { |
||||
window.clearTimeout(decodeTimeout) |
||||
} |
||||
self.event.trigger('solidityStateUpdating') |
||||
decodeTimeout = setTimeout(function () { |
||||
self.decode(index) |
||||
}, 500) |
||||
} |
||||
|
||||
reset () { |
||||
this.stateVariablesByAddresses = {} |
||||
} |
||||
|
||||
decode (index) { |
||||
const self = this |
||||
self.traceManager.getCurrentCalledAddressAt(self.stepManager.currentStepIndex, function (error, address) { |
||||
if (error) { |
||||
return self.event.trigger('solidityState', [{}]) |
||||
} |
||||
if (self.stateVariablesByAddresses[address]) { |
||||
return self.extractStateVariables(self.stateVariablesByAddresses[address], address) |
||||
} |
||||
self.solidityProxy.extractStateVariablesAt(index, function (error, stateVars) { |
||||
if (error) { |
||||
return self.event.trigger('solidityState', [{}]) |
||||
} |
||||
self.stateVariablesByAddresses[address] = stateVars |
||||
self.extractStateVariables(stateVars, address) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
extractStateVariables (stateVars, address) { |
||||
const self = this |
||||
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: address }, self.storageResolver, self.traceManager) |
||||
stateDecoder.decodeState(stateVars, storageViewer).then((result) => { |
||||
self.event.trigger('solidityStateMessage', ['']) |
||||
if (result.error) { |
||||
return self.event.trigger('solidityStateMessage', [result.error]) |
||||
} |
||||
self.event.trigger('solidityState', [result]) |
||||
}) |
||||
} |
||||
|
||||
} |
||||
|
||||
module.exports = DebuggerSolidityState |
@ -0,0 +1,145 @@ |
||||
var remixLib = require('remix-lib') |
||||
var EventManager = remixLib.EventManager |
||||
|
||||
class DebuggerStepManager { |
||||
|
||||
constructor (_debugger, traceManager) { |
||||
this.event = new EventManager() |
||||
this.debugger = _debugger |
||||
this.traceManager = traceManager |
||||
this.currentStepIndex = 0 |
||||
this.traceLength = 0 |
||||
this.revertionPoint = null |
||||
|
||||
this.listenToEvents() |
||||
} |
||||
|
||||
listenToEvents () { |
||||
const self = this |
||||
|
||||
this.debugger.event.register('newTraceLoaded', this, function () { |
||||
self.traceManager.getLength(function (error, newLength) { |
||||
if (error) { |
||||
return console.log(error) |
||||
} |
||||
if (self.traceLength !== newLength) { |
||||
self.event.trigger('traceLengthChanged', [newLength]) |
||||
self.traceLength = newLength |
||||
} |
||||
self.jumpTo(0) |
||||
}) |
||||
}) |
||||
|
||||
this.debugger.callTree.event.register('callTreeReady', () => { |
||||
if (self.debugger.callTree.functionCallStack.length) { |
||||
self.jumpTo(self.debugger.callTree.functionCallStack[0]) |
||||
} |
||||
}) |
||||
|
||||
this.event.register('indexChanged', this, (index) => { |
||||
if (index < 0) return |
||||
if (self.currentStepIndex !== index) return |
||||
|
||||
self.traceManager.buildCallPath(index, (error, callsPath) => { |
||||
if (error) { |
||||
console.log(error) |
||||
return self.event.trigger('revertWarning', ['']) |
||||
} |
||||
self.currentCall = callsPath[callsPath.length - 1] |
||||
if (self.currentCall.reverted) { |
||||
let revertedReason = self.currentCall.outofgas ? 'outofgas' : '' |
||||
self.revertionPoint = self.currentCall.return |
||||
return self.event.trigger('revertWarning', [revertedReason]) |
||||
} |
||||
for (var k = callsPath.length - 2; k >= 0; k--) { |
||||
var parent = callsPath[k] |
||||
if (!parent.reverted) continue |
||||
self.revertionPoint = parent.return |
||||
self.event.trigger('revertWarning', ['parenthasthrown']) |
||||
} |
||||
self.event.trigger('revertWarning', ['']) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
triggerStepChanged (step) { |
||||
const self = this |
||||
this.traceManager.getLength(function (error, length) { |
||||
let stepState = 'valid' |
||||
|
||||
if (error) { |
||||
stepState = 'invalid' |
||||
} else if (step <= 0) { |
||||
stepState = 'initial' |
||||
} else if (step >= length - 1) { |
||||
stepState = 'end' |
||||
} |
||||
|
||||
let jumpOutDisabled = (step === self.traceManager.findStepOut(step)) |
||||
|
||||
self.event.trigger('stepChanged', [step, stepState, jumpOutDisabled]) |
||||
}) |
||||
} |
||||
|
||||
stepIntoBack () { |
||||
if (!this.traceManager.isLoaded()) return |
||||
var step = this.currentStepIndex - 1 |
||||
this.currentStepIndex = step |
||||
if (!this.traceManager.inRange(step)) { |
||||
return |
||||
} |
||||
this.event.trigger('stepChanged', [step]) |
||||
} |
||||
|
||||
stepIntoForward () { |
||||
if (!this.traceManager.isLoaded()) return |
||||
var step = this.currentStepIndex + 1 |
||||
this.currentStepIndex = step |
||||
if (!this.traceManager.inRange(step)) { |
||||
return |
||||
} |
||||
this.event.trigger('stepChanged', [step]) |
||||
} |
||||
|
||||
stepOverBack () { |
||||
if (!this.traceManager.isLoaded()) return |
||||
var step = this.traceManager.findStepOverBack(this.currentStepIndex) |
||||
this.currentStepIndex = step |
||||
this.event.trigger('stepChanged', [step]) |
||||
} |
||||
|
||||
stepOverForward () { |
||||
if (!this.traceManager.isLoaded()) return |
||||
var step = this.traceManager.findStepOverForward(this.currentStepIndex) |
||||
this.currentStepIndex = step |
||||
this.event.trigger('stepChanged', [step]) |
||||
} |
||||
|
||||
jumpOut () { |
||||
if (!this.traceManager.isLoaded()) return |
||||
var step = this.traceManager.findStepOut(this.currentStepIndex) |
||||
this.currentStepIndex = step |
||||
this.event.trigger('stepChanged', [step]) |
||||
} |
||||
|
||||
jumpTo (step) { |
||||
if (!this.traceManager.inRange(step)) return |
||||
this.currentStepIndex = step |
||||
this.event.trigger('stepChanged', [step]) |
||||
} |
||||
|
||||
jumpToException () { |
||||
this.jumpTo(this.revertionPoint) |
||||
} |
||||
|
||||
jumpNextBreakpoint () { |
||||
this.debugger.breakpointManager.jumpNextBreakpoint(this.currentStepIndex, true) |
||||
} |
||||
|
||||
jumpPreviousBreakpoint () { |
||||
this.debugger.breakpointManager.jumpPreviousBreakpoint(this.currentStepIndex, true) |
||||
} |
||||
|
||||
} |
||||
|
||||
module.exports = DebuggerStepManager |
@ -1,190 +1,190 @@ |
||||
'use strict' |
||||
var tape = require('tape') |
||||
var remixLib = require('remix-lib') |
||||
var compilerInput = remixLib.helpers.compiler.compilerInput |
||||
var vmCall = require('./vmCall') |
||||
var Debugger = require('../src/Ethdebugger') |
||||
var compiler = require('solc') |
||||
|
||||
require('./decoder/decodeInfo.js') |
||||
require('./decoder/storageLocation.js') |
||||
require('./decoder/storageDecoder.js') |
||||
require('./decoder/localDecoder.js') |
||||
|
||||
var BreakpointManager = remixLib.code.BreakpointManager |
||||
|
||||
tape('debug contract', function (t) { |
||||
t.plan(12) |
||||
var privateKey = Buffer.from('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex') |
||||
var vm = vmCall.initVM(t, privateKey) |
||||
var output = compiler.compile(compilerInput(ballot)) |
||||
output = JSON.parse(output) |
||||
var web3VM = new remixLib.vm.Web3VMProvider() |
||||
web3VM.setVM(vm) |
||||
vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object, (error, txHash) => { |
||||
if (error) { |
||||
t.end(error) |
||||
} else { |
||||
web3VM.eth.getTransaction(txHash, (error, tx) => { |
||||
if (error) { |
||||
t.end(error) |
||||
} else { |
||||
var debugManager = new Debugger({ |
||||
compilationResult: function () { |
||||
return output |
||||
} |
||||
}) |
||||
|
||||
debugManager.addProvider('web3vmprovider', web3VM) |
||||
debugManager.switchProvider('web3vmprovider') |
||||
|
||||
debugManager.callTree.event.register('callTreeReady', () => { |
||||
testDebugging(t, debugManager) |
||||
}) |
||||
|
||||
debugManager.debug(tx) |
||||
} |
||||
}) |
||||
} |
||||
}) |
||||
}) |
||||
|
||||
|
||||
function testDebugging (t, debugManager) { |
||||
// stack
|
||||
debugManager.traceManager.getStackAt(4, (error, callstack) => { |
||||
if (error) return t.end(error) |
||||
t.equal(JSON.stringify(callstack), JSON.stringify([ '0x0000000000000000000000000000000000000000000000000000000000000000' ])) |
||||
}) |
||||
|
||||
debugManager.traceManager.getStackAt(41, (error, callstack) => { |
||||
if (error) return t.end(error) |
||||
|
||||
/* |
||||
t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', '0x000000000000000000000000000000000000000000000000000000000000002d'])) |
||||
*/ |
||||
}) |
||||
|
||||
// storage
|
||||
debugManager.traceManager.getCurrentCalledAddressAt(38, (error, address) => { |
||||
if (error) return t.end(error) |
||||
var storageView = debugManager.storageViewAt(38, address) |
||||
storageView.storageRange((error, storage) => { |
||||
if (error) return t.end(error) |
||||
t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db' } })) |
||||
}) |
||||
}) |
||||
|
||||
debugManager.extractStateAt(116, (error, state) => { |
||||
if (error) return t.end(error) |
||||
debugManager.decodeStateAt(116, state, (error, decodedState) => { |
||||
if (error) return t.end(error) |
||||
t.equal(decodedState['chairperson'].value, '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB') |
||||
t.equal(decodedState['chairperson'].type, 'address') |
||||
t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0') |
||||
t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256') |
||||
t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal') |
||||
t.equal(decodedState['proposals'].length, '0x1') |
||||
t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]') |
||||
}) |
||||
}) |
||||
|
||||
debugManager.traceManager.getCurrentCalledAddressAt(104, (error, address) => { |
||||
if (error) return t.end(error) |
||||
debugManager.sourceLocationFromVMTraceIndex(address, 104, (error, location) => { |
||||
if (error) return t.end(error) |
||||
debugManager.decodeLocalsAt(104, location, (error, decodedlocals) => { |
||||
if (error) return t.end(error) |
||||
t.equal(JSON.stringify(decodedlocals), JSON.stringify({'p': {'value': '45', 'type': 'uint256'}, 'addressLocal': {'value': '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB', 'type': 'address'}, 'proposalsLocals': {'value': [{'value': {'voteCount': {'value': '0', 'type': 'uint256'}}, 'type': 'struct Ballot.Proposal'}], 'length': '0x1', 'type': 'struct Ballot.Proposal[]'}})) |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
var sourceMappingDecoder = new remixLib.SourceMappingDecoder() |
||||
var breakPointManager = new BreakpointManager(debugManager, (rawLocation) => { |
||||
return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot)) |
||||
}) |
||||
|
||||
breakPointManager.add({fileName: 'test.sol', row: 23}) |
||||
|
||||
breakPointManager.event.register('breakpointHit', function (sourceLocation, step) { |
||||
console.log('breakpointHit') |
||||
t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 587, length: 1, file: 0, jump: '-' })) |
||||
t.equal(step, 74) |
||||
}) |
||||
|
||||
breakPointManager.event.register('noBreakpointHit', function () { |
||||
t.end('noBreakpointHit') |
||||
console.log('noBreakpointHit') |
||||
}) |
||||
breakPointManager.jumpNextBreakpoint(0, true) |
||||
} |
||||
|
||||
var ballot = `pragma solidity ^0.5.0;
|
||||
contract Ballot { |
||||
|
||||
struct Voter { |
||||
uint weight; |
||||
bool voted; |
||||
uint8 vote; |
||||
address delegate; |
||||
} |
||||
struct Proposal { |
||||
uint voteCount; |
||||
} |
||||
|
||||
address chairperson; |
||||
mapping(address => Voter) voters; |
||||
Proposal[] proposals; |
||||
|
||||
/// Create a new ballot with $(_numProposals) different proposals.
|
||||
constructor() public { |
||||
uint p = 45; |
||||
chairperson = msg.sender; |
||||
address addressLocal = msg.sender; // copy of state variable
|
||||
voters[chairperson].weight = 1; |
||||
proposals.length = 1; |
||||
Proposal[] storage proposalsLocals = proposals; // copy of state variable
|
||||
} |
||||
|
||||
/// Give $(toVoter) the right to vote on this ballot.
|
||||
/// May only be called by $(chairperson).
|
||||
function giveRightToVote(address toVoter) public { |
||||
if (msg.sender != chairperson || voters[toVoter].voted) return; |
||||
voters[toVoter].weight = 1; |
||||
} |
||||
|
||||
/// Delegate your vote to the voter $(to).
|
||||
function delegate(address to) public { |
||||
Voter storage sender = voters[msg.sender]; // assigns reference
|
||||
if (sender.voted) return; |
||||
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) |
||||
to = voters[to].delegate; |
||||
if (to == msg.sender) return; |
||||
sender.voted = true; |
||||
sender.delegate = to; |
||||
Voter storage delegateTo = voters[to]; |
||||
if (delegateTo.voted) |
||||
proposals[delegateTo.vote].voteCount += sender.weight; |
||||
else |
||||
delegateTo.weight += sender.weight; |
||||
} |
||||
|
||||
/// Give a single vote to proposal $(toProposal).
|
||||
function vote(uint8 toProposal) public { |
||||
Voter storage sender = voters[msg.sender]; |
||||
if (sender.voted || toProposal >= proposals.length) return; |
||||
sender.voted = true; |
||||
sender.vote = toProposal; |
||||
proposals[toProposal].voteCount += sender.weight; |
||||
} |
||||
|
||||
function winningProposal() public view returns (uint8 _winningProposal) { |
||||
uint256 winningVoteCount = 0; |
||||
for (uint8 prop = 0; prop < proposals.length; prop++) |
||||
if (proposals[prop].voteCount > winningVoteCount) { |
||||
winningVoteCount = proposals[prop].voteCount; |
||||
_winningProposal = prop; |
||||
} |
||||
} |
||||
}` |
||||
// 'use strict'
|
||||
// var tape = require('tape')
|
||||
// var remixLib = require('remix-lib')
|
||||
// var compilerInput = remixLib.helpers.compiler.compilerInput
|
||||
// var vmCall = require('./vmCall')
|
||||
// var Debugger = require('../src/Ethdebugger')
|
||||
// var compiler = require('solc')
|
||||
//
|
||||
// require('./decoder/decodeInfo.js')
|
||||
// require('./decoder/storageLocation.js')
|
||||
// require('./decoder/storageDecoder.js')
|
||||
// require('./decoder/localDecoder.js')
|
||||
//
|
||||
// var BreakpointManager = remixLib.code.BreakpointManager
|
||||
//
|
||||
// tape('debug contract', function (t) {
|
||||
// t.plan(12)
|
||||
// var privateKey = Buffer.from('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex')
|
||||
// var vm = vmCall.initVM(t, privateKey)
|
||||
// var output = compiler.compile(compilerInput(ballot))
|
||||
// output = JSON.parse(output)
|
||||
// var web3VM = new remixLib.vm.Web3VMProvider()
|
||||
// web3VM.setVM(vm)
|
||||
// vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object, (error, txHash) => {
|
||||
// if (error) {
|
||||
// t.end(error)
|
||||
// } else {
|
||||
// web3VM.eth.getTransaction(txHash, (error, tx) => {
|
||||
// if (error) {
|
||||
// t.end(error)
|
||||
// } else {
|
||||
// var debugManager = new Debugger({
|
||||
// compilationResult: function () {
|
||||
// return output
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// debugManager.addProvider('web3vmprovider', web3VM)
|
||||
// debugManager.switchProvider('web3vmprovider')
|
||||
//
|
||||
// debugManager.callTree.event.register('callTreeReady', () => {
|
||||
// testDebugging(t, debugManager)
|
||||
// })
|
||||
//
|
||||
// debugManager.debug(tx)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
//
|
||||
//
|
||||
// function testDebugging (t, debugManager) {
|
||||
// // stack
|
||||
// debugManager.traceManager.getStackAt(4, (error, callstack) => {
|
||||
// if (error) return t.end(error)
|
||||
// t.equal(JSON.stringify(callstack), JSON.stringify([ '0x0000000000000000000000000000000000000000000000000000000000000000' ]))
|
||||
// })
|
||||
//
|
||||
// debugManager.traceManager.getStackAt(41, (error, callstack) => {
|
||||
// if (error) return t.end(error)
|
||||
//
|
||||
// /*
|
||||
// t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', '0x000000000000000000000000000000000000000000000000000000000000002d']))
|
||||
// */
|
||||
// })
|
||||
//
|
||||
// // storage
|
||||
// debugManager.traceManager.getCurrentCalledAddressAt(38, (error, address) => {
|
||||
// if (error) return t.end(error)
|
||||
// var storageView = debugManager.storageViewAt(38, address)
|
||||
// storageView.storageRange((error, storage) => {
|
||||
// if (error) return t.end(error)
|
||||
// t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db' } }))
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// debugManager.extractStateAt(116, (error, state) => {
|
||||
// if (error) return t.end(error)
|
||||
// debugManager.decodeStateAt(116, state, (error, decodedState) => {
|
||||
// if (error) return t.end(error)
|
||||
// t.equal(decodedState['chairperson'].value, '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB')
|
||||
// t.equal(decodedState['chairperson'].type, 'address')
|
||||
// t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0')
|
||||
// t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256')
|
||||
// t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal')
|
||||
// t.equal(decodedState['proposals'].length, '0x1')
|
||||
// t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]')
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// debugManager.traceManager.getCurrentCalledAddressAt(104, (error, address) => {
|
||||
// if (error) return t.end(error)
|
||||
// debugManager.sourceLocationFromVMTraceIndex(address, 104, (error, location) => {
|
||||
// if (error) return t.end(error)
|
||||
// debugManager.decodeLocalsAt(104, location, (error, decodedlocals) => {
|
||||
// if (error) return t.end(error)
|
||||
// t.equal(JSON.stringify(decodedlocals), JSON.stringify({'p': {'value': '45', 'type': 'uint256'}, 'addressLocal': {'value': '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB', 'type': 'address'}, 'proposalsLocals': {'value': [{'value': {'voteCount': {'value': '0', 'type': 'uint256'}}, 'type': 'struct Ballot.Proposal'}], 'length': '0x1', 'type': 'struct Ballot.Proposal[]'}}))
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// var sourceMappingDecoder = new remixLib.SourceMappingDecoder()
|
||||
// var breakPointManager = new BreakpointManager(debugManager, (rawLocation) => {
|
||||
// return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot))
|
||||
// })
|
||||
//
|
||||
// breakPointManager.add({fileName: 'test.sol', row: 23})
|
||||
//
|
||||
// breakPointManager.event.register('breakpointHit', function (sourceLocation, step) {
|
||||
// console.log('breakpointHit')
|
||||
// t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 587, length: 1, file: 0, jump: '-' }))
|
||||
// t.equal(step, 74)
|
||||
// })
|
||||
//
|
||||
// breakPointManager.event.register('noBreakpointHit', function () {
|
||||
// t.end('noBreakpointHit')
|
||||
// console.log('noBreakpointHit')
|
||||
// })
|
||||
// breakPointManager.jumpNextBreakpoint(0, true)
|
||||
// }
|
||||
//
|
||||
// var ballot = `pragma solidity ^0.5.0;
|
||||
// contract Ballot {
|
||||
//
|
||||
// struct Voter {
|
||||
// uint weight;
|
||||
// bool voted;
|
||||
// uint8 vote;
|
||||
// address delegate;
|
||||
// }
|
||||
// struct Proposal {
|
||||
// uint voteCount;
|
||||
// }
|
||||
//
|
||||
// address chairperson;
|
||||
// mapping(address => Voter) voters;
|
||||
// Proposal[] proposals;
|
||||
//
|
||||
// /// Create a new ballot with $(_numProposals) different proposals.
|
||||
// constructor() public {
|
||||
// uint p = 45;
|
||||
// chairperson = msg.sender;
|
||||
// address addressLocal = msg.sender; // copy of state variable
|
||||
// voters[chairperson].weight = 1;
|
||||
// proposals.length = 1;
|
||||
// Proposal[] storage proposalsLocals = proposals; // copy of state variable
|
||||
// }
|
||||
//
|
||||
// /// Give $(toVoter) the right to vote on this ballot.
|
||||
// /// May only be called by $(chairperson).
|
||||
// function giveRightToVote(address toVoter) public {
|
||||
// if (msg.sender != chairperson || voters[toVoter].voted) return;
|
||||
// voters[toVoter].weight = 1;
|
||||
// }
|
||||
//
|
||||
// /// Delegate your vote to the voter $(to).
|
||||
// function delegate(address to) public {
|
||||
// Voter storage sender = voters[msg.sender]; // assigns reference
|
||||
// if (sender.voted) return;
|
||||
// while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
|
||||
// to = voters[to].delegate;
|
||||
// if (to == msg.sender) return;
|
||||
// sender.voted = true;
|
||||
// sender.delegate = to;
|
||||
// Voter storage delegateTo = voters[to];
|
||||
// if (delegateTo.voted)
|
||||
// proposals[delegateTo.vote].voteCount += sender.weight;
|
||||
// else
|
||||
// delegateTo.weight += sender.weight;
|
||||
// }
|
||||
//
|
||||
// /// Give a single vote to proposal $(toProposal).
|
||||
// function vote(uint8 toProposal) public {
|
||||
// Voter storage sender = voters[msg.sender];
|
||||
// if (sender.voted || toProposal >= proposals.length) return;
|
||||
// sender.voted = true;
|
||||
// sender.vote = toProposal;
|
||||
// proposals[toProposal].voteCount += sender.weight;
|
||||
// }
|
||||
//
|
||||
// function winningProposal() public view returns (uint8 _winningProposal) {
|
||||
// uint256 winningVoteCount = 0;
|
||||
// for (uint8 prop = 0; prop < proposals.length; prop++)
|
||||
// if (proposals[prop].voteCount > winningVoteCount) {
|
||||
// winningVoteCount = proposals[prop].voteCount;
|
||||
// _winningProposal = prop;
|
||||
// }
|
||||
// }
|
||||
// }`
|
||||
|
Loading…
Reference in new issue