Merge pull request #1543 from ethereum/refactor_debugger

Refactor debugger - extract logic from views, move to separate module
pull/3094/head
yann300 6 years ago committed by GitHub
commit e9ef5a3ad0
  1. 25
      .circleci/config.yml
  2. 2
      src/app.js
  3. 133
      src/app/debugger/debugger.js
  4. 240
      src/app/debugger/debugger/VmDebugger.js
  5. 144
      src/app/debugger/debugger/debugger.js
  6. 65
      src/app/debugger/debugger/solidityLocals.js
  7. 81
      src/app/debugger/debugger/solidityState.js
  8. 145
      src/app/debugger/debugger/stepManager.js
  9. 161
      src/app/debugger/debuggerUI.js
  10. 95
      src/app/debugger/debuggerUI/ButtonNavigator.js
  11. 58
      src/app/debugger/debuggerUI/Slider.js
  12. 59
      src/app/debugger/debuggerUI/StepManager.js
  13. 122
      src/app/debugger/debuggerUI/TxBrowser.js
  14. 163
      src/app/debugger/debuggerUI/VmDebugger.js
  15. 0
      src/app/debugger/debuggerUI/styles/basicStyles.js
  16. 0
      src/app/debugger/debuggerUI/styles/dropdownPanel.js
  17. 0
      src/app/debugger/debuggerUI/styles/sliderStyles.js
  18. 0
      src/app/debugger/debuggerUI/styles/treeView.js
  19. 17
      src/app/debugger/debuggerUI/vmDebugger/CalldataPanel.js
  20. 18
      src/app/debugger/debuggerUI/vmDebugger/CallstackPanel.js
  21. 29
      src/app/debugger/debuggerUI/vmDebugger/CodeListView.js
  22. 28
      src/app/debugger/debuggerUI/vmDebugger/DropdownPanel.js
  23. 21
      src/app/debugger/debuggerUI/vmDebugger/FullStoragesChanges.js
  24. 21
      src/app/debugger/debuggerUI/vmDebugger/MemoryPanel.js
  25. 38
      src/app/debugger/debuggerUI/vmDebugger/SolidityLocals.js
  26. 35
      src/app/debugger/debuggerUI/vmDebugger/SolidityState.js
  27. 17
      src/app/debugger/debuggerUI/vmDebugger/StackPanel.js
  28. 23
      src/app/debugger/debuggerUI/vmDebugger/StepDetail.js
  29. 17
      src/app/debugger/debuggerUI/vmDebugger/StoragePanel.js
  30. 2
      src/app/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js
  31. 1
      src/app/debugger/remix-debugger/index.js
  32. 33
      src/app/debugger/remix-debugger/src/ui/CalldataPanel.js
  33. 33
      src/app/debugger/remix-debugger/src/ui/CallstackPanel.js
  34. 210
      src/app/debugger/remix-debugger/src/ui/Ethdebugger.js
  35. 154
      src/app/debugger/remix-debugger/src/ui/EthdebuggerUI.js
  36. 72
      src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js
  37. 39
      src/app/debugger/remix-debugger/src/ui/MemoryPanel.js
  38. 75
      src/app/debugger/remix-debugger/src/ui/Slider.js
  39. 83
      src/app/debugger/remix-debugger/src/ui/SolidityLocals.js
  40. 103
      src/app/debugger/remix-debugger/src/ui/SolidityState.js
  41. 33
      src/app/debugger/remix-debugger/src/ui/StackPanel.js
  42. 101
      src/app/debugger/remix-debugger/src/ui/StepDetail.js
  43. 205
      src/app/debugger/remix-debugger/src/ui/StepManager.js
  44. 50
      src/app/debugger/remix-debugger/src/ui/StoragePanel.js
  45. 206
      src/app/debugger/remix-debugger/src/ui/TxBrowser.js
  46. 126
      src/app/debugger/remix-debugger/src/ui/VmDebugger.js
  47. 40
      src/app/tabs/debugger-tab.js
  48. 2
      src/lib/cmdInterpreterAPI.js
  49. 4
      test-browser/tests/ballot.js

@ -26,38 +26,17 @@ jobs:
- checkout - checkout
- restore_cache: - restore_cache:
keys: keys:
- dep-bundle-22-{{ checksum "package.json" }} - dep-bundle-23-{{ checksum "package.json" }}
- run: npm install - run: npm install
- save_cache: - save_cache:
key: dep-bundle-22-{{ checksum "package.json" }} key: dep-bundle-23-{{ checksum "package.json" }}
paths: paths:
- ~/repo/node_modules - ~/repo/node_modules
- run: npm run lint && npm run test && npm run make-mock-compiler && npm run build - run: npm run lint && npm run test && npm run make-mock-compiler && npm run build
- run: ./ci/browser_tests.sh - run: ./ci/browser_tests.sh
remix-debugger:
docker:
# specify the version you desire here
- image: circleci/node:9.11.2
working_directory: ~/repo
steps:
- checkout
- restore_cache:
keys:
- dep-bundle-17-{{ checksum "package.json" }}
- run: npm install
- save_cache:
key: dep-bundle-17-{{ checksum "package.json" }}
paths:
- ~/repo/node_modules
- run: npm run build_debugger
- run: ./ci/browser_tests_debugger.sh
workflows: workflows:
version: 2 version: 2
build_all: build_all:
jobs: jobs:
- remix-ide - remix-ide
- remix-debugger

@ -230,7 +230,7 @@ class App {
} }
runCompiler () { runCompiler () {
const self = this const self = this
if (self._components.righthandpanel.debugger().isActive) return if (self._components.righthandpanel.debugger().isDebuggerActive()) return
self._components.fileManager.saveCurrentFile() self._components.fileManager.saveCurrentFile()
self._components.editorpanel.getEditor().clearAnnotations() self._components.editorpanel.getEditor().clearAnnotations()

@ -1,133 +0,0 @@
'use strict'
var EthdebuggerUI = require('./remix-debugger/src/ui/EthdebuggerUI')
var Ethdebugger = require('remix-debug').EthDebugger
var remixLib = require('remix-lib')
var executionContext = require('../../execution-context')
var globlalRegistry = require('../../global/registry')
/**
* Manage remix and source highlighting
*/
function Debugger (container, sourceHighlighter, localRegistry) {
this._components = {
sourceHighlighter: sourceHighlighter
}
this._components.registry = localRegistry || globlalRegistry
// dependencies
this._deps = {
offsetToLineColumnConverter: this._components.registry.get('offsettolinecolumnconverter').api,
editor: this._components.registry.get('editor').api,
compiler: this._components.registry.get('compiler').api,
compilersArtefacts: this._components.registry.get('compilersartefacts').api
}
this.debugger = new Ethdebugger(
{
executionContext: executionContext,
compilationResult: () => {
if (this._deps.compilersArtefacts['__last']) return this._deps.compilersArtefacts['__last'].getData()
return null
}
})
this.debugger_ui = new EthdebuggerUI({debugger: this.debugger})
this.sourceMappingDecoder = new remixLib.SourceMappingDecoder()
//
// TODO: render doesn't exist anymore
container.appendChild(this.debugger_ui.render())
//
this.isActive = false
this.breakPointManager = new remixLib.code.BreakpointManager(this.debugger, (sourceLocation) => {
return self._deps.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, this._deps.compiler.lastCompilationResult.source.sources, this._deps.compiler.lastCompilationResult.data.sources)
}, (step) => {
this.debugger_ui.stepManager.jumpTo(step)
})
this.debugger.setBreakpointManager(this.breakPointManager)
var self = this
self._deps.editor.event.register('breakpointCleared', (fileName, row) => {
this.breakPointManager.remove({fileName: fileName, row: row})
})
self._deps.editor.event.register('breakpointAdded', (fileName, row) => {
this.breakPointManager.add({fileName: fileName, row: row})
})
executionContext.event.register('contextChanged', this, function (context) {
self.switchProvider(context)
})
this.debugger.event.register('newTraceLoaded', this, function () {
self.isActive = true
})
this.debugger.event.register('traceUnloaded', this, function () {
self._components.sourceHighlighter.currentSourceLocation(null)
self.isActive = false
})
// unload if a file has changed (but not if tabs were switched)
self._deps.editor.event.register('contentChanged', function () {
self.debugger.unLoad()
})
// register selected code item, highlight the corresponding source location
this.debugger_ui.event.register('indexChanged', function (index) {
if (self._deps.compiler.lastCompilationResult) {
self.debugger.traceManager.getCurrentCalledAddressAt(index, (error, address) => {
if (error) return console.log(error)
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, index, self._deps.compiler.lastCompilationResult.data.contracts, function (error, rawLocation) {
if (!error && self._deps.compiler.lastCompilationResult && self._deps.compiler.lastCompilationResult.data) {
var lineColumnPos = self._deps.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, self._deps.compiler.lastCompilationResult.source.sources, self._deps.compiler.lastCompilationResult.data.sources)
self._components.sourceHighlighter.currentSourceLocation(lineColumnPos, rawLocation)
} else {
self._components.sourceHighlighter.currentSourceLocation(null)
}
})
})
}
})
}
/**
* Start debugging using Remix
*
* @param {String} txHash - hash of the transaction
*/
Debugger.prototype.debug = function (txHash) {
var self = this
this.debugger.web3.eth.getTransaction(txHash, function (error, tx) {
if (!error) {
self.debugger_ui.debug(tx)
}
})
}
/**
* add a new web3 provider to remix
*
* @param {String} type - type/name of the provider to add
* @param {Object} obj - provider
*/
Debugger.prototype.addProvider = function (type, obj) {
this.debugger.addProvider(type, obj)
}
/**
* switch the provider
*
* @param {String} type - type/name of the provider to use
*/
Debugger.prototype.switchProvider = function (type) {
this.debugger.switchProvider(type)
}
/**
* get the current provider
*/
Debugger.prototype.web3 = function (type) {
return this.debugger.web3
}
module.exports = Debugger

@ -0,0 +1,240 @@
var remixDebug = require('remix-debug')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var ui = remixLib.helpers.ui
var StorageResolver = remixDebug.storage.StorageResolver
var StorageViewer = remixDebug.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.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.stepManager.currentStepIndex !== index) return
if (!self.storageResolver) return
// Full storage changes are queried for each step (not only at the end of the trace as it was before)
let storageJSON = {}
for (var k in self.addresses) {
let address = self.addresses[k]
let storage = {}
self._traceManager.accumulateStorageChanges(self.stepManager.currentStepIndex, address, storage, (error, result) => {
if (!error) {
storageJSON[address] = result
self.event.trigger('traceStorageUpdate', [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,144 @@
'use strict'
var Ethdebugger = require('remix-debug').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.executionContext = options.executionContext
// dependencies
this.offsetToLineColumnConverter = options.offsetToLineColumnConverter
this.compilersArtefacts = options.compilersArtefacts
this.debugger = new Ethdebugger({
executionContext: options.executionContext,
compilationResult: () => {
if (this.compilersArtefacts['__last']) return this.compilersArtefacts['__last'].getData()
return null
}
})
this.breakPointManager = new remixLib.code.BreakpointManager(this.debugger, (sourceLocation) => {
if (!this.compilersArtefacts['__last']) return null
let compilationData = this.compilersArtefacts['__last'].getData()
if (!compilationData) return null
return self.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, compilationData.sources, compilationData.sources)
}, (step) => {
self.event.trigger('breakpointStep', [step])
})
this.debugger.setBreakpointManager(this.breakPointManager)
this.executionContext.event.register('contextChanged', this, function (context) {
self.debugger.switchProvider(context)
})
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)
})
this.debugger.addProvider('vm', this.executionContext.vm())
this.debugger.addProvider('injected', this.executionContext.internalWeb3())
this.debugger.addProvider('web3', this.executionContext.internalWeb3())
this.debugger.switchProvider(this.executionContext.getProvider())
}
Debugger.prototype.registerAndHighlightCodeItem = function (index) {
const self = this
// register selected code item, highlight the corresponding source location
if (!self.compilersArtefacts['__last']) {
self.event.trigger('newSourceLocation', [null])
return
}
var compilerData = self.compilersArtefacts['__last'].getData()
self.debugger.traceManager.getCurrentCalledAddressAt(index, (error, address) => {
if (error) return console.log(error)
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, index, compilerData.contracts, function (error, rawLocation) {
if (!error) {
var lineColumnPos = self.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, compilerData.sources, compilerData.sources)
self.event.trigger('newSourceLocation', [lineColumnPos, rawLocation])
} else {
self.event.trigger('newSourceLocation', [null])
}
})
})
}
Debugger.prototype.debug = function (blockNumber, txNumber, tx, loadingCb) {
const self = this
let web3 = this.executionContext.web3()
if (this.debugger.traceManager.isLoading) {
return
}
self.debugger.solidityProxy.reset({})
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,65 @@
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var remixDebug = require('remix-debug')
var localDecoder = remixDebug.SolidityDecoder.localDecoder
var StorageViewer = remixDebug.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,81 @@
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var remixDebug = require('remix-debug')
var stateDecoder = remixDebug.SolidityDecoder.stateDecoder
var StorageViewer = remixDebug.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

@ -0,0 +1,161 @@
var TxBrowser = require('./debuggerUI/TxBrowser')
var StepManagerUI = require('./debuggerUI/StepManager')
var VmDebugger = require('./debuggerUI/VmDebugger')
var Debugger = require('./debugger/debugger')
var SourceHighlighter = require('../editor/sourceHighlighter')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var executionContext = require('../../execution-context')
var globalRegistry = require('../../global/registry')
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var css = csjs`
.statusMessage {
margin-left: 15px;
}
.innerShift {
padding: 2px;
margin-left: 10px;
}
`
class DebuggerUI {
constructor (container) {
this.registry = globalRegistry
this.event = new EventManager()
this.debugger = new Debugger({
executionContext: executionContext,
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api,
compiler: this.registry.get('compiler').api,
compilersArtefacts: this.registry.get('compilersartefacts').api
})
this.isActive = false
this.sourceHighlighter = new SourceHighlighter()
this.startTxBrowser()
this.stepManager = null
this.statusMessage = ''
this.view
container.appendChild(this.render())
this.setEditor()
this.listenToEvents()
}
setEditor () {
const self = this
this.editor = this.registry.get('editor').api
self.editor.event.register('breakpointCleared', (fileName, row) => {
self.debugger.breakPointManager.remove({fileName: fileName, row: row})
})
self.editor.event.register('breakpointAdded', (fileName, row) => {
self.debugger.breakPointManager.add({fileName: fileName, row: row})
})
self.editor.event.register('contentChanged', function () {
self.debugger.unload()
})
}
listenToEvents () {
const self = this
this.debugger.event.register('debuggerStatus', function (isActive) {
self.sourceHighlighter.currentSourceLocation(null)
self.isActive = isActive
})
this.debugger.event.register('newSourceLocation', function (lineColumnPos, rawLocation) {
self.sourceHighlighter.currentSourceLocation(lineColumnPos, rawLocation)
})
this.debugger.event.register('debuggerUnloaded', self.unLoad.bind(this))
}
startTxBrowser () {
const self = this
let txBrowser = new TxBrowser()
this.txBrowser = txBrowser
txBrowser.event.register('requestDebug', function (blockNumber, txNumber, tx) {
self.debugger.unload()
self.startDebugging(blockNumber, txNumber, tx)
})
txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) {
self.debugger.unload()
})
}
isDebuggerActive () {
return this.isActive
}
startDebugging (blockNumber, txNumber, tx) {
const self = this
this.debugger.debug(blockNumber, txNumber, tx, () => {
self.stepManager = new StepManagerUI(this.debugger.step_manager)
self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic)
self.renderDebugger()
})
}
debug (txHash) {
this.startDebugging(null, txHash, null)
}
render () {
this.debuggerPanelsView = yo`<div class="${css.innerShift}"></div>`
this.debuggerHeadPanelsView = yo`<div class="${css.innerShift}"></div>`
this.stepManagerView = yo`<div class="${css.innerShift}"></div>`
var view = yo`<div>
<div class="${css.innerShift}">
${this.txBrowser.render()}
${this.debuggerHeadPanelsView}
${this.stepManagerView}
</div>
<div class="${css.statusMessage}" >${this.statusMessage}</div>
${this.debuggerPanelsView}
</div>`
if (!this.view) {
this.view = view
}
return view
}
unLoad () {
yo.update(this.debuggerHeadPanelsView, yo`<div></div>`)
yo.update(this.debuggerPanelsView, yo`<div></div>`)
yo.update(this.stepManagerView, yo`<div></div>`)
if (this.vmDebugger) this.vmDebugger.remove()
if (this.stepManager) this.stepManager.remove()
this.vmDebugger = null
this.stepManager = null
this.event.trigger('traceUnloaded')
}
renderDebugger () {
yo.update(this.debuggerHeadPanelsView, this.vmDebugger.renderHead())
yo.update(this.debuggerPanelsView, this.vmDebugger.render())
yo.update(this.stepManagerView, this.stepManager.render())
}
}
module.exports = DebuggerUI

@ -4,7 +4,7 @@ var EventManager = remixLib.EventManager
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var styleGuide = require('../../../../ui/styles-guide/theme-chooser') var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser() var styles = styleGuide.chooser()
var css = csjs` var css = csjs`
@ -36,7 +36,7 @@ var css = csjs`
} }
` `
function ButtonNavigator (_parent, _traceManager) { function ButtonNavigator () {
this.event = new EventManager() this.event = new EventManager()
this.intoBackDisabled = true this.intoBackDisabled = true
this.overBackDisabled = true this.overBackDisabled = true
@ -46,50 +46,9 @@ function ButtonNavigator (_parent, _traceManager) {
this.jumpNextBreakpointDisabled = true this.jumpNextBreakpointDisabled = true
this.jumpPreviousBreakpointDisabled = true this.jumpPreviousBreakpointDisabled = true
this.traceManager = _traceManager
this.currentCall = null
this.revertionPoint = null
_parent.event.register('indexChanged', this, (index) => {
if (!this.view) return
if (index < 0) return
if (_parent.currentStepIndex !== index) return
this.traceManager.buildCallPath(index, (error, callsPath) => {
if (error) {
console.log(error)
resetWarning(this)
} else {
this.currentCall = callsPath[callsPath.length - 1]
if (this.currentCall.reverted) {
this.revertionPoint = this.currentCall.return
this.view.querySelector('#reverted').style.display = 'block'
this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'inline' : 'none'
this.view.querySelector('#reverted #parenthasthrown').style.display = 'none'
} else {
var k = callsPath.length - 2
while (k >= 0) {
var parent = callsPath[k]
if (parent.reverted) {
this.revertionPoint = parent.return
this.view.querySelector('#reverted').style.display = 'block'
this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'inline' : 'none'
this.view.querySelector('#reverted #outofgas').style.display = 'none'
return
}
k--
}
resetWarning(this)
}
}
})
})
this.view this.view
} }
module.exports = ButtonNavigator
ButtonNavigator.prototype.render = function () { ButtonNavigator.prototype.render = function () {
var self = this var self = this
var view = yo`<div class="${css.buttons}"> var view = yo`<div class="${css.buttons}">
@ -106,7 +65,7 @@ ButtonNavigator.prototype.render = function () {
<button id='jumpnextbreakpoint' title='Jump to the next breakpoint' class='${css.navigator} ${css.jumpButton} fa fa-step-forward' onclick=${function () { self.event.trigger('jumpNextBreakpoint') }} disabled=${this.jumpNextBreakpointDisabled} ></button> <button id='jumpnextbreakpoint' title='Jump to the next breakpoint' class='${css.navigator} ${css.jumpButton} fa fa-step-forward' onclick=${function () { self.event.trigger('jumpNextBreakpoint') }} disabled=${this.jumpNextBreakpointDisabled} ></button>
</div> </div>
<div id='reverted' style="display:none"> <div id='reverted' style="display:none">
<button id='jumptoexception' title='Jump to exception' class='${css.navigator} ${css.button} fa fa-exclamation-triangle' onclick=${function () { self.event.trigger('jumpToException', [self.revertionPoint]) }} disabled=${this.jumpOutDisabled} > <button id='jumptoexception' title='Jump to exception' class='${css.navigator} ${css.button} fa fa-exclamation-triangle' onclick=${function () { self.event.trigger('jumpToException') }} disabled=${this.jumpOutDisabled} >
</button> </button>
<span>State changes made during this call will be reverted.</span> <span>State changes made during this call will be reverted.</span>
<span id='outofgas' style="display:none">This call will run out of gas.</span> <span id='outofgas' style="display:none">This call will run out of gas.</span>
@ -127,32 +86,25 @@ ButtonNavigator.prototype.reset = function () {
this.jumpOutDisabled = true this.jumpOutDisabled = true
this.jumpNextBreakpointDisabled = true this.jumpNextBreakpointDisabled = true
this.jumpPreviousBreakpointDisabled = true this.jumpPreviousBreakpointDisabled = true
resetWarning(this) this.resetWarning('')
} }
ButtonNavigator.prototype.stepChanged = function (step) { ButtonNavigator.prototype.stepChanged = function (stepState, jumpOutDisabled) {
this.intoBackDisabled = step <= 0 if (stepState === 'invalid') {
this.overBackDisabled = step <= 0 // TODO: probably not necessary, already implicit done in the next steps
if (!this.traceManager) { this.reset()
this.intoForwardDisabled = true this.updateAll()
this.overForwardDisabled = true return
} else {
var self = this
this.traceManager.getLength(function (error, length) {
if (error) {
self.reset()
console.log(error)
} else {
self.jumpNextBreakpointDisabled = step >= length - 1
self.jumpPreviousBreakpointDisabled = step <= 0
self.intoForwardDisabled = step >= length - 1
self.overForwardDisabled = step >= length - 1
var stepOut = self.traceManager.findStepOut(step)
self.jumpOutDisabled = stepOut === step
}
self.updateAll()
})
} }
this.intoBackDisabled = (stepState === 'initial')
this.overBackDisabled = (stepState === 'initial')
this.jumpPreviousBreakpointDisabled = (stepState === 'initial')
this.jumpNextBreakpointDisabled = (stepState === 'end')
this.intoForwardDisabled = (stepState === 'end')
this.overForwardDisabled = (stepState === 'end')
this.jumpNextBreakpointDisabled = jumpOutDisabled
this.updateAll() this.updateAll()
} }
@ -175,10 +127,11 @@ ButtonNavigator.prototype.updateDisabled = function (id, disabled) {
} }
} }
function resetWarning (self) { ButtonNavigator.prototype.resetWarning = function (revertedReason) {
self.view.querySelector('#reverted #outofgas').style.display = 'none' if (!this.view) return
self.view.querySelector('#reverted #parenthasthrown').style.display = 'none' this.view.querySelector('#reverted #outofgas').style.display = (revertedReason === 'outofgas') ? 'inline' : 'none'
self.view.querySelector('#reverted').style.display = 'none' this.view.querySelector('#reverted #parenthasthrown').style.display = (revertedReason === 'parenthasthrown') ? 'inline' : 'none'
this.view.querySelector('#reverted').style.display = (revertedReason === '') ? 'none' : 'block'
} }
module.exports = ButtonNavigator module.exports = ButtonNavigator

@ -0,0 +1,58 @@
'use strict'
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var yo = require('yo-yo')
class Slider {
constructor () {
this.event = new EventManager()
this.max
this.disabled = true
this.view
this.previousValue = null
}
setSliderLength (length) {
if (!this.view) return
this.view.querySelector('#slider').setAttribute('max', length - 1)
this.max = length - 1
this.disabled = (length === 0)
if (this.disabled) {
this.view.querySelector('#slider').setAttribute('disabled', true)
} else {
this.view.querySelector('#slider').removeAttribute('disabled')
}
this.setValue(0)
}
onChange (event) {
if (!this.view) return
var value = parseInt(this.view.querySelector('#slider').value)
if (value === this.previousValue) return
this.previousValue = value
this.event.trigger('sliderMoved', [value])
}
setValue (value) {
if (!this.view) return
this.view.querySelector('#slider').value = value
}
render () {
var self = this
var view = yo`<div>
<input id='slider' style='width: 100%' type='range' min=0 max=${this.max} value=0
onchange=${function () { self.onChange() }} oninput=${function () { self.onChange() }} disabled=${this.disabled} />
</div>`
if (!this.view) {
this.view = view
}
return view
}
}
module.exports = Slider

@ -0,0 +1,59 @@
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var yo = require('yo-yo')
var ButtonNavigator = require('./ButtonNavigator')
var Slider = require('./Slider')
function StepManager (stepManager) {
this.event = new EventManager()
this.stepManager = stepManager
this.startSlider()
this.startButtonNavigator()
this.stepManager.event.register('stepChanged', this.updateStep.bind(this))
}
StepManager.prototype.startSlider = function () {
this.slider = new Slider()
this.slider.event.register('sliderMoved', this.stepManager.jumpTo.bind(this.stepManager))
this.stepManager.event.register('traceLengthChanged', this.slider.setSliderLength.bind(this.slider))
}
StepManager.prototype.startButtonNavigator = function () {
this.buttonNavigator = new ButtonNavigator()
this.stepManager.event.register('revertWarning', this.buttonNavigator.resetWarning.bind(this.buttonNavigator))
this.buttonNavigator.event.register('stepIntoBack', this.stepManager.stepIntoBack.bind(this.stepManager))
this.buttonNavigator.event.register('stepIntoForward', this.stepManager.stepIntoForward.bind(this.stepManager))
this.buttonNavigator.event.register('stepOverBack', this.stepManager.stepOverBack.bind(this.stepManager))
this.buttonNavigator.event.register('stepOverForward', this.stepManager.stepOverForward.bind(this.stepManager))
this.buttonNavigator.event.register('jumpOut', this.stepManager.jumpOut.bind(this.stepManager))
this.buttonNavigator.event.register('jumpToException', this.stepManager.jumpToException.bind(this.stepManager))
this.buttonNavigator.event.register('jumpNextBreakpoint', this.stepManager.jumpNextBreakpoint.bind(this.stepManager))
this.buttonNavigator.event.register('jumpPreviousBreakpoint', this.stepManager.jumpPreviousBreakpoint.bind(this.stepManager))
}
StepManager.prototype.updateStep = function (step, stepState, jumpOutDisabled) {
if (!this.slider) return
this.slider.setValue(step)
this.buttonNavigator.stepChanged(stepState, jumpOutDisabled)
this.event.trigger('stepChanged', [step])
}
StepManager.prototype.remove = function () {
// used to stop listenning on event. bad and should be "refactored"
this.slider.view = null
this.slider = null
this.buttonNavigator.view = null
this.buttonNavigator = null
}
StepManager.prototype.render = function () {
return yo`<div>
${this.slider.render()}
${this.buttonNavigator.render()}
</div>`
}
module.exports = StepManager

@ -0,0 +1,122 @@
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.container {
display: flex;
flex-direction: column;
}
.txContainer {
display: flex;
flex-direction: column;
}
.txinputs {
width: 100%;
display: flex;
justify-content: center;
}
.txinput {
${styles.rightPanel.debuggerTab.input_Debugger}
margin: 3px;
width: inherit;
}
.txbuttons {
width: 100%;
display: flex;
justify-content: center;
}
.txbutton {
${styles.rightPanel.debuggerTab.button_Debugger}
width: inherit;
}
.txbuttonstart {
${styles.rightPanel.debuggerTab.button_Debugger}
}
.txbutton:hover {
color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
}
.vmargin {
margin-top: 10px;
margin-bottom: 10px;
}
`
function TxBrowser () {
this.event = new EventManager()
this.blockNumber
this.txNumber
this.view
this.setDefaultValues()
}
TxBrowser.prototype.setDefaultValues = function () {
this.connectInfo = ''
if (this.view) {
yo.update(this.view, this.render())
}
}
TxBrowser.prototype.submit = function (tx) {
this.event.trigger('requestDebug', [this.blockNumber, this.txNumber, tx])
}
TxBrowser.prototype.update = function (error, tx) {
if (error) {
this.view.querySelector('#error').innerHTML = error
return
}
if (!tx) {
this.view.querySelector('#error').innerHTML = 'Cannot find transaction with reference. Block number: ' + this.blockNumber + '. Transaction index/hash: ' + this.txNumber
return
}
this.view.querySelector('#error').innerHTML = ''
}
TxBrowser.prototype.updateBlockN = function (ev) {
this.blockNumber = ev.target.value
}
TxBrowser.prototype.updateTxN = function (ev) {
this.txNumber = ev.target.value
}
TxBrowser.prototype.load = function (txHash, tx) {
this.txNumber = txHash
}
TxBrowser.prototype.unload = function (txHash) {
this.event.trigger('unloadRequested')
this.setDefaultValues()
}
TxBrowser.prototype.render = function () {
var self = this
var view = yo`<div class="${css.container}">
<div class="${css.txContainer}">
<div class="${css.txinputs}">
<input class="${css.txinput}" onkeyup=${function () { self.updateBlockN(arguments[0]) }} type='text' placeholder=${'Block number'} />
<input class="${css.txinput}" id='txinput' onkeyup=${function () { self.updateTxN(arguments[0]) }} type='text' placeholder=${'Transaction index or hash'} />
</div>
<div class="${css.txbuttons}">
<button id='load' class='${css.txbutton}' title='start debugging' onclick=${function () { self.submit() }}>Start debugging</button>
<button id='unload' class='${css.txbutton}' title='stop debugging' onclick=${function () { self.unload() }}>Stop</button>
</div>
</div>
<span id='error'></span>
</div>`
if (!this.view) {
this.view = view
}
return view
}
module.exports = TxBrowser

@ -0,0 +1,163 @@
'use strict'
var csjs = require('csjs-inject')
var yo = require('yo-yo')
var CodeListView = require('./vmDebugger/CodeListView')
var CalldataPanel = require('./vmDebugger/CalldataPanel')
var MemoryPanel = require('./vmDebugger/MemoryPanel')
var CallstackPanel = require('./vmDebugger/CallstackPanel')
var StackPanel = require('./vmDebugger/StackPanel')
var StoragePanel = require('./vmDebugger/StoragePanel')
var StepDetail = require('./vmDebugger/StepDetail')
var SolidityState = require('./vmDebugger/SolidityState')
var SolidityLocals = require('./vmDebugger/SolidityLocals')
var FullStoragesChangesPanel = require('./vmDebugger/FullStoragesChanges')
var DropdownPanel = require('./vmDebugger/DropdownPanel')
var css = csjs`
.asmCode {
float: left;
width: 50%;
}
.stepDetail {
}
.vmheadView {
margin-top:10px;
}
`
function VmDebugger (vmDebuggerLogic) {
var self = this
this.view
this.vmDebuggerLogic = vmDebuggerLogic
this.asmCode = new CodeListView()
this.vmDebuggerLogic.event.register('codeManagerChanged', this.asmCode.changed.bind(this.asmCode))
this.vmDebuggerLogic.event.register('traceUnloaded', this.asmCode.reset.bind(this.asmCode))
this.calldataPanel = new CalldataPanel()
this.vmDebuggerLogic.event.register('traceManagerCallDataUpdate', this.calldataPanel.update.bind(this.calldataPanel))
this.memoryPanel = new MemoryPanel()
this.vmDebuggerLogic.event.register('traceManagerMemoryUpdate', this.memoryPanel.update.bind(this.memoryPanel))
this.callstackPanel = new CallstackPanel()
this.vmDebuggerLogic.event.register('traceManagerCallStackUpdate', this.callstackPanel.update.bind(this.callstackPanel))
this.stackPanel = new StackPanel()
this.vmDebuggerLogic.event.register('traceManagerStackUpdate', this.stackPanel.update.bind(this.stackPanel))
this.storagePanel = new StoragePanel()
this.vmDebuggerLogic.event.register('traceManagerStorageUpdate', this.storagePanel.update.bind(this.storagePanel))
this.stepDetail = new StepDetail()
this.vmDebuggerLogic.event.register('traceUnloaded', this.stepDetail.reset.bind(this.stepDetail))
this.vmDebuggerLogic.event.register('newTraceLoaded', this.stepDetail.reset.bind(this.stepDetail))
this.vmDebuggerLogic.event.register('traceCurrentStepUpdate', function (error, step) {
self.stepDetail.updateField('execution step', (error ? '-' : step))
})
this.vmDebuggerLogic.event.register('traceMemExpandUpdate', function (error, addmem) {
self.stepDetail.updateField('add memory', (error ? '-' : addmem))
})
this.vmDebuggerLogic.event.register('traceStepCostUpdate', function (error, gas) {
self.stepDetail.updateField('gas', (error ? '-' : gas))
})
this.vmDebuggerLogic.event.register('traceCurrentCalledAddressAtUpdate', function (error, address) {
self.stepDetail.updateField('loaded address', (error ? '-' : address))
})
this.vmDebuggerLogic.event.register('traceRemainingGasUpdate', function (error, remainingGas) {
self.stepDetail.updateField('remaining gas', (error ? '-' : remainingGas))
})
this.vmDebuggerLogic.event.register('indexUpdate', function (index) {
self.stepDetail.updateField('vm trace step', index)
})
this.solidityState = new SolidityState()
this.vmDebuggerLogic.event.register('solidityState', this.solidityState.update.bind(this.solidityState))
this.vmDebuggerLogic.event.register('solidityStateMessage', this.solidityState.setMessage.bind(this.solidityState))
this.vmDebuggerLogic.event.register('solidityStateUpdating', this.solidityState.setUpdating.bind(this.solidityState))
this.solidityLocals = new SolidityLocals()
this.vmDebuggerLogic.event.register('solidityLocals', this.solidityLocals.update.bind(this.solidityLocals))
this.vmDebuggerLogic.event.register('solidityLocalsMessage', this.solidityLocals.setMessage.bind(this.solidityLocals))
this.vmDebuggerLogic.event.register('solidityLocalsUpdating', this.solidityLocals.setUpdating.bind(this.solidityLocals))
this.returnValuesPanel = new DropdownPanel('Return Value', {json: true})
this.returnValuesPanel.data = {}
this.vmDebuggerLogic.event.register('traceReturnValueUpdate', this.returnValuesPanel.update.bind(this.returnValuesPanel))
this.fullStoragesChangesPanel = new FullStoragesChangesPanel()
this.addresses = []
this.vmDebuggerLogic.event.register('traceAddressesUpdate', function (_addresses) {
self.fullStoragesChangesPanel.update({})
})
this.vmDebuggerLogic.event.register('traceStorageUpdate', this.fullStoragesChangesPanel.update.bind(this.fullStoragesChangesPanel))
this.vmDebuggerLogic.event.register('newTrace', () => {
if (!self.view) return
self.asmCode.basicPanel.show()
self.stackPanel.basicPanel.show()
self.storagePanel.basicPanel.show()
self.memoryPanel.basicPanel.show()
self.calldataPanel.basicPanel.show()
self.callstackPanel.basicPanel.show()
})
this.vmDebuggerLogic.event.register('newCallTree', () => {
if (!self.view) return
self.solidityLocals.basicPanel.show()
self.solidityState.basicPanel.show()
})
this.vmDebuggerLogic.start()
}
VmDebugger.prototype.renderHead = function () {
var headView = yo`<div id='vmheadView' class=${css.vmheadView}>
<div>
<div class=${css.asmCode}>${this.asmCode.render()}</div>
<div class=${css.stepDetail}>${this.stepDetail.render()}</div>
</div>
</div>`
if (!this.headView) {
this.headView = headView
}
return headView
}
VmDebugger.prototype.remove = function () {
// used to stop listenning on event. bad and should be "refactored"
this.view = null
}
VmDebugger.prototype.render = function () {
var view = yo`<div id='vmdebugger'>
<div>
${this.solidityLocals.render()}
${this.solidityState.render()}
${this.stackPanel.render()}
${this.memoryPanel.render()}
${this.storagePanel.render()}
${this.callstackPanel.render()}
${this.calldataPanel.render()}
${this.returnValuesPanel.render()}
${this.fullStoragesChangesPanel.render()}
</div>
</div>`
if (!this.view) {
this.view = view
}
return view
}
module.exports = VmDebugger

@ -0,0 +1,17 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function CalldataPanel () {
this.basicPanel = new DropdownPanel('Call Data', {json: true})
}
CalldataPanel.prototype.update = function (calldata) {
this.basicPanel.update(calldata)
}
CalldataPanel.prototype.render = function () {
return yo`<div id='calldatapanel' >${this.basicPanel.render()}</div>`
}
module.exports = CalldataPanel

@ -0,0 +1,18 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function CallstackPanel () {
this.basicPanel = new DropdownPanel('Call Stack', {json: true})
}
CallstackPanel.prototype.update = function (calldata) {
this.basicPanel.update(calldata)
}
CallstackPanel.prototype.render = function () {
return yo`<div id='callstackpanel' >${this.basicPanel.render()}</div>`
}
module.exports = CallstackPanel

@ -1,11 +1,11 @@
'use strict' 'use strict'
var style = require('./styles/basicStyles') var style = require('../styles/basicStyles')
var yo = require('yo-yo') var yo = require('yo-yo')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var DropdownPanel = require('./DropdownPanel') var DropdownPanel = require('./DropdownPanel')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var styleGuide = require('../../../../ui/styles-guide/theme-chooser') var styleGuide = require('../../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser() var styles = styleGuide.chooser()
var css = csjs` var css = csjs`
@ -15,10 +15,8 @@ var css = csjs`
max-height: 150px; max-height: 150px;
} }
` `
function CodeListView (_parent, _codeManager) { function CodeListView () {
this.event = new EventManager() this.event = new EventManager()
this.parent = _parent
this.codeManager = _codeManager
this.code this.code
this.address this.address
this.codeView this.codeView
@ -30,23 +28,14 @@ function CodeListView (_parent, _codeManager) {
this.basicPanel.event.register('show', () => { this.basicPanel.event.register('show', () => {
this.event.trigger('show', []) this.event.trigger('show', [])
}) })
this.init()
} }
CodeListView.prototype.render = function () { CodeListView.prototype.render = function () {
return yo`<div id='asmcodes' >${this.basicPanel.render({height: style.instructionsList.height})}</div>` return yo`<div id='asmcodes' >${this.basicPanel.render({height: style.instructionsList.height})}</div>`
} }
CodeListView.prototype.init = function () {
var self = this
this.codeManager.event.register('changed', this, this.changed)
this.parent.event.register('traceUnloaded', this, function () {
self.changed([], '', -1)
})
}
CodeListView.prototype.indexChanged = function (index) { CodeListView.prototype.indexChanged = function (index) {
if (index >= 0) { if (index < 0) return
if (this.itemSelected) { if (this.itemSelected) {
this.itemSelected.removeAttribute('selected') this.itemSelected.removeAttribute('selected')
this.itemSelected.removeAttribute('style') this.itemSelected.removeAttribute('style')
@ -61,16 +50,20 @@ CodeListView.prototype.indexChanged = function (index) {
this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px') this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px')
} }
this.codeView.scrollTop = this.itemSelected.offsetTop - parseInt(this.codeView.offsetTop) this.codeView.scrollTop = this.itemSelected.offsetTop - parseInt(this.codeView.offsetTop)
} }
CodeListView.prototype.reset = function () {
this.changed([], '', -1)
} }
CodeListView.prototype.changed = function (code, address, index) { CodeListView.prototype.changed = function (code, address, index) {
if (this.address !== address) { if (this.address === address) {
return this.indexChanged(index)
}
this.code = code this.code = code
this.address = address this.address = address
this.codeView = this.renderAssemblyItems() this.codeView = this.renderAssemblyItems()
this.basicPanel.setContent(this.codeView) this.basicPanel.setContent(this.codeView)
}
this.indexChanged(index) this.indexChanged(index)
} }

@ -2,11 +2,11 @@
var yo = require('yo-yo') var yo = require('yo-yo')
const copy = require('clipboard-copy') const copy = require('clipboard-copy')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var TreeView = require('../../../../ui/TreeView') // TODO setup a direct reference to the UI components
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var TreeView = require('../../../ui/TreeView') // TODO setup a direct reference to the UI components
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var styleGuide = require('../../../../ui/styles-guide/theme-chooser') var styleGuide = require('../../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser() var styles = styleGuide.chooser()
var css = csjs` var css = csjs`
@ -69,31 +69,28 @@ function DropdownPanel (_name, _opts) {
} }
DropdownPanel.prototype.setMessage = function (message) { DropdownPanel.prototype.setMessage = function (message) {
if (this.view) { if (!this.view) return
this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none' this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none' this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none' this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none'
this.message(message) this.message(message)
}
} }
DropdownPanel.prototype.setLoading = function () { DropdownPanel.prototype.setLoading = function () {
if (this.view) { if (!this.view) return
this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none' this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none' this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'inline-block' this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'inline-block'
this.message('') this.message('')
}
} }
DropdownPanel.prototype.setUpdating = function () { DropdownPanel.prototype.setUpdating = function () {
if (this.view) { if (!this.view) return
this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.greyedText_color this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.greyedText_color
}
} }
DropdownPanel.prototype.update = function (_data, _header) { DropdownPanel.prototype.update = function (_data, _header) {
if (this.view) { if (!this.view) return
this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none' this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block' this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.mainText_Color this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.mainText_Color
@ -106,14 +103,12 @@ DropdownPanel.prototype.update = function (_data, _header) {
if (this.json) { if (this.json) {
this.treeView.update(_data) this.treeView.update(_data)
} }
}
} }
DropdownPanel.prototype.setContent = function (node) { DropdownPanel.prototype.setContent = function (node) {
if (this.view) { if (!this.view) return
var parent = this.view.querySelector('.dropdownpanel div.dropdowncontent') var parent = this.view.querySelector('.dropdownpanel div.dropdowncontent')
parent.replaceChild(node, parent.firstElementChild) parent.replaceChild(node, parent.firstElementChild)
}
} }
DropdownPanel.prototype.render = function (overridestyle) { DropdownPanel.prototype.render = function (overridestyle) {
@ -178,31 +173,28 @@ DropdownPanel.prototype.toggle = function () {
} }
DropdownPanel.prototype.hide = function () { DropdownPanel.prototype.hide = function () {
if (this.view && !this.displayContentOnly) { if (!(this.view && !this.displayContentOnly)) return
var caret = this.view.querySelector('.title').firstElementChild var caret = this.view.querySelector('.title').firstElementChild
var el = this.view.querySelector('.dropdownpanel') var el = this.view.querySelector('.dropdownpanel')
el.style.display = 'none' el.style.display = 'none'
caret.className = `${css.icon} fa fa-caret-right` caret.className = `${css.icon} fa fa-caret-right`
this.event.trigger('hide', []) this.event.trigger('hide', [])
}
} }
DropdownPanel.prototype.show = function () { DropdownPanel.prototype.show = function () {
if (this.view && !this.displayContentOnly) { if (!(this.view && !this.displayContentOnly)) return
var caret = this.view.querySelector('.title').firstElementChild var caret = this.view.querySelector('.title').firstElementChild
var el = this.view.querySelector('.dropdownpanel') var el = this.view.querySelector('.dropdownpanel')
el.style.display = '' el.style.display = ''
caret.className = `${css.icon} fa fa-caret-down` caret.className = `${css.icon} fa fa-caret-down`
this.event.trigger('show', []) this.event.trigger('show', [])
}
} }
DropdownPanel.prototype.message = function (message) { DropdownPanel.prototype.message = function (message) {
if (this.view) { if (!this.view) return
var mes = this.view.querySelector('.dropdownpanel .message') var mes = this.view.querySelector('.dropdownpanel .message')
mes.innerText = message mes.innerText = message
mes.style.display = (message === '') ? 'none' : 'block' mes.style.display = (message === '') ? 'none' : 'block'
}
} }
module.exports = DropdownPanel module.exports = DropdownPanel

@ -0,0 +1,21 @@
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function FullStoragesChanges () {
this.view
this.basicPanel = new DropdownPanel('Full Storages Changes', {json: true})
}
FullStoragesChanges.prototype.update = function (storageData) {
this.basicPanel.update(storageData)
}
FullStoragesChanges.prototype.render = function () {
var view = yo`<div id='fullstorageschangespanel' >${this.basicPanel.render()}</div>`
if (!this.view) {
this.view = view
}
return view
}
module.exports = FullStoragesChanges

@ -0,0 +1,21 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function MemoryPanel () {
this.basicPanel = new DropdownPanel('Memory', {
json: true,
css: {
'font-family': 'monospace'
}})
}
MemoryPanel.prototype.update = function (calldata) {
this.basicPanel.update(calldata)
}
MemoryPanel.prototype.render = function () {
return yo`<div id='memorypanel' >${this.basicPanel.render()}</div>`
}
module.exports = MemoryPanel

@ -0,0 +1,38 @@
'use strict'
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var DropdownPanel = require('./DropdownPanel')
var solidityTypeFormatter = require('./utils/SolidityTypeFormatter')
var yo = require('yo-yo')
class SolidityLocals {
constructor (_parent, _traceManager, _internalTreeCall) {
this.event = new EventManager()
this.basicPanel = new DropdownPanel('Solidity Locals', {
json: true,
formatSelf: solidityTypeFormatter.formatSelf,
extractData: solidityTypeFormatter.extractData
})
this.view
}
update (data) {
this.basicPanel.update(data)
}
setMessage (message) {
this.basicPanel.setMessage(message)
}
setUpdating () {
this.basicPanel.setUpdating()
}
render () {
this.view = yo`<div id='soliditylocals'>${this.basicPanel.render()}</div>`
return this.view
}
}
module.exports = SolidityLocals

@ -0,0 +1,35 @@
var DropdownPanel = require('./DropdownPanel')
var solidityTypeFormatter = require('./utils/SolidityTypeFormatter')
var yo = require('yo-yo')
function SolidityState () {
this.basicPanel = new DropdownPanel('Solidity State', {
json: true,
// TODO: used by TreeView ui
formatSelf: solidityTypeFormatter.formatSelf,
extractData: solidityTypeFormatter.extractData
})
this.view
}
SolidityState.prototype.update = function (data) {
this.basicPanel.update(data)
}
SolidityState.prototype.setMessage = function (message) {
this.basicPanel.setMessage(message)
}
SolidityState.prototype.setUpdating = function () {
this.basicPanel.setUpdating()
}
SolidityState.prototype.render = function () {
if (this.view) return
this.view = yo`<div id='soliditystate' >
${this.basicPanel.render()}
</div>`
return this.view
}
module.exports = SolidityState

@ -0,0 +1,17 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function StackPanel () {
this.basicPanel = new DropdownPanel('Stack', {json: true, displayContentOnly: false})
}
StackPanel.prototype.update = function (calldata) {
this.basicPanel.update(calldata)
}
StackPanel.prototype.render = function () {
return yo`<div id='stackpanel' >${this.basicPanel.render()}</div>`
}
module.exports = StackPanel

@ -0,0 +1,23 @@
var yo = require('yo-yo')
var DropdownPanel = require('./DropdownPanel')
function StepDetail () {
this.basicPanel = new DropdownPanel('Step detail', {json: true, displayContentOnly: true})
this.detail = { 'vm trace step': '-', 'execution step': '-', 'add memory': '', 'gas': '', 'remaining gas': '-', 'loaded address': '-' }
}
StepDetail.prototype.reset = function () {
this.detail = { 'vm trace step': '-', 'execution step': '-', 'add memory': '', 'gas': '', 'remaining gas': '-', 'loaded address': '-' }
this.basicPanel.update(this.detail)
}
StepDetail.prototype.updateField = function (key, value) {
this.detail[key] = value
this.basicPanel.update(this.detail)
}
StepDetail.prototype.render = function () {
return yo`<div id='stepdetail' >${this.basicPanel.render()}</div>`
}
module.exports = StepDetail

@ -0,0 +1,17 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function StoragePanel (_parent, _traceManager) {
this.basicPanel = new DropdownPanel('Storage', {json: true})
}
StoragePanel.prototype.update = function (storage, header) {
this.basicPanel.update(storage, header)
}
StoragePanel.prototype.render = function () {
return yo`<div id='storagepanel' >${this.basicPanel.render()}</div>`
}
module.exports = StoragePanel

@ -1,4 +1,3 @@
'use strict'
var yo = require('yo-yo') var yo = require('yo-yo')
var BN = require('ethereumjs-util').BN var BN = require('ethereumjs-util').BN
@ -69,3 +68,4 @@ function fontColor (data) {
} }
return 'color:' + color return 'color:' + color
} }

@ -1,4 +1,5 @@
'use strict' 'use strict'
// TODO: this file doesn't seem to be used any more and can be removed
var VMDebugger = require('./src/ui/VmDebugger') var VMDebugger = require('./src/ui/VmDebugger')
var Debugger = require('./src/ui/EthdebuggerUI') var Debugger = require('./src/ui/EthdebuggerUI')
var DebuggerBackend = require('remix-debug').EthDebugger var DebuggerBackend = require('remix-debug').EthDebugger

@ -1,33 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function CalldataPanel (_parentUI, _traceManager) {
this._parentUI = _parentUI
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Call Data', {json: true})
this.init()
}
CalldataPanel.prototype.render = function () {
return yo`<div id='calldatapanel' >${this.basicPanel.render()}</div>`
}
CalldataPanel.prototype.init = function () {
var self = this
this._parentUI.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self._parentUI.currentStepIndex !== index) return
self.traceManager.getCallDataAt(index, function (error, calldata) {
if (error) {
self.basicPanel.update({})
console.log(error)
} else if (self._parentUI.currentStepIndex === index) {
self.basicPanel.update(calldata)
}
})
})
}
module.exports = CalldataPanel

@ -1,33 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function CallstackPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Call Stack', {json: true})
this.init()
}
CallstackPanel.prototype.render = function () {
return yo`<div id='callstackpanel' >${this.basicPanel.render()}</div>`
}
CallstackPanel.prototype.init = function () {
var self = this
this.parent.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.parent.currentStepIndex !== index) return
self.traceManager.getCallStackAt(index, function (error, callstack) {
if (error) {
console.log(error)
self.basicPanel.update({})
} else if (self.parent.currentStepIndex === index) {
self.basicPanel.update(callstack)
}
})
})
}
module.exports = CallstackPanel

@ -1,210 +0,0 @@
'use strict'
var TxBrowser = require('./TxBrowser')
var StepManager = require('./StepManager')
var remixLib = require('remix-lib')
var TraceManager = remixLib.trace.TraceManager
var VmDebugger = require('./VmDebugger')
var init = remixLib.init
var executionContext = remixLib.execution.executionContext
var EventManager = remixLib.EventManager
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var Web3Providers = remixLib.vm.Web3Providers
var DummyProvider = remixLib.vm.DummyProvider
var CodeManager = remixLib.code.CodeManager
var remixDebug = require('remix-debug')
var SolidityProxy = remixDebug.SolidityDecoder.SolidityProxy
var InternalCallTree = remixDebug.SolidityDecoder.InternalCallTree
var css = csjs`
.statusMessage {
margin-left: 15px;
}
.innerShift {
padding: 2px;
margin-left: 10px;
}
`
function Ethdebugger (opts) {
this.opts = opts || {}
if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null }
var self = this
this.event = new EventManager()
this.currentStepIndex = -1
this.tx
this.statusMessage = ''
this.view
this.web3Providers = new Web3Providers()
this.addProvider('DUMMYWEB3', new DummyProvider())
this.switchProvider('DUMMYWEB3')
this.traceManager = new TraceManager()
this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
var callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
this.callTree = callTree // TODO: currently used by browser solidity, we should improve the API
this.event.register('indexChanged', this, function (index) {
self.codeManager.resolveStep(index, self.tx)
})
this.txBrowser = new TxBrowser(this)
this.txBrowser.event.register('newTxLoading', this, function () {
self.unLoad()
})
this.txBrowser.event.register('newTraceRequested', this, function (blockNumber, txIndex, tx) {
console.dir('newTraceRequestd')
console.dir(arguments)
self.startDebugging(blockNumber, txIndex, tx)
})
this.txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) {
self.unLoad()
})
this.stepManager = new StepManager(this, this.traceManager)
this.stepManager.event.register('stepChanged', this, function (stepIndex) {
self.stepChanged(stepIndex)
})
this.vmDebugger = new VmDebugger(this, this.traceManager, this.codeManager, this.solidityProxy, callTree)
this.codeManager.event.register('changed', this, (code, address, instIndex) => {
this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.currentStepIndex, this.solidityProxy.contracts, (error, sourceLocation) => {
if (!error) {
this.event.trigger('sourceLocationChanged', [sourceLocation])
}
})
})
}
Ethdebugger.prototype.setManagers = function () {
this.traceManager = new TraceManager({web3: this.web3})
this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
this.storageResolver = null
var callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
this.callTree = callTree // TODO: currently used by browser solidity, we should improve the API
this.vmDebugger = new VmDebugger(this, this.traceManager, this.codeManager, this.solidityProxy, callTree)
this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
}
Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) {
this.breakpointManager = breakpointManager
}
Ethdebugger.prototype.web3 = function () {
return global.web3
}
Ethdebugger.prototype.addProvider = function (type, obj) {
this.web3Providers.addProvider(type, obj)
this.event.trigger('providerAdded', [type])
}
Ethdebugger.prototype.updateWeb3Reference = function () {
if (!this.txBrowser) return
this.txBrowser.web3 = this.web3
}
Ethdebugger.prototype.switchProvider = function (type) {
var self = this
this.web3Providers.get(type, function (error, obj) {
if (error) {
console.log('provider ' + type + ' not defined')
} else {
self.web3 = obj
self.setManagers()
self.updateWeb3Reference()
executionContext.detectNetwork((error, network) => {
if (error || !network) {
global.web3Debug = obj
} else {
var webDebugNode = init.web3DebugNode(network.name)
global.web3Debug = !webDebugNode ? obj : webDebugNode
}
self.updateWeb3Reference()
})
self.event.trigger('providerChanged', [type])
}
})
}
Ethdebugger.prototype.setCompilationResult = function (compilationResult) {
if (compilationResult && compilationResult.sources && compilationResult.contracts) {
this.solidityProxy.reset(compilationResult)
} else {
this.solidityProxy.reset({})
}
}
Ethdebugger.prototype.debug = function (tx) {
this.setCompilationResult(this.opts.compilationResult())
if (tx instanceof Object) {
this.txBrowser.load(tx.hash)
} else if (tx instanceof String) {
this.txBrowser.load(tx)
}
}
Ethdebugger.prototype.render = function () {
var view = yo`<div>
<div class="${css.innerShift}">
${this.txBrowser.render()}
${this.vmDebugger.renderHead()}
${this.stepManager.render()}
</div>
<div class="${css.statusMessage}" >${this.statusMessage}</div>
${this.vmDebugger.render()}
</div>`
if (!this.view) {
this.view = view
}
return view
}
Ethdebugger.prototype.unLoad = function () {
this.traceManager.init()
this.codeManager.clear()
this.stepManager.reset()
this.event.trigger('traceUnloaded')
}
Ethdebugger.prototype.stepChanged = function (stepIndex) {
this.currentStepIndex = stepIndex
this.event.trigger('indexChanged', [stepIndex])
}
Ethdebugger.prototype.startDebugging = function (blockNumber, txIndex, tx) {
console.dir('startDebugging')
console.dir(arguments)
if (this.traceManager.isLoading) {
return
}
this.setCompilationResult(this.opts.compilationResult())
this.statusMessage = 'Loading trace...'
yo.update(this.view, this.render())
console.log('loading trace...')
this.tx = tx
var self = this
console.dir('resolving a trace with tx: ')
console.dir(tx)
this.traceManager.resolveTrace(tx, function (error, result) {
console.log('trace loaded ' + result)
if (result) {
self.statusMessage = ''
yo.update(self.view, self.render())
self.event.trigger('newTraceLoaded', [self.traceManager.trace])
if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) {
self.breakpointManager.jumpNextBreakpoint(0, false)
}
} else {
self.statusMessage = error ? error.message : 'Trace not loaded'
yo.update(self.view, self.render())
}
})
}
module.exports = Ethdebugger

@ -1,154 +0,0 @@
'use strict'
var TxBrowser = require('./TxBrowser')
var StepManager = require('./StepManager')
var VmDebugger = require('./VmDebugger')
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var executionContext = remixLib.execution.executionContext
var EventManager = remixLib.EventManager
var css = csjs`
.statusMessage {
margin-left: 15px;
}
.innerShift {
padding: 2px;
margin-left: 10px;
}
`
function EthdebuggerUI (opts) {
this.opts = opts || {}
this.debugger = opts.debugger
if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null }
var self = this
this.event = new EventManager()
this.currentStepIndex = -1
this.tx
this.statusMessage = ''
this.view
this.event.register('indexChanged', this, function (index) {
self.debugger.codeManager.resolveStep(index, self.tx)
})
executionContext.event.register('contextChanged', this, function () {
self.updateWeb3Reference()
})
this.txBrowser = new TxBrowser(this, {displayConnectionSetting: false, web3: executionContext.web3()})
this.txBrowser.event.register('newTxLoading', this, function () {
self.unLoad()
})
this.txBrowser.event.register('newTraceRequested', this, function (blockNumber, txIndex, tx) {
self.startDebugging(blockNumber, txIndex, tx)
})
this.txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) {
self.unLoad()
})
}
EthdebuggerUI.prototype.setBreakpointManager = function (breakpointManager) {
this.breakpointManager = breakpointManager
}
EthdebuggerUI.prototype.get_web3 = function () {
return this.web3
}
EthdebuggerUI.prototype.updateWeb3Reference = function (web3) {
if (!this.txBrowser) return
this.txBrowser.web3 = web3 || executionContext.web3()
}
EthdebuggerUI.prototype.setCompilationResult = function (compilationResult) {
if (compilationResult && compilationResult.sources && compilationResult.contracts) {
this.debugger.solidityProxy.reset(compilationResult)
} else {
this.debugger.solidityProxy.reset({})
}
}
EthdebuggerUI.prototype.debug = function (tx) {
this.setCompilationResult(this.opts.compilationResult())
if (tx instanceof Object) {
this.txBrowser.load(tx.hash, tx)
} else if (tx instanceof String) {
this.txBrowser.load(tx)
}
}
EthdebuggerUI.prototype.render = function () {
this.debuggerPanelsView = yo`<div class="${css.innerShift}"></div>`
this.debuggerHeadPanelsView = yo`<div class="${css.innerShift}"></div>`
this.stepManagerView = yo`<div class="${css.innerShift}"></div>`
var view = yo`<div>
<div class="${css.innerShift}">
${this.txBrowser.render()}
${this.debuggerHeadPanelsView}
${this.stepManagerView}
</div>
<div class="${css.statusMessage}" >${this.statusMessage}</div>
${this.debuggerPanelsView}
</div>`
if (!this.view) {
this.view = view
}
return view
}
EthdebuggerUI.prototype.unLoad = function () {
this.debugger.unLoad()
yo.update(this.debuggerHeadPanelsView, yo`<div></div>`)
yo.update(this.debuggerPanelsView, yo`<div></div>`)
yo.update(this.stepManagerView, yo`<div></div>`)
if (this.vmDebugger) this.vmDebugger.remove()
if (this.stepManager) this.stepManager.remove()
this.vmDebugger = null
this.stepManager = null
this.event.trigger('traceUnloaded')
}
EthdebuggerUI.prototype.stepChanged = function (stepIndex) {
this.currentStepIndex = stepIndex
this.event.trigger('indexChanged', [stepIndex])
}
EthdebuggerUI.prototype.startDebugging = function (blockNumber, txIndex, tx) {
const self = this
if (this.debugger.traceManager.isLoading) {
return
}
this.tx = tx
this.stepManager = new StepManager(this, this.debugger.traceManager)
this.stepManager.event.register('stepChanged', this, function (stepIndex) {
self.stepChanged(stepIndex)
})
this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => {
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.currentStepIndex, this.debugger.solidityProxy.contracts, (error, sourceLocation) => {
if (!error) {
self.event.trigger('sourceLocationChanged', [sourceLocation])
}
})
})
this.vmDebugger = new VmDebugger(this, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree)
yo.update(this.debuggerHeadPanelsView, this.vmDebugger.renderHead())
yo.update(this.debuggerPanelsView, this.vmDebugger.render())
yo.update(this.stepManagerView, this.stepManager.render())
this.debugger.debug(tx)
}
module.exports = EthdebuggerUI

@ -1,72 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var remixDebug = require('remix-debug')
var StorageViewer = remixDebug.storage.StorageViewer
var yo = require('yo-yo')
function FullStoragesChanges (_parent, _traceManager) {
this.storageResolver = null
this.parent = _parent
this.debugger = _parent.debugger
this.traceManager = _traceManager
this.addresses = []
this.view
this.traceLength
this.basicPanel = new DropdownPanel('Full Storages Changes', {json: true})
this.init()
}
FullStoragesChanges.prototype.render = function () {
var view = yo`<div id='fullstorageschangespanel' >${this.basicPanel.render()}</div>`
if (!this.view) {
this.view = view
}
return view
}
FullStoragesChanges.prototype.init = function () {
var self = this
this.debugger.event.register('newTraceLoaded', this, function (length) {
self.panels = []
self.traceManager.getAddresses(function (error, addresses) {
if (!error) {
self.addresses = addresses
self.basicPanel.update({})
}
})
self.traceManager.getLength(function (error, length) {
if (!error) {
self.traceLength = length
}
})
})
this.debugger.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.parent.currentStepIndex !== index) return
if (!self.storageResolver) return
if (index === self.traceLength - 1) {
var storageJSON = {}
for (var k in self.addresses) {
var address = self.addresses[k]
var storageViewer = new StorageViewer({
stepIndex: self.parent.currentStepIndex,
tx: self.parent.tx,
address: address
}, self.storageResolver, self.traceManager)
storageViewer.storageRange(function (error, result) {
if (!error) {
storageJSON[address] = result
self.basicPanel.update(storageJSON)
}
})
}
} else {
self.basicPanel.update({})
}
})
}
module.exports = FullStoragesChanges

@ -1,39 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var remixLib = require('remix-lib')
var ui = remixLib.helpers.ui
var yo = require('yo-yo')
function MemoryPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Memory', {
json: true,
css: {
'font-family': 'monospace'
}})
this.init()
}
MemoryPanel.prototype.render = function () {
return yo`<div id='memorypanel' >${this.basicPanel.render()}</div>`
}
MemoryPanel.prototype.init = function () {
var self = this
this.parent.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.parent.currentStepIndex !== index) return
self.traceManager.getMemoryAt(index, function (error, memory) {
if (error) {
console.log(error)
self.basicPanel.update({})
} else if (self.parent.currentStepIndex === index) {
self.basicPanel.update(ui.formatMemory(memory, 16))
}
})
})
}
module.exports = MemoryPanel

@ -1,75 +0,0 @@
'use strict'
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var yo = require('yo-yo')
class Slider {
constructor (_traceManager, _stepOverride) {
this.event = new EventManager()
this.traceManager = _traceManager
this.max
this.disabled = true
this.view
this.solidityMode = false
this.stepOverride = _stepOverride
this.previousValue = null
}
render () {
var self = this
var view = yo`<div>
<input
id='slider'
style='width: 100%'
type='range'
min=0
max=${this.max}
value=0
onchange=${function () { self.onChange() }}
oninput=${function () { self.onChange() }}
disabled=${this.disabled} />
</div>`
if (!this.view) {
this.view = view
}
return view
}
init (length) {
var slider = this.view.querySelector('#slider')
slider.setAttribute('max', length - 1)
this.max = length - 1
this.updateDisabled(length === 0)
this.disabled = length === 0
this.setValue(0)
}
onChange (event) {
var value = parseInt(this.view.querySelector('#slider').value)
if (this.stepOverride) {
var correctedValue = this.stepOverride(value)
if (correctedValue !== value) {
this.setValue(correctedValue)
value = correctedValue
}
}
if (value === this.previousValue) return
this.previousValue = value
this.event.trigger('moved', [value])
}
setValue (value) {
this.view.querySelector('#slider').value = value
}
updateDisabled (disabled) {
if (disabled) {
this.view.querySelector('#slider').setAttribute('disabled', true)
} else {
this.view.querySelector('#slider').removeAttribute('disabled')
}
}
}
module.exports = Slider

@ -1,83 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var remixDebug = require('remix-debug')
var localDecoder = remixDebug.SolidityDecoder.localDecoder
var solidityTypeFormatter = require('./SolidityTypeFormatter')
var StorageViewer = remixDebug.storage.StorageViewer
var yo = require('yo-yo')
class SolidityLocals {
constructor (_parent, _traceManager, _internalTreeCall) {
this.parent = _parent
this.internalTreeCall = _internalTreeCall
this.storageResolver = null
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Solidity Locals', {
json: true,
formatSelf: solidityTypeFormatter.formatSelf,
extractData: solidityTypeFormatter.extractData
})
this.init()
this.view
}
render () {
this.view = yo`<div id='soliditylocals' >
${this.basicPanel.render()}
</div>`
return this.view
}
init () {
var decodeTimeout = null
this.parent.event.register('sourceLocationChanged', this, (sourceLocation) => {
if (!this.storageResolver) {
this.basicPanel.setMessage('storage not ready')
return
}
if (decodeTimeout) {
window.clearTimeout(decodeTimeout)
}
this.basicPanel.setUpdating()
decodeTimeout = setTimeout(() => {
decode(this, sourceLocation)
}, 500)
})
}
}
function decode (self, sourceLocation) {
self.basicPanel.setMessage('')
self.traceManager.waterfall([
self.traceManager.getStackAt,
self.traceManager.getMemoryAt,
self.traceManager.getCurrentCalledAddressAt],
self.parent.currentStepIndex,
(error, result) => {
if (!error) {
var stack = result[0].value
var memory = result[1].value
try {
var storageViewer = new StorageViewer({
stepIndex: self.parent.currentStepIndex,
tx: self.parent.tx,
address: result[2].value
}, self.storageResolver, self.traceManager)
localDecoder.solidityLocals(self.parent.currentStepIndex, self.internalTreeCall, stack, memory, storageViewer, sourceLocation).then((locals) => {
if (!locals.error) {
self.basicPanel.update(locals)
}
if (!Object.keys(locals).length) {
self.basicPanel.setMessage('no locals')
}
})
} catch (e) {
self.basicPanel.setMessage(e.message)
}
} else {
console.log(error)
}
})
}
module.exports = SolidityLocals

@ -1,103 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var remixDebug = require('remix-debug')
var stateDecoder = remixDebug.SolidityDecoder.stateDecoder
var solidityTypeFormatter = require('./SolidityTypeFormatter')
var StorageViewer = remixDebug.storage.StorageViewer
var yo = require('yo-yo')
function SolidityState (_parent, _traceManager, _codeManager, _solidityProxy) {
this.storageResolver = null
this.parent = _parent
this.traceManager = _traceManager
this.codeManager = _codeManager
this.solidityProxy = _solidityProxy
this.basicPanel = new DropdownPanel('Solidity State', {
json: true,
formatSelf: solidityTypeFormatter.formatSelf,
extractData: solidityTypeFormatter.extractData
})
this.init()
this.view
this.stateVariablesByAddresses = {}
_parent.event.register('traceUnloaded', () => { this.stateVariablesByAddresses = {} })
_parent.event.register('newTraceLoaded', () => { this.stateVariablesByAddresses = {} })
}
SolidityState.prototype.render = function () {
if (!this.view) {
this.view = yo`<div id='soliditystate' >
${this.basicPanel.render()}
</div>`
}
return this.view
}
SolidityState.prototype.init = function () {
var self = this
var decodeTimeout = null
this.parent.event.register('indexChanged', this, function (index) {
if (index < 0) {
self.basicPanel.setMessage('invalid step index')
return
}
if (self.parent.currentStepIndex !== index) return
if (!self.solidityProxy.loaded()) {
self.basicPanel.setMessage('no source has been specified')
return
}
if (!self.storageResolver) {
return
}
if (decodeTimeout) {
window.clearTimeout(decodeTimeout)
}
self.basicPanel.setUpdating()
decodeTimeout = setTimeout(() => {
decode(self, index)
}, 500)
})
}
function decode (self, index) {
self.traceManager.getCurrentCalledAddressAt(self.parent.currentStepIndex, (error, address) => {
if (error) {
self.basicPanel.update({})
console.log(error)
} else {
if (self.stateVariablesByAddresses[address]) {
extractStateVariables(self, self.stateVariablesByAddresses[address], address)
} else {
self.solidityProxy.extractStateVariablesAt(index, function (error, stateVars) {
if (error) {
self.basicPanel.update({})
console.log(error)
} else {
self.stateVariablesByAddresses[address] = stateVars
extractStateVariables(self, stateVars, address)
}
})
}
}
})
}
function extractStateVariables (self, stateVars, address) {
var storageViewer = new StorageViewer({
stepIndex: self.parent.currentStepIndex,
tx: self.parent.tx,
address: address
}, self.storageResolver, self.traceManager)
stateDecoder.decodeState(stateVars, storageViewer).then((result) => {
self.basicPanel.setMessage('')
if (!result.error) {
self.basicPanel.update(result)
} else {
self.basicPanel.setMessage(result.error)
}
})
}
module.exports = SolidityState

@ -1,33 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function StackPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Stack', {json: true, displayContentOnly: false})
this.init()
}
StackPanel.prototype.render = function () {
return yo`<div id='stackpanel' >${this.basicPanel.render()}</div>`
}
StackPanel.prototype.init = function () {
var self = this
this.parent.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.parent.currentStepIndex !== index) return
self.traceManager.getStackAt(index, function (error, stack) {
if (error) {
self.basicPanel.update({})
console.log(error)
} else if (self.parent.currentStepIndex === index) {
self.basicPanel.update(stack)
}
})
})
}
module.exports = StackPanel

@ -1,101 +0,0 @@
'use strict'
var yo = require('yo-yo')
var DropdownPanel = require('./DropdownPanel')
function StepDetail (_parentUI, _traceManager) {
this.debugger = _parentUI.debugger
this.parentUI = _parentUI
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Step detail', {json: true, displayContentOnly: true})
this.detail = initDetail()
this.view
this.init()
}
StepDetail.prototype.render = function () {
return yo`<div id='stepdetail' >${this.basicPanel.render()}</div>`
}
StepDetail.prototype.init = function () {
var self = this
this.debugger.event.register('traceUnloaded', this, function () {
self.detail = initDetail()
self.basicPanel.update(self.detail)
})
this.debugger.event.register('newTraceLoaded', this, function () {
self.detail = initDetail()
self.basicPanel.update(self.detail)
})
this.parentUI.event.register('indexChanged', this, function (index) {
if (index < 0) return
self.detail['vm trace step'] = index
self.traceManager.getCurrentStep(index, function (error, step) {
if (error) {
console.log(error)
self.detail['execution step'] = '-'
} else {
self.detail['execution step'] = step
}
self.basicPanel.update(self.detail)
})
self.traceManager.getMemExpand(index, function (error, addmem) {
if (error) {
console.log(error)
self.detail['add memory'] = '-'
} else {
self.detail['add memory'] = addmem
}
self.basicPanel.update(self.detail)
})
self.traceManager.getStepCost(index, function (error, gas) {
if (error) {
console.log(error)
self.detail.gas = '-'
} else {
self.detail.gas = gas
}
self.basicPanel.update(self.detail)
})
self.traceManager.getCurrentCalledAddressAt(index, function (error, address) {
if (error) {
console.log(error)
self.detail['loaded address'] = '-'
} else {
self.detail['loaded address'] = address
}
self.basicPanel.update(self.detail)
})
self.traceManager.getRemainingGas(index, function (error, remaingas) {
if (error) {
console.log(error)
self.detail['remaining gas'] = '-'
} else {
self.detail['remaining gas'] = remaingas
}
self.basicPanel.update(self.detail)
})
})
}
module.exports = StepDetail
function initDetail () {
return {
'vm trace step': '-',
'execution step': '-',
'add memory': '',
'gas': '',
'remaining gas': '-',
'loaded address': '-'
}
}

@ -1,205 +0,0 @@
'use strict'
var ButtonNavigator = require('./ButtonNavigator')
var Slider = require('./Slider')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var yo = require('yo-yo')
var util = remixLib.util
function StepManager (_parent, _traceManager) {
this.event = new EventManager()
this.parent = _parent.debugger
this.traceManager = _traceManager
this.sourceMapByAddress = {}
this.solidityMode = false
var self = this
this.parent.event.register('newTraceLoaded', this, function () {
if (!this.slider) return
self.traceManager.getLength(function (error, length) {
if (error) {
console.log(error)
} else {
self.slider.init(length)
self.init()
}
})
})
this.slider = new Slider(this.traceManager, (step) => {
return this.solidityMode ? this.resolveToReducedTrace(step, 0) : step
})
this.slider.event.register('moved', this, function (step) {
self.sliderMoved(step)
})
this.parent.callTree.event.register('callTreeReady', () => {
if (!this.slider) return
if (this.parent.callTree.functionCallStack.length) {
this.jumpTo(this.parent.callTree.functionCallStack[0])
}
})
this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager)
this.buttonNavigator.event.register('stepIntoBack', this, function () {
self.stepIntoBack()
})
this.buttonNavigator.event.register('stepIntoForward', this, function () {
self.stepIntoForward()
})
this.buttonNavigator.event.register('stepOverBack', this, function () {
self.stepOverBack()
})
this.buttonNavigator.event.register('stepOverForward', this, function () {
self.stepOverForward()
})
this.buttonNavigator.event.register('jumpOut', this, function () {
self.jumpOut()
})
this.buttonNavigator.event.register('jumpToException', this, function (exceptionIndex) {
self.jumpTo(exceptionIndex)
})
this.buttonNavigator.event.register('jumpNextBreakpoint', (exceptionIndex) => {
self.parent.breakpointManager.jumpNextBreakpoint(_parent.currentStepIndex, true)
})
this.buttonNavigator.event.register('jumpPreviousBreakpoint', (exceptionIndex) => {
self.parent.breakpointManager.jumpPreviousBreakpoint(_parent.currentStepIndex, true)
})
}
StepManager.prototype.remove = function () {
// used to stop listenning on event. bad and should be "refactored"
this.slider.view = null
this.slider = null
this.buttonNavigator.view = null
this.buttonNavigator = null
}
StepManager.prototype.resolveToReducedTrace = function (value, incr) {
if (this.parent.callTree.reducedTrace.length) {
var nextSource = util.findClosestIndex(value, this.parent.callTree.reducedTrace)
nextSource = nextSource + incr
if (nextSource <= 0) {
nextSource = 0
} else if (nextSource > this.parent.callTree.reducedTrace.length) {
nextSource = this.parent.callTree.reducedTrace.length - 1
}
return this.parent.callTree.reducedTrace[nextSource]
}
return value
}
StepManager.prototype.render = function () {
return yo`<div>
${this.slider.render()}
${this.buttonNavigator.render()}
</div>`
}
StepManager.prototype.reset = function () {
this.slider.setValue(0)
this.currentStepIndex = 0
this.buttonNavigator.reset()
}
StepManager.prototype.init = function () {
this.slider.setValue(0)
this.changeState(0)
}
StepManager.prototype.newTraceAvailable = function () {
this.init()
}
StepManager.prototype.jumpTo = function (step) {
if (!this.traceManager.inRange(step)) {
return
}
this.slider.setValue(step)
this.changeState(step)
}
StepManager.prototype.sliderMoved = function (step) {
if (!this.traceManager.inRange(step)) {
return
}
this.changeState(step)
}
StepManager.prototype.stepIntoForward = function () {
if (!this.traceManager.isLoaded()) {
return
}
var step = this.currentStepIndex
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, 1)
} else {
step += 1
}
if (!this.traceManager.inRange(step)) {
return
}
this.slider.setValue(step)
this.changeState(step)
}
StepManager.prototype.stepIntoBack = function () {
if (!this.traceManager.isLoaded()) {
return
}
var step = this.currentStepIndex
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, -1)
} else {
step -= 1
}
if (!this.traceManager.inRange(step)) {
return
}
this.slider.setValue(step)
this.changeState(step)
}
StepManager.prototype.stepOverForward = function () {
if (!this.traceManager.isLoaded()) {
return
}
var step = this.traceManager.findStepOverForward(this.currentStepIndex)
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, 1)
}
this.slider.setValue(step)
this.changeState(step)
}
StepManager.prototype.stepOverBack = function () {
if (!this.traceManager.isLoaded()) {
return
}
var step = this.traceManager.findStepOverBack(this.currentStepIndex)
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, -1)
}
this.slider.setValue(step)
this.changeState(step)
}
StepManager.prototype.jumpOut = function () {
if (!this.traceManager.isLoaded()) {
return
}
var step = this.traceManager.findStepOut(this.currentStepIndex)
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, 0)
}
this.slider.setValue(step)
this.changeState(step)
}
StepManager.prototype.changeState = function (step) {
this.currentStepIndex = step
this.buttonNavigator.stepChanged(step)
this.event.trigger('stepChanged', [step])
}
module.exports = StepManager

@ -1,50 +0,0 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var remixDebug = require('remix-debug')
var StorageViewer = remixDebug.storage.StorageViewer
var yo = require('yo-yo')
function StoragePanel (_parent, _traceManager) {
this.parent = _parent
this.storageResolver = null
this.traceManager = _traceManager
this.basicPanel = new DropdownPanel('Storage', {json: true})
this.init()
this.disabled = false
}
StoragePanel.prototype.render = function () {
return yo`<div id='storagepanel' >${this.basicPanel.render()}</div>`
}
StoragePanel.prototype.init = function () {
var self = this
this.parent.event.register('indexChanged', this, function (index) {
if (self.disabled) return
if (index < 0) return
if (self.parent.currentStepIndex !== index) return
if (!self.storageResolver) return
this.traceManager.getCurrentCalledAddressAt(index, (error, address) => {
if (!error) {
var storageViewer = new StorageViewer({
stepIndex: self.parent.currentStepIndex,
tx: self.parent.tx,
address: address
}, self.storageResolver, self.traceManager)
storageViewer.storageRange((error, storage) => {
if (error) {
console.log(error)
self.basicPanel.update({})
} else if (self.parent.currentStepIndex === index) {
var header = storageViewer.isComplete(address) ? 'completely loaded' : 'partially loaded...'
self.basicPanel.update(storage, header)
}
})
}
})
})
}
module.exports = StoragePanel

@ -1,206 +0,0 @@
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var traceHelper = remixLib.helpers.trace
var yo = require('yo-yo')
var init = remixLib.init
var csjs = require('csjs-inject')
var styleGuide = require('../../../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.container {
display: flex;
flex-direction: column;
}
.txContainer {
display: flex;
flex-direction: column;
}
.txinputs {
width: 100%;
display: flex;
justify-content: center;
}
.txinput {
${styles.rightPanel.debuggerTab.input_Debugger}
margin: 3px;
width: inherit;
}
.txbuttons {
width: 100%;
display: flex;
justify-content: center;
}
.txbutton {
${styles.rightPanel.debuggerTab.button_Debugger}
width: inherit;
}
.txbuttonstart {
${styles.rightPanel.debuggerTab.button_Debugger}
}
.txbutton:hover {
color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
}
.vmargin {
margin-top: 10px;
margin-bottom: 10px;
}
`
function TxBrowser (_parent, opts) {
this.event = new EventManager()
this.blockNumber
this.txNumber
this.view
this.displayConnectionSetting = opts.displayConnectionSetting
this.web3 = opts.web3
var self = this
_parent.event.register('providerChanged', this, function (provider) {
self.setDefaultValues()
if (self.view) {
yo.update(self.view, self.render())
}
})
}
// creation 0xa9619e1d0a35b2c1d686f5b661b3abd87f998d2844e8e9cc905edb57fc9ce349
// invokation 0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4 0xcda2b2835add61af54cf83bd076664d98d7908c6cd98d86423b3b48d8b8e51ff
// test:
// creation: 0x72908de76f99fca476f9e3a3b5d352f350a98cd77d09cebfc59ffe32a6ecaa0b
// invokation: 0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51
TxBrowser.prototype.setDefaultValues = function () {
this.connectInfo = ''
if (this.view) {
yo.update(this.view, this.render())
}
}
TxBrowser.prototype.submit = function (tx) {
var self = this
self.event.trigger('newTxLoading', [this.blockNumber, this.txNumber])
if (tx) {
return self.update(null, tx)
}
if (!this.txNumber) {
self.update('no tx index or tx hash to look for')
return
}
try {
if (this.txNumber.indexOf('0x') !== -1) {
self.web3.eth.getTransaction(this.txNumber, function (error, result) {
self.update(error, result)
})
} else {
self.web3.eth.getTransactionFromBlock(this.blockNumber, this.txNumber, function (error, result) {
self.update(error, result)
})
}
} catch (e) {
self.update(e.message)
}
}
TxBrowser.prototype.update = function (error, tx) {
var info = {}
if (error) {
this.view.querySelector('#error').innerHTML = error
} else {
if (tx) {
this.view.querySelector('#error').innerHTML = ''
if (!tx.to) {
tx.to = traceHelper.contractCreationToken('0')
}
info.from = tx.from
info.to = tx.to
info.hash = tx.hash
this.event.trigger('newTraceRequested', [this.blockNumber, this.txNumber, tx])
} else {
var mes = '<not found>'
info.from = mes
info.to = mes
info.hash = mes
this.view.querySelector('#error').innerHTML = 'Cannot find transaction with reference. Block number: ' + this.blockNumber + '. Transaction index/hash: ' + this.txNumber
}
}
}
TxBrowser.prototype.updateWeb3Url = function (newhost) {
init.setProvider(global.web3, newhost)
var self = this
this.checkWeb3(function (error, block) {
if (!error) {
self.connectInfo = 'Connected to ' + global.web3.currentProvider.host + '. Current block number: ' + block
} else {
self.connectInfo = 'Unable to connect to ' + global.web3.currentProvider.host + '. ' + error.message
}
yo.update(self.view, self.render())
})
}
TxBrowser.prototype.checkWeb3 = function (callback) {
try {
global.web3.eth.getBlockNumber(function (error, block) {
callback(error, block)
})
} catch (e) {
console.log(e)
callback(e.message, null)
}
}
TxBrowser.prototype.updateBlockN = function (ev) {
this.blockNumber = ev.target.value
}
TxBrowser.prototype.updateTxN = function (ev) {
this.txNumber = ev.target.value
}
TxBrowser.prototype.load = function (txHash, tx) {
this.txNumber = txHash
this.submit(tx)
}
TxBrowser.prototype.unload = function (txHash) {
this.event.trigger('unloadRequested')
this.init()
}
TxBrowser.prototype.init = function (ev) {
this.setDefaultValues()
}
TxBrowser.prototype.connectionSetting = function () {
if (this.displayConnectionSetting) {
var self = this
return yo`<div class="${css.vmargin}"><span>Node URL: </span><input onkeyup=${function () { self.updateWeb3Url(arguments[0].target.value) }} value=${global.web3.currentProvider ? global.web3.currentProvider.host : ' - none - '} type='text' />
<span>${this.connectInfo}</span></div>`
} else {
return ''
}
}
TxBrowser.prototype.render = function () {
var self = this
var view = yo`<div class="${css.container}">
${this.connectionSetting()}
<div class="${css.txContainer}">
<div class="${css.txinputs}">
<input class="${css.txinput}" onkeyup=${function () { self.updateBlockN(arguments[0]) }} type='text' placeholder=${'Block number'} />
<input class="${css.txinput}" id='txinput' onkeyup=${function () { self.updateTxN(arguments[0]) }} type='text' placeholder=${'Transaction index or hash'} />
</div>
<div class="${css.txbuttons}">
<button id='load' class='${css.txbutton}' title='start debugging' onclick=${function () { self.submit() }}>Start debugging</button>
<button id='unload' class='${css.txbutton}' title='stop debugging' onclick=${function () { self.unload() }}>Stop</button>
</div>
</div>
<span id='error'></span>
</div>`
if (!this.view) {
this.view = view
}
return view
}
module.exports = TxBrowser

@ -1,126 +0,0 @@
'use strict'
var csjs = require('csjs-inject')
var CodeListView = require('./CodeListView')
var CalldataPanel = require('./CalldataPanel')
var MemoryPanel = require('./MemoryPanel')
var CallstackPanel = require('./CallstackPanel')
var StackPanel = require('./StackPanel')
var StoragePanel = require('./StoragePanel')
var FullStoragesChangesPanel = require('./FullStoragesChanges')
var StepDetail = require('./StepDetail')
var DropdownPanel = require('./DropdownPanel')
var SolidityState = require('./SolidityState')
var SolidityLocals = require('./SolidityLocals')
var remixDebug = require('remix-debug')
var StorageResolver = remixDebug.storage.StorageResolver
var yo = require('yo-yo')
var css = csjs`
.asmCode {
float: left;
width: 50%;
}
.stepDetail {
}
.vmheadView {
margin-top:10px;
}
`
function VmDebugger (_parentUI, _traceManager, _codeManager, _solidityProxy, _callTree) {
let _parent = _parentUI.debugger
var self = this
this.view
this.asmCode = new CodeListView(_parent, _codeManager)
this.stackPanel = new StackPanel(_parentUI, _traceManager)
this.storagePanel = new StoragePanel(_parentUI, _traceManager)
this.memoryPanel = new MemoryPanel(_parentUI, _traceManager)
this.calldataPanel = new CalldataPanel(_parentUI, _traceManager)
this.callstackPanel = new CallstackPanel(_parentUI, _traceManager)
this.stepDetail = new StepDetail(_parentUI, _traceManager)
this.solidityState = new SolidityState(_parentUI, _traceManager, _codeManager, _solidityProxy)
this.solidityLocals = new SolidityLocals(_parentUI, _traceManager, _callTree)
/* Return values - */
this.returnValuesPanel = new DropdownPanel('Return Value', {json: true})
this.returnValuesPanel.data = {}
_parentUI.event.register('indexChanged', this.returnValuesPanel, function (index) {
if (!self.view) return
var innerself = this
_traceManager.getReturnValue(index, function (error, returnValue) {
if (error) {
innerself.update([error])
} else if (_parentUI.currentStepIndex === index) {
innerself.update([returnValue])
}
})
})
/* Return values - */
this.fullStoragesChangesPanel = new FullStoragesChangesPanel(_parentUI, _traceManager)
_parent.event.register('newTraceLoaded', this, function () {
if (!self.view) return
var storageResolver = new StorageResolver({web3: _parent.web3})
self.storagePanel.storageResolver = storageResolver
self.solidityState.storageResolver = storageResolver
self.solidityLocals.storageResolver = storageResolver
self.fullStoragesChangesPanel.storageResolver = storageResolver
self.asmCode.basicPanel.show()
self.stackPanel.basicPanel.show()
self.storagePanel.basicPanel.show()
self.memoryPanel.basicPanel.show()
self.calldataPanel.basicPanel.show()
self.callstackPanel.basicPanel.show()
})
_parent.event.register('traceUnloaded', this, function () {
if (!self.view) return
})
_parent.callTree.event.register('callTreeReady', () => {
if (!self.view) return
if (_parent.callTree.reducedTrace.length) {
self.solidityLocals.basicPanel.show()
self.solidityState.basicPanel.show()
}
})
}
VmDebugger.prototype.renderHead = function () {
var headView = yo`<div id='vmheadView' class=${css.vmheadView}>
<div>
<div class=${css.asmCode}>${this.asmCode.render()}</div>
<div class=${css.stepDetail}>${this.stepDetail.render()}</div>
</div>
</div>`
if (!this.headView) {
this.headView = headView
}
return headView
}
VmDebugger.prototype.remove = function () {
// used to stop listenning on event. bad and should be "refactored"
this.view = null
}
VmDebugger.prototype.render = function () {
var view = yo`<div id='vmdebugger'>
<div>
${this.solidityLocals.render()}
${this.solidityState.render()}
${this.stackPanel.render()}
${this.memoryPanel.render()}
${this.storagePanel.render()}
${this.callstackPanel.render()}
${this.calldataPanel.render()}
${this.returnValuesPanel.render()}
${this.fullStoragesChangesPanel.render()}
</div>
</div>`
if (!this.view) {
this.view = view
}
return view
}
module.exports = VmDebugger

@ -2,16 +2,23 @@ var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var Debugger = require('../debugger/debugger') var DebuggerUI = require('../debugger/debuggerUI')
var SourceHighlighter = require('../editor/sourceHighlighter')
var executionContext = require('../../execution-context')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var styles = require('../ui/styles-guide/theme-chooser').chooser() var styles = require('../ui/styles-guide/theme-chooser').chooser()
module.exports = class DebuggerTab { const css = csjs`
.debuggerTabView {
padding: 2%;
}
.debugger {
margin-bottom: 1%;
${styles.rightPanel.debuggerTab.box_Debugger}
}
`
class DebuggerTab {
constructor (localRegistry) { constructor (localRegistry) {
const self = this const self = this
self.event = new EventManager() self.event = new EventManager()
@ -20,6 +27,7 @@ module.exports = class DebuggerTab {
self._components = {} self._components = {}
self._components.registry = localRegistry || globalRegistry self._components.registry = localRegistry || globalRegistry
} }
render () { render () {
const self = this const self = this
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
@ -29,23 +37,15 @@ module.exports = class DebuggerTab {
<div id="debugger" class="${css.debugger}"></div> <div id="debugger" class="${css.debugger}"></div>
</div>` </div>`
self._view.transactionDebugger = new Debugger(self._view.el.querySelector('#debugger'), new SourceHighlighter()) this.debuggerUI = new DebuggerUI(self._view.el.querySelector('#debugger'))
self._view.transactionDebugger.addProvider('vm', executionContext.vm()) // self._view.transactionDebugger = this.debuggerUI.view()
self._view.transactionDebugger.addProvider('injected', executionContext.internalWeb3())
self._view.transactionDebugger.addProvider('web3', executionContext.internalWeb3())
self._view.transactionDebugger.switchProvider(executionContext.getProvider())
return self._view.el return self._view.el
} }
debugger () { debugger () {
return this._view.transactionDebugger // return this._view.transactionDebugger
return this.debuggerUI
} }
} }
const css = csjs`
.debuggerTabView { module.exports = DebuggerTab
padding: 2%;
}
.debugger {
margin-bottom: 1%;
${styles.rightPanel.debuggerTab.box_Debugger}
}
`

@ -10,7 +10,7 @@ var globalRegistry = require('../global/registry')
var SourceHighlighter = require('../app/editor/sourceHighlighter') var SourceHighlighter = require('../app/editor/sourceHighlighter')
var RemixDebug = require('remix-debug').EthDebugger var RemixDebug = require('remix-debug').EthDebugger
var TreeView = require('../app/ui/TreeView') // TODO setup a direct reference to the UI components var TreeView = require('../app/ui/TreeView') // TODO setup a direct reference to the UI components
var solidityTypeFormatter = require('../app/debugger/remix-debugger/src/ui/SolidityTypeFormatter') var solidityTypeFormatter = require('../app/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter')
class CmdInterpreterAPI { class CmdInterpreterAPI {
constructor (terminal, localRegistry) { constructor (terminal, localRegistry) {

@ -43,9 +43,9 @@ function runTests (browser, testData) {
{types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null) {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null)
.pause(500) .pause(500)
.click('span#tx0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e div[class^="debug"]') .click('span#tx0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e div[class^="debug"]')
.pause(1000) .pause(2000)
.click('#jumppreviousbreakpoint') .click('#jumppreviousbreakpoint')
.pause(500) .pause(2000)
.perform(function (client, done) { .perform(function (client, done) {
console.log('goToVMtraceStep') console.log('goToVMtraceStep')
contractHelper.goToVMtraceStep(browser, 47, () => { contractHelper.goToVMtraceStep(browser, 47, () => {

Loading…
Cancel
Save