diff --git a/src/app/debugger/debuggerUI/VmDebugger.js b/src/app/debugger/debuggerUI/VmDebugger.js index aaf777f848..ca32a31073 100644 --- a/src/app/debugger/debuggerUI/VmDebugger.js +++ b/src/app/debugger/debuggerUI/VmDebugger.js @@ -7,10 +7,10 @@ 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('../remix-debugger/src/ui/SolidityLocals') var FullStoragesChangesPanel = require('../remix-debugger/src/ui/FullStoragesChanges') var DropdownPanel = require('../remix-debugger/src/ui/DropdownPanel') -var SolidityState = require('../remix-debugger/src/ui/SolidityState') -var SolidityLocals = require('../remix-debugger/src/ui/SolidityLocals') var remixDebug = require('remix-debug') var remixLib = require('remix-lib') var ui = remixLib.helpers.ui diff --git a/src/app/debugger/debuggerUI/vmDebugger/SolidityState.js b/src/app/debugger/debuggerUI/vmDebugger/SolidityState.js new file mode 100644 index 0000000000..74dcba9548 --- /dev/null +++ b/src/app/debugger/debuggerUI/vmDebugger/SolidityState.js @@ -0,0 +1,103 @@ +var DropdownPanel = require('./DropdownPanel') +var remixDebug = require('remix-debug') +var stateDecoder = remixDebug.SolidityDecoder.stateDecoder +var solidityTypeFormatter = require('./utils/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`
+ ${this.basicPanel.render()} +
` + } + 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 + diff --git a/src/app/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js b/src/app/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js new file mode 100644 index 0000000000..f24a939098 --- /dev/null +++ b/src/app/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js @@ -0,0 +1,71 @@ +var yo = require('yo-yo') +var BN = require('ethereumjs-util').BN + +module.exports = { + formatSelf: formatSelf, + extractData: extractData +} + +function formatSelf (key, data) { + var style = fontColor(data) + var keyStyle = data.isProperty ? 'color:#847979' : '' + if (data.type === 'string') { + data.self = JSON.stringify(data.self) + } + return yo`` +} + +function extractData (item, parent, key) { + var ret = {} + if (item.isProperty) { + return item + } + if (item.type.lastIndexOf(']') === item.type.length - 1) { + ret.children = (item.value || []).map(function (item, index) { + return {key: index, value: item} + }) + ret.children.unshift({ + key: 'length', + value: { + self: (new BN(item.length.replace('0x', ''), 16)).toString(10), + type: 'uint', + isProperty: true + } + }) + ret.isArray = true + ret.self = parent.isArray ? '' : item.type + } else if (item.type.indexOf('struct') === 0) { + ret.children = Object.keys((item.value || {})).map(function (key) { + return {key: key, value: item.value[key]} + }) + ret.self = item.type + ret.isStruct = true + } else if (item.type.indexOf('mapping') === 0) { + ret.children = Object.keys((item.value || {})).map(function (key) { + return {key: key, value: item.value[key]} + }) + ret.isMapping = true + ret.self = item.type + } else { + ret.children = null + ret.self = item.value + ret.type = item.type + } + return ret +} + +function fontColor (data) { + var color = '#124B46' + if (data.isArray || data.isStruct || data.isMapping) { + color = '#847979' + } else if (data.type.indexOf('uint') === 0 || + data.type.indexOf('int') === 0 || + data.type.indexOf('bool') === 0 || + data.type.indexOf('enum') === 0) { + color = '#0F0CE9' + } else if (data.type === 'string') { + color = '#E91E0C' + } + return 'color:' + color +} +