commit
6d0de8aad5
@ -0,0 +1,94 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var style = require('./basicStyles') |
||||
var codeResolver = require('./codeResolver') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object, |
||||
tx: React.PropTypes.object, |
||||
web3: React.PropTypes.object |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
code: [], |
||||
selected: -1, |
||||
address: '' // selected instruction in the asm
|
||||
} |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<select |
||||
size='10' |
||||
ref='itemsList' |
||||
style={style.instructionsList} |
||||
value={this.state.selected}> |
||||
{this.renderAssemblyItems()} |
||||
</select> |
||||
) |
||||
}, |
||||
|
||||
renderAssemblyItems: function () { |
||||
if (this.state.code) { |
||||
return this.state.code.map(function (item, i) { |
||||
return <option key={i} value={i}>{item}</option> |
||||
}) |
||||
} |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
codeResolver.setWeb3(this.context.web3) |
||||
var self = this |
||||
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
self.ensureCodeLoaded(address, nextProps.currentStepIndex) |
||||
} |
||||
}) |
||||
}, |
||||
|
||||
ensureCodeLoaded: function (address, currentStep) { |
||||
if (address !== this.state.address) { |
||||
this.setState({ |
||||
code: ['loading...'] |
||||
}) |
||||
var self = this |
||||
codeResolver.resolveCode(address, currentStep, this.context.tx, function (address, code) { |
||||
if (window.ethDebuggerSelectedItem !== currentStep) { |
||||
console.log(currentStep + ' discarded. current is ' + window.ethDebuggerSelectedItem) |
||||
return |
||||
} |
||||
self.setState({ |
||||
code: code, |
||||
address: address |
||||
}) |
||||
self.setInstructionIndex(address, currentStep) |
||||
}) |
||||
} else { |
||||
this.setInstructionIndex(this.state.address, currentStep) |
||||
} |
||||
}, |
||||
|
||||
setInstructionIndex: function (address, step) { |
||||
var self = this |
||||
this.context.traceManager.getCurrentPC(step, function (error, instIndex) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
self.setState({ |
||||
selected: codeResolver.getInstructionIndex(address, instIndex) |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
}) |
@ -1,370 +0,0 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var BasicPanel = require('./basicPanel') |
||||
var Sticker = require('./sticker') |
||||
var ButtonNavigator = require('./vmTraceButtonNavigator') |
||||
var codeUtils = require('./codeUtils') |
||||
var style = require('./basicStyles') |
||||
var Slider = require('./slider') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
web3: React.PropTypes.object |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
currentSelected: -1, // current selected item in the vmTrace
|
||||
selectedInst: -1, // current selected item in the contract assembly code
|
||||
currentAddress: null, |
||||
currentStack: null, |
||||
currentLevels: null, |
||||
currentStorage: null, |
||||
currentMemory: null, |
||||
currentCallData: null, |
||||
currentStepInfo: null, |
||||
codes: {}, // assembly items instructions list by contract addesses
|
||||
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
|
||||
callStack: {} |
||||
} |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
vmTrace: null |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<div style={this.props.vmTrace === null ? style.hidden : style.display}> |
||||
<div style={style.container}> |
||||
<span style={style.address}>Current code: {this.state.currentAddress}</span> |
||||
</div> |
||||
<div style={style.container}> |
||||
<Slider |
||||
ref='slider' |
||||
onChange={this.selectState} |
||||
min='0' |
||||
max={this.props.vmTrace ? this.props.vmTrace.length : 0} /> |
||||
<ButtonNavigator |
||||
vmTraceLength={this.props.vmTrace ? this.props.vmTrace.length : 0} |
||||
step={this.state.currentSelected} |
||||
stepIntoBack={this.stepIntoBack} |
||||
stepIntoForward={this.stepIntoForward} |
||||
stepOverBack={this.stepOverBack} |
||||
stepOverForward={this.stepOverForward} /> |
||||
</div> |
||||
<div style={style.container}> |
||||
<table> |
||||
<tbody> |
||||
<tr> |
||||
<td> |
||||
<select |
||||
size='10' |
||||
ref='itemsList' |
||||
style={style.instructionsList} |
||||
value={this.state.selectedInst}> |
||||
{this.renderAssemblyItems()} |
||||
</select> |
||||
<div style={Object.assign(style.inline, style.sticker)}> |
||||
<Sticker data={this.state.currentStepInfo} /> |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<BasicPanel name='CallData' data={this.state.currentCallData} /> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<BasicPanel name='Stack' data={this.state.currentStack} /> |
||||
</td> |
||||
<td> |
||||
<BasicPanel name='CallStack' data={this.state.currentCallStack} /> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<BasicPanel name='Storage' data={this.state.currentStorage} renderRow={this.renderStorageRow} /> |
||||
</td> |
||||
<td> |
||||
<BasicPanel name='Memory' data={this.state.currentMemory} renderRow={this.renderMemoryRow} /> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
) |
||||
}, |
||||
|
||||
renderStorageRow: function (data) { |
||||
var ret = [] |
||||
if (data) { |
||||
for (var key in data) { |
||||
ret.push( |
||||
<tr key={key}> |
||||
<td> |
||||
{key} |
||||
</td> |
||||
<td> |
||||
{data[key]} |
||||
</td> |
||||
</tr>) |
||||
} |
||||
} |
||||
return ret |
||||
}, |
||||
|
||||
renderMemoryRow: function (data) { |
||||
var ret = [] |
||||
if (data) { |
||||
for (var key in data) { |
||||
var memSlot = data[key] |
||||
ret.push( |
||||
<tr key={key}> |
||||
<td> |
||||
{memSlot.address} |
||||
</td> |
||||
<td> |
||||
{memSlot.content.raw} |
||||
</td> |
||||
<td> |
||||
{memSlot.content.ascii} |
||||
</td> |
||||
</tr>) |
||||
} |
||||
} |
||||
return ret |
||||
}, |
||||
|
||||
resolveAddress: function (address) { |
||||
if (!this.state.codes[address]) { |
||||
var hexCode = this.context.web3.eth.getCode(address) |
||||
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex')) |
||||
this.state.codes[address] = code[0] |
||||
this.state.instructionsIndexByBytesOffset[address] = code[1] |
||||
} |
||||
}, |
||||
|
||||
renderAssemblyItems: function () { |
||||
if (this.props.vmTrace) { |
||||
return this.state.codes[this.state.currentAddress].map(function (item, i) { |
||||
return <option key={i} value={i}>{item}</option> |
||||
}) |
||||
} |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (!nextProps.vmTrace) { |
||||
return |
||||
} |
||||
this.buildCallStack(nextProps.vmTrace) |
||||
this.setState({'currentSelected': -1}) |
||||
this.updateState(nextProps, 0) |
||||
}, |
||||
|
||||
buildCallStack: function (vmTrace) { |
||||
if (!vmTrace) { |
||||
return |
||||
} |
||||
var callStack = [] |
||||
var depth = -1 |
||||
for (var k = 0; k < vmTrace.length; k++) { |
||||
var trace = vmTrace[k] |
||||
if (trace.depth === undefined || trace.depth === depth) { |
||||
continue |
||||
} |
||||
if (trace.depth > depth) { |
||||
callStack.push(trace.address) // new context
|
||||
} else if (trace.depth < depth) { |
||||
callStack.pop() // returning from context
|
||||
} |
||||
depth = trace.depth |
||||
this.state.callStack[k] = callStack.slice(0) |
||||
} |
||||
}, |
||||
|
||||
updateState: function (props, vmTraceIndex) { |
||||
if (!props.vmTrace || !props.vmTrace[vmTraceIndex]) { |
||||
return |
||||
} |
||||
var previousIndex = this.state.currentSelected |
||||
var stateChanges = {} |
||||
|
||||
if (props.vmTrace[vmTraceIndex].stack) { // there's always a stack
|
||||
var stack = props.vmTrace[vmTraceIndex].stack |
||||
stack.reverse() |
||||
stateChanges['currentStack'] = stack |
||||
} |
||||
|
||||
var currentAddress = this.state.currentAddress |
||||
var addressIndex = this.shouldUpdateStateProperty('address', vmTraceIndex, previousIndex, props.vmTrace) |
||||
if (addressIndex > -1) { |
||||
currentAddress = props.vmTrace[addressIndex].address |
||||
this.resolveAddress(currentAddress) |
||||
Object.assign(stateChanges, { 'currentAddress': currentAddress }) |
||||
} |
||||
|
||||
var depthIndex = this.shouldUpdateStateProperty('depth', vmTraceIndex, previousIndex, props.vmTrace) |
||||
if (depthIndex > -1) { |
||||
Object.assign(stateChanges, { 'currentCallStack': this.state.callStack[depthIndex] }) |
||||
} |
||||
|
||||
var storageIndex = this.shouldUpdateStateProperty('storage', vmTraceIndex, previousIndex, props.vmTrace) |
||||
if (storageIndex > -1) { |
||||
Object.assign(stateChanges, { 'currentStorage': props.vmTrace[storageIndex].storage }) |
||||
} |
||||
|
||||
var memoryIndex = this.shouldUpdateStateProperty('memory', vmTraceIndex, previousIndex, props.vmTrace) |
||||
if (memoryIndex > -1) { |
||||
Object.assign(stateChanges, { 'currentMemory': this.formatMemory(props.vmTrace[memoryIndex].memory, 16) }) |
||||
} |
||||
|
||||
var callDataIndex = this.shouldUpdateStateProperty('calldata', vmTraceIndex, previousIndex, props.vmTrace) |
||||
if (callDataIndex > -1) { |
||||
Object.assign(stateChanges, { 'currentCallData': [props.vmTrace[callDataIndex].calldata] }) |
||||
} |
||||
|
||||
stateChanges['selectedInst'] = this.state.instructionsIndexByBytesOffset[currentAddress][props.vmTrace[vmTraceIndex].pc] |
||||
stateChanges['currentSelected'] = vmTraceIndex |
||||
|
||||
stateChanges['currentStepInfo'] = [ |
||||
'Current Step: ' + props.vmTrace[vmTraceIndex].steps, |
||||
'Adding Memory: ' + (props.vmTrace[vmTraceIndex].memexpand ? props.vmTrace[vmTraceIndex].memexpand : ''), |
||||
'Step Cost: ' + props.vmTrace[vmTraceIndex].gascost, |
||||
'Remaining Gas: ' + props.vmTrace[vmTraceIndex].gas |
||||
] |
||||
this.refs.slider.setValue(vmTraceIndex) |
||||
this.setState(stateChanges) |
||||
}, |
||||
|
||||
shouldUpdateStateProperty: function (vmTraceName, nextIndex, previousIndex, vmTrace) { |
||||
var propIndex = -1 |
||||
if (previousIndex + 1 === nextIndex) { |
||||
propIndex = nextIndex |
||||
} else { |
||||
propIndex = this.retrieveLastSeenProperty(nextIndex, vmTraceName, vmTrace) |
||||
} |
||||
|
||||
if (propIndex > -1 && vmTrace[propIndex][vmTraceName] !== undefined) { |
||||
return propIndex |
||||
} else { |
||||
return -1 |
||||
} |
||||
}, |
||||
|
||||
retrieveLastSeenProperty: function (currentIndex, propertyName, vmTrace) { |
||||
var index = currentIndex |
||||
while (index > 0) { |
||||
if (vmTrace[index][propertyName]) { |
||||
break |
||||
} |
||||
index-- |
||||
} |
||||
return index |
||||
}, |
||||
|
||||
stepIntoBack: function () { |
||||
this.moveSelection(-1) |
||||
}, |
||||
|
||||
stepIntoForward: function () { |
||||
this.moveSelection(1) |
||||
}, |
||||
|
||||
stepOverBack: function () { |
||||
if (this.isReturnInstruction(this.state.currentSelected - 1)) { |
||||
this.stepOutBack() |
||||
} else { |
||||
this.moveSelection(-1) |
||||
} |
||||
}, |
||||
|
||||
stepOverForward: function () { |
||||
if (this.isCallInstruction(this.state.currentSelected)) { |
||||
this.stepOutForward() |
||||
} else { |
||||
this.moveSelection(1) |
||||
} |
||||
}, |
||||
|
||||
isCallInstruction: function (index) { |
||||
var state = this.props.vmTrace[index] |
||||
return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL' |
||||
}, |
||||
|
||||
isReturnInstruction: function (index) { |
||||
var state = this.props.vmTrace[index] |
||||
return state.instname === 'RETURN' |
||||
}, |
||||
|
||||
stepOutBack: function () { |
||||
var i = this.state.currentSelected - 1 |
||||
var depth = 0 |
||||
while (--i >= 0) { |
||||
if (this.isCallInstruction(i)) { |
||||
if (depth === 0) { |
||||
break |
||||
} else { |
||||
depth-- |
||||
} |
||||
} else if (this.isReturnInstruction(i)) { |
||||
depth++ |
||||
} |
||||
} |
||||
this.selectState(i) |
||||
}, |
||||
|
||||
stepOutForward: function () { |
||||
var i = this.state.currentSelected |
||||
var depth = 0 |
||||
while (++i < this.props.vmTrace.length) { |
||||
if (this.isReturnInstruction(i)) { |
||||
if (depth === 0) { |
||||
break |
||||
} else { |
||||
depth-- |
||||
} |
||||
} else if (this.isCallInstruction(i)) { |
||||
depth++ |
||||
} |
||||
} |
||||
this.selectState(i + 1) |
||||
}, |
||||
|
||||
moveSelection: function (incr) { |
||||
this.selectState(this.state.currentSelected + incr) |
||||
}, |
||||
|
||||
selectState: function (index) { |
||||
this.updateState(this.props, index) |
||||
}, |
||||
|
||||
formatMemory: function (mem, width) { |
||||
var ret = [] |
||||
for (var k = 0; k < mem.length; k += (width * 2)) { |
||||
var memory = mem.substr(k, width * 2) |
||||
ret.push({ |
||||
address: this.context.web3.toHex(k), |
||||
content: this.tryAsciiFormat(memory) |
||||
}) |
||||
} |
||||
return ret |
||||
}, |
||||
|
||||
tryAsciiFormat: function (memorySlot) { |
||||
var ret = { ascii: '', raw: '' } |
||||
for (var k = 0; k < memorySlot.length; k += 2) { |
||||
var raw = memorySlot.substr(k, 2) |
||||
var ascii = this.context.web3.toAscii(raw) |
||||
if (ascii === String.fromCharCode(0)) { |
||||
ret.ascii += '?' |
||||
} else { |
||||
ret.ascii += ascii |
||||
} |
||||
ret.raw += ' ' + raw |
||||
} |
||||
return ret |
||||
} |
||||
}) |
@ -0,0 +1,43 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var BasicPanel = require('./basicPanel') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 |
||||
} |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
data: null |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<BasicPanel name='CallData' data={this.state.data} /> |
||||
) |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return |
||||
|
||||
var self = this |
||||
this.context.traceManager.getCallDataAt(nextProps.currentStepIndex, function (error, calldata) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { |
||||
self.setState({ |
||||
data: calldata |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
}) |
@ -0,0 +1,43 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var BasicPanel = require('./basicPanel') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 |
||||
} |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
data: null |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<BasicPanel name='CallStack' data={this.state.data} /> |
||||
) |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return |
||||
|
||||
var self = this |
||||
this.context.traceManager.getCallStackAt(nextProps.currentStepIndex, function (error, callstack) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { |
||||
self.setState({ |
||||
data: callstack |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
}) |
@ -0,0 +1,67 @@ |
||||
'use strict' |
||||
var codeUtils = require('./codeUtils') |
||||
|
||||
module.exports = { |
||||
web3: null, |
||||
|
||||
codes: {}, // assembly items instructions list by contract addesses
|
||||
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
|
||||
|
||||
setWeb3: function (web3) { |
||||
this.web3 = web3 |
||||
}, |
||||
|
||||
resolveCode: function (address, vmTraceIndex, transaction, callBack) { |
||||
var cache = this.getExecutingCodeFromCache(address) |
||||
if (cache) { |
||||
callBack(address, cache.code) |
||||
return |
||||
} |
||||
|
||||
if (vmTraceIndex === 0 && transaction.to === null) { // start of the trace
|
||||
callBack(address, this.cacheExecutingCode(address, transaction.input).code) |
||||
return |
||||
} |
||||
|
||||
var self = this |
||||
this.loadCode(address, function (code) { |
||||
callBack(address, self.cacheExecutingCode(address, code).code) |
||||
}) |
||||
}, |
||||
|
||||
loadCode: function (address, callback) { |
||||
console.log('loading new code from web3 ' + address) |
||||
this.web3.eth.getCode(address, function (error, result) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
callback(result) |
||||
} |
||||
}) |
||||
}, |
||||
|
||||
cacheExecutingCode: function (address, hexCode) { |
||||
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex')) |
||||
this.codes[address] = code[0] |
||||
this.instructionsIndexByBytesOffset[address] = code[1] |
||||
return { |
||||
code: code[0], |
||||
instructionsIndexByBytesOffset: code[1] |
||||
} |
||||
}, |
||||
|
||||
getExecutingCodeFromCache: function (address) { |
||||
if (this.codes[address]) { |
||||
return { |
||||
code: this.codes[address], |
||||
instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address] |
||||
} |
||||
} else { |
||||
return null |
||||
} |
||||
}, |
||||
|
||||
getInstructionIndex: function (address, pc) { |
||||
return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc] |
||||
} |
||||
} |
@ -1,44 +1,72 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var TxBrowser = require('./txBrowser') |
||||
var VmTraceBrowser = require('./vmTraceBrowser') |
||||
var StepManager = require('./stepManager') |
||||
var AssemblyItemsBrowser = require('./vmDebugger') |
||||
var TraceManager = require('./traceManager') |
||||
var style = require('./basicStyles') |
||||
|
||||
module.exports = React.createClass({ |
||||
getInitialState: function () { |
||||
return {vmTrace: null, state: '', currentStep: -1} |
||||
return { |
||||
currentStepIndex: -1, // index of the selected item in the vmtrace
|
||||
tx: null, |
||||
traceManager: null |
||||
} |
||||
}, |
||||
|
||||
childContextTypes: { |
||||
web3: React.PropTypes.object |
||||
web3: React.PropTypes.object, |
||||
traceManager: React.PropTypes.object, |
||||
tx: React.PropTypes.object |
||||
}, |
||||
|
||||
getChildContext: function () { |
||||
return { web3: this.props.web3 } |
||||
return { |
||||
web3: this.props.web3, |
||||
traceManager: this.state.traceManager, |
||||
tx: this.state.tx |
||||
} |
||||
}, |
||||
|
||||
componentDidMount: function () { |
||||
this.setState({ |
||||
traceManager: new TraceManager(this.props.web3) |
||||
}) |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<div style={style.wrapper}> |
||||
<div style={style.font}> |
||||
<h1 style={style.container}>Eth Debugger</h1> |
||||
<TxBrowser onNewTxRequested={this.retrieveVmTrace} /> |
||||
<div style={style.container}> |
||||
{this.state.state} |
||||
</div> |
||||
<VmTraceBrowser vmTrace={this.state.vmTrace} /> |
||||
<TxBrowser onNewTxRequested={this.startDebugging} /> |
||||
<StepManager ref='stepManager' onStepChanged={this.stepChanged} /> |
||||
<AssemblyItemsBrowser ref='assemblyitemsbrowser' currentStepIndex={this.state.currentStepIndex} /> |
||||
</div> |
||||
) |
||||
}, |
||||
|
||||
retrieveVmTrace: function (blockNumber, txNumber) { |
||||
this.setState({state: 'loading...'}) |
||||
stepChanged: function (stepIndex) { |
||||
this.setState({ |
||||
currentStepIndex: stepIndex |
||||
}) |
||||
}, |
||||
|
||||
startDebugging: function (blockNumber, txIndex, tx) { |
||||
if (this.state.traceManager.isLoading) { |
||||
return |
||||
} |
||||
console.log('loading trace...') |
||||
this.setState({ |
||||
tx: tx |
||||
}) |
||||
var self = this |
||||
this.props.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
self.setState({vmTrace: result, state: ''}) |
||||
} |
||||
this.state.traceManager.resolveTrace(blockNumber, txIndex, function (success) { |
||||
console.log('trace loaded ' + success) |
||||
self.setState({ |
||||
currentStepIndex: 0 |
||||
}) |
||||
self.refs.stepManager.newTraceAvailable() |
||||
}) |
||||
} |
||||
}) |
||||
|
@ -0,0 +1,94 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var BasicPanel = require('./basicPanel') |
||||
var style = require('./basicStyles') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object, |
||||
web3: React.PropTypes.object |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 |
||||
} |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
data: null |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<BasicPanel name='Memory' data={this.state.data} renderRow={this.renderMemoryRow} /> |
||||
) |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return |
||||
|
||||
var self = this |
||||
this.context.traceManager.getMemoryAt(nextProps.currentStepIndex, function (error, memory) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { |
||||
self.setState({ |
||||
data: self.formatMemory(memory, 16) |
||||
}) |
||||
} |
||||
}) |
||||
}, |
||||
|
||||
renderMemoryRow: function (data) { |
||||
var ret = [] |
||||
if (data) { |
||||
for (var key in data) { |
||||
var memSlot = data[key] |
||||
ret.push( |
||||
<tr key={key}> |
||||
<td> |
||||
<pre style={style.font}>{memSlot.address}</pre> |
||||
</td> |
||||
<td> |
||||
<pre style={style.font}>{memSlot.content.raw}</pre> |
||||
</td> |
||||
<td> |
||||
<pre style={style.font}>{memSlot.content.ascii}</pre> |
||||
</td> |
||||
</tr>) |
||||
} |
||||
} |
||||
return ret |
||||
}, |
||||
|
||||
formatMemory: function (mem, width) { |
||||
var ret = [] |
||||
for (var k = 0; k < mem.length; k += (width * 2)) { |
||||
var memory = mem.substr(k, width * 2) |
||||
ret.push({ |
||||
address: this.context.web3.toHex(k), |
||||
content: this.tryAsciiFormat(memory) |
||||
}) |
||||
} |
||||
return ret |
||||
}, |
||||
|
||||
tryAsciiFormat: function (memorySlot) { |
||||
var ret = { ascii: '', raw: '' } |
||||
for (var k = 0; k < memorySlot.length; k += 2) { |
||||
var raw = memorySlot.substr(k, 2) |
||||
var ascii = this.context.web3.toAscii(raw) |
||||
if (ascii === String.fromCharCode(0)) { |
||||
ret.ascii += '?' |
||||
} else { |
||||
ret.ascii += ascii |
||||
} |
||||
ret.raw += ' ' + raw |
||||
} |
||||
return ret |
||||
} |
||||
}) |
@ -0,0 +1,43 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var BasicPanel = require('./basicPanel') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 |
||||
} |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
data: null |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<BasicPanel name='Stack' data={this.state.data} /> |
||||
) |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return |
||||
|
||||
var self = this |
||||
this.context.traceManager.getStackAt(nextProps.currentStepIndex, function (error, stack) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { |
||||
self.setState({ |
||||
data: stack |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
}) |
@ -0,0 +1,111 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var ButtonNavigator = require('./buttonNavigator') |
||||
var Slider = require('./slider') |
||||
var style = require('./basicStyles') |
||||
|
||||
module.exports = React.createClass({ |
||||
propTypes: { |
||||
onStepChanged: React.PropTypes.func.isRequired |
||||
}, |
||||
|
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
currentStepIndex: 0, |
||||
traceLength: 0 |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<div style={style.container}> |
||||
<Slider |
||||
ref='slider' |
||||
onChange={this.sliderMoved} |
||||
min='0' |
||||
max={this.state.traceLength} /> |
||||
<ButtonNavigator |
||||
stepIntoBack={this.stepIntoBack} |
||||
stepIntoForward={this.stepIntoForward} |
||||
stepOverBack={this.stepOverBack} |
||||
stepOverForward={this.stepOverForward} |
||||
jumpToNextCall={this.jumpToNextCall} |
||||
max={this.state.traceLength} /> |
||||
</div> |
||||
) |
||||
}, |
||||
|
||||
componentDidMount: function () { |
||||
this.updateGlobalSelectedItem(0) |
||||
}, |
||||
|
||||
updateGlobalSelectedItem: function (value) { |
||||
window.ethDebuggerSelectedItem = value |
||||
}, |
||||
|
||||
init: function () { |
||||
this.refs.slider.setValue(0) |
||||
}, |
||||
|
||||
newTraceAvailable: function () { |
||||
this.init() |
||||
var self = this |
||||
this.context.traceManager.getLength(function (error, length) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
self.setState({ traceLength: length }) |
||||
} |
||||
}) |
||||
}, |
||||
|
||||
sliderMoved: function (step) { |
||||
this.props.onStepChanged(step) |
||||
this.changeState(step) |
||||
}, |
||||
|
||||
stepIntoForward: function () { |
||||
var step = this.state.currentStepIndex + 1 |
||||
this.props.onStepChanged(step) |
||||
this.changeState(step) |
||||
}, |
||||
|
||||
stepIntoBack: function () { |
||||
var step = this.state.currentStepIndex - 1 |
||||
this.props.onStepChanged(step) |
||||
this.refs.slider.setValue(step) |
||||
this.changeState(step) |
||||
}, |
||||
|
||||
stepOverForward: function () { |
||||
var step = this.context.traceManager.findStepOverForward(this.state.currentStepIndex) |
||||
this.props.onStepChanged(step) |
||||
this.refs.slider.setValue(step) |
||||
this.changeState(step) |
||||
}, |
||||
|
||||
stepOverBack: function () { |
||||
var step = this.context.traceManager.findStepOverBack(this.state.currentStepIndex) |
||||
this.props.onStepChanged(step) |
||||
this.refs.slider.setValue(step) |
||||
this.changeState(step) |
||||
}, |
||||
|
||||
jumpToNextCall: function () { |
||||
var step = this.context.traceManager.findNextCall(this.state.currentStepIndex) |
||||
this.props.onStepChanged(step) |
||||
this.refs.slider.setValue(step) |
||||
this.changeState(step) |
||||
}, |
||||
|
||||
changeState: function (step) { |
||||
this.updateGlobalSelectedItem(step) |
||||
this.setState({ |
||||
currentStepIndex: step |
||||
}) |
||||
} |
||||
}) |
@ -0,0 +1,63 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var BasicPanel = require('./basicPanel') |
||||
var style = require('./basicStyles') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object, |
||||
tx: React.PropTypes.object |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 |
||||
} |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
data: null |
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<BasicPanel name='Storage' data={this.state.data} renderRow={this.renderStorageRow} /> |
||||
) |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return |
||||
|
||||
var self = this |
||||
this.context.traceManager.getStorageAt(nextProps.currentStepIndex, this.context.tx.blockNumber.toString(), this.context.tx.transactionIndex, function (error, storage) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { |
||||
self.setState({ |
||||
data: storage |
||||
}) |
||||
} |
||||
}) |
||||
}, |
||||
|
||||
renderStorageRow: function (data) { |
||||
var ret = [] |
||||
if (data) { |
||||
for (var key in data) { |
||||
ret.push( |
||||
<tr key={key}> |
||||
<td> |
||||
<pre style={style.font} >{key}</pre> |
||||
</td> |
||||
<td> |
||||
<pre style={style.font}>{data[key]}</pre> |
||||
</td> |
||||
</tr>) |
||||
} |
||||
} |
||||
return ret |
||||
} |
||||
}) |
@ -0,0 +1,77 @@ |
||||
'use strict' |
||||
function TraceAnalyser (_cache) { |
||||
this.traceCache = _cache |
||||
this.trace = null |
||||
} |
||||
|
||||
TraceAnalyser.prototype.analyse = function (trace, callback) { |
||||
this.trace = trace |
||||
var currentDepth = 0 |
||||
var context = { |
||||
currentStorageAddress: trace[0].address, |
||||
previousStorageAddress: trace[0].address |
||||
} |
||||
var callStack = [] |
||||
for (var k in this.trace) { |
||||
var step = this.trace[k] |
||||
this.buildCalldata(k, step) |
||||
this.buildMemory(k, step) |
||||
var depth = this.buildDepth(k, step, currentDepth, callStack) |
||||
if (depth) { |
||||
currentDepth = depth |
||||
} |
||||
context = this.buildStorage(k, step, context) |
||||
} |
||||
callback(null, true) |
||||
} |
||||
|
||||
TraceAnalyser.prototype.buildCalldata = function (index, step) { |
||||
if (step.calldata) { |
||||
this.traceCache.pushCallDataChanges(index) |
||||
} |
||||
} |
||||
|
||||
TraceAnalyser.prototype.buildMemory = function (index, step) { |
||||
if (step.memory) { |
||||
this.traceCache.pushMemoryChanges(index) |
||||
} |
||||
} |
||||
|
||||
TraceAnalyser.prototype.buildStorage = function (index, step, context) { |
||||
if (step.address) { |
||||
// new context
|
||||
context.currentStorageAddress = step.address |
||||
this.traceCache.pushStoreChanges(index, context.currentStorageAddress) |
||||
} else if (step.inst === 'SSTORE') { |
||||
this.traceCache.pushStoreChanges(index, context.currentStorageAddress, step.stack[step.stack.length - 1], step.stack[step.stack.length - 2]) |
||||
} else if (!step.address && step.depth) { |
||||
// returned from context
|
||||
context.currentStorageAddress = context.previousStorageAddress |
||||
this.traceCache.pushStoreChanges(index, context.currentStorageAddress) |
||||
} |
||||
return context |
||||
} |
||||
|
||||
TraceAnalyser.prototype.buildDepth = function (index, step, currentDepth, callStack) { |
||||
if (step.depth === undefined) return |
||||
if (step.depth > currentDepth) { |
||||
if (index === 0) { |
||||
callStack.push('0x' + step.address) // new context
|
||||
} else { |
||||
// getting the address from the stack
|
||||
var callTrace = this.trace[index - 1] |
||||
var address = callTrace.stack[callTrace.stack.length - 2] |
||||
callStack.push(address) // new context
|
||||
} |
||||
} else if (step.depth < currentDepth) { |
||||
callStack.pop() // returning from context
|
||||
} |
||||
this.traceCache.pushCallStack(index, { |
||||
stack: callStack.slice(0), |
||||
depth: step.depth |
||||
}) |
||||
this.traceCache.pushDepthChanges(index) |
||||
return step.depth |
||||
} |
||||
|
||||
module.exports = TraceAnalyser |
@ -0,0 +1,55 @@ |
||||
'use strict' |
||||
function TraceCache () { |
||||
this.init() |
||||
} |
||||
|
||||
TraceCache.prototype.init = function () { |
||||
// ...Changes contains index in the vmtrace of the corresponding changes
|
||||
this.depthChanges = [] |
||||
this.memoryChanges = [] |
||||
this.callDataChanges = [] |
||||
this.storageChanges = [] |
||||
this.sstore = {} // all sstore occurence in the trace
|
||||
this.callStack = {} // contains all callStack by vmtrace index (we need to rebuild it, callstack is not included in the vmtrace)
|
||||
} |
||||
|
||||
TraceCache.prototype.pushCallDataChanges = function (value) { |
||||
this.callDataChanges.push(value) |
||||
} |
||||
|
||||
TraceCache.prototype.pushMemoryChanges = function (value) { |
||||
this.memoryChanges.push(value) |
||||
} |
||||
|
||||
TraceCache.prototype.pushDepthChanges = function (value) { |
||||
this.depthChanges.push(value) |
||||
} |
||||
|
||||
TraceCache.prototype.pushCallStack = function (index, callStack) { |
||||
this.callStack[index] = callStack |
||||
} |
||||
|
||||
TraceCache.prototype.pushStoreChanges = function (index, address, key, value) { |
||||
this.sstore[index] = { |
||||
'address': address, |
||||
'key': key, |
||||
'value': value |
||||
} |
||||
this.storageChanges.push(index) |
||||
} |
||||
|
||||
TraceCache.prototype.rebuildStorage = function (address, storage, index) { |
||||
for (var k in this.storageChanges) { |
||||
var changesIndex = this.storageChanges[k] |
||||
if (changesIndex > index) { |
||||
return storage |
||||
} |
||||
var sstore = this.sstore[changesIndex] |
||||
if (sstore.address === address && sstore.key) { |
||||
storage[sstore.key] = sstore.value |
||||
} |
||||
} |
||||
return storage |
||||
} |
||||
|
||||
module.exports = TraceCache |
@ -0,0 +1,150 @@ |
||||
'use strict' |
||||
var TraceAnalyser = require('./traceAnalyser') |
||||
var TraceRetriever = require('./traceRetriever') |
||||
var TraceCache = require('./traceCache') |
||||
var TraceStepManager = require('./traceStepManager') |
||||
var traceManagerUtil = require('./traceManagerUtil') |
||||
|
||||
function TraceManager (_web3) { |
||||
this.web3 = _web3 |
||||
this.isLoading = false |
||||
this.trace = null |
||||
this.traceCache = new TraceCache() |
||||
this.traceAnalyser = new TraceAnalyser(this.traceCache) |
||||
this.traceRetriever = new TraceRetriever(_web3) |
||||
this.traceStepManager = new TraceStepManager(this.traceAnalyser) |
||||
} |
||||
|
||||
// init section
|
||||
TraceManager.prototype.resolveTrace = function (blockNumber, txNumber, callback) { |
||||
this.isLoading = true |
||||
this.init() |
||||
if (!this.web3) callback(false) |
||||
var self = this |
||||
this.traceRetriever.getTrace(blockNumber, parseInt(txNumber), function (error, result) { |
||||
self.trace = result |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
self.traceAnalyser.analyse(result, function (error, result) { |
||||
if (error) { |
||||
console.log(error) |
||||
callback(false) |
||||
} else { |
||||
callback(true) |
||||
} |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
TraceManager.prototype.init = function () { |
||||
this.trace = null |
||||
this.traceCache.init() |
||||
} |
||||
|
||||
// API section
|
||||
TraceManager.prototype.getLength = function (callback) { |
||||
if (!this.trace) callback('no trace available', null) |
||||
callback(null, this.trace.length) |
||||
} |
||||
|
||||
TraceManager.prototype.getStorageAt = function (stepIndex, blockNumber, txIndex, callback) { |
||||
var stoChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.storageChanges) |
||||
|
||||
var address = this.traceCache.sstore[stoChange].address |
||||
var self = this |
||||
this.traceRetriever.getStorage(blockNumber, txIndex, address, function (error, result) { |
||||
if (error) { |
||||
console.log(error) |
||||
callback(error, null) |
||||
} else { |
||||
var storage = self.traceCache.rebuildStorage(address, result, stepIndex) |
||||
callback(null, storage) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
TraceManager.prototype.getCallDataAt = function (stepIndex, callback) { |
||||
var callDataChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callDataChanges) |
||||
if (!callDataChange) return callback('no calldata found', null) |
||||
callback(null, [this.trace[callDataChange].calldata]) |
||||
} |
||||
|
||||
TraceManager.prototype.getCallStackAt = function (stepIndex, callback) { |
||||
var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) |
||||
if (!callStackChange) return callback('no callstack found', null) |
||||
callback(null, this.traceCache.callStack[callStackChange].stack) |
||||
} |
||||
|
||||
TraceManager.prototype.getStackAt = function (stepIndex, callback) { |
||||
var stack |
||||
if (this.trace[stepIndex].stack) { // there's always a stack
|
||||
stack = this.trace[stepIndex].stack.slice(0) |
||||
stack.reverse() |
||||
callback(null, stack) |
||||
} else { |
||||
callback('no stack found', null) |
||||
} |
||||
} |
||||
|
||||
TraceManager.prototype.getLastDepthIndexChangeSince = function (stepIndex, callback) { |
||||
var depthIndex = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) |
||||
callback(null, depthIndex) |
||||
} |
||||
|
||||
TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback) { |
||||
var self = this |
||||
this.getLastDepthIndexChangeSince(stepIndex, function (error, addressIndex) { |
||||
if (error) { |
||||
callback(error, null) |
||||
} else { |
||||
callback(null, traceManagerUtil.resolveCalledAddress(addressIndex, self.trace)) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
TraceManager.prototype.getMemoryAt = function (stepIndex, callback) { |
||||
var lastChanges = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.memoryChanges) |
||||
if (!lastChanges) return callback('no memory found', null) |
||||
callback(null, this.trace[lastChanges].memory) |
||||
} |
||||
|
||||
TraceManager.prototype.getCurrentPC = function (stepIndex, callback) { |
||||
callback(null, this.trace[stepIndex].pc) |
||||
} |
||||
|
||||
TraceManager.prototype.getCurrentStep = function (stepIndex, callback) { |
||||
callback(null, this.trace[stepIndex].steps) |
||||
} |
||||
|
||||
TraceManager.prototype.getMemExpand = function (stepIndex, callback) { |
||||
callback(null, this.trace[stepIndex].memexpand ? this.trace[stepIndex].memexpand : '') |
||||
} |
||||
|
||||
TraceManager.prototype.getStepCost = function (stepIndex, callback) { |
||||
callback(null, this.trace[stepIndex].gascost) |
||||
} |
||||
|
||||
TraceManager.prototype.getRemainingGas = function (stepIndex, callback) { |
||||
callback(null, this.trace[stepIndex].gas) |
||||
} |
||||
|
||||
// step section
|
||||
TraceManager.prototype.findStepOverBack = function (currentStep) { |
||||
return this.traceStepManager.findStepOverBack(currentStep) |
||||
} |
||||
|
||||
TraceManager.prototype.findStepOverForward = function (currentStep) { |
||||
return this.traceStepManager.findStepOverForward(currentStep) |
||||
} |
||||
|
||||
TraceManager.prototype.findStepOutBack = function (currentStep) { |
||||
return this.traceStepManager.findStepOutBack(currentStep) |
||||
} |
||||
|
||||
TraceManager.prototype.findStepOutForward = function (currentStep) { |
||||
return this.traceStepManager.findStepOutForward(currentStep) |
||||
} |
||||
|
||||
module.exports = TraceManager |
@ -0,0 +1,36 @@ |
||||
module.exports = { |
||||
// util section
|
||||
findLowerBound: function (target, changes) { |
||||
if (changes.length === 0) { |
||||
return undefined |
||||
} |
||||
|
||||
if (changes.length === 1) { |
||||
if (changes[0] > target) { |
||||
// we only a closest maximum, returning O
|
||||
return 0 |
||||
} else { |
||||
return changes[0] |
||||
} |
||||
} |
||||
|
||||
var middle = Math.floor(changes.length / 2) |
||||
if (changes[middle] > target) { |
||||
return this.findLowerBound(target, changes.slice(0, middle)) |
||||
} else if (changes[middle] < target) { |
||||
return this.findLowerBound(target, changes.slice(middle, changes.length)) |
||||
} else { |
||||
return changes[middle] |
||||
} |
||||
}, |
||||
|
||||
resolveCalledAddress: function (vmTraceIndex, trace) { |
||||
var address = trace[vmTraceIndex].address |
||||
if (vmTraceIndex > 0) { |
||||
var stack = trace[vmTraceIndex - 1].stack // callcode, delegatecall, ...
|
||||
address = stack[stack.length - 2] |
||||
} |
||||
return address |
||||
} |
||||
|
||||
} |
@ -0,0 +1,25 @@ |
||||
'use strict' |
||||
function TraceRetriever (_web3) { |
||||
this.web3 = _web3 |
||||
this.storages = {} // contains all intial storage (by addresses)
|
||||
} |
||||
|
||||
TraceRetriever.prototype.getTrace = function (blockNumber, txNumber, callback) { |
||||
this.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { |
||||
callback(error, result) |
||||
}) |
||||
} |
||||
|
||||
TraceRetriever.prototype.getStorage = function (blockNumber, txIndex, address, callback) { |
||||
if (this.storages[address]) { |
||||
callback(null, this.storages[address]) |
||||
} else { |
||||
var self = this |
||||
this.web3.debug.storageAt(blockNumber, txIndex, address, function (error, result) { |
||||
self.storages[address] = result |
||||
callback(error, result) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
module.exports = TraceRetriever |
@ -0,0 +1,66 @@ |
||||
'use strict' |
||||
function TraceStepManager (_traceAnalyser) { |
||||
this.traceAnalyser = _traceAnalyser |
||||
} |
||||
|
||||
TraceStepManager.prototype.isCallInstruction = function (index) { |
||||
var state = this.traceAnalyser.trace[index] |
||||
return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL' |
||||
} |
||||
|
||||
TraceStepManager.prototype.isReturnInstruction = function (index) { |
||||
var state = this.traceAnalyser.trace[index] |
||||
return state.instname === 'RETURN' |
||||
} |
||||
|
||||
TraceStepManager.prototype.findStepOverBack = function (currentStep) { |
||||
if (this.isReturnInstruction(currentStep - 1)) { |
||||
return this.findStepOutBack(currentStep) |
||||
} else { |
||||
return currentStep - 1 |
||||
} |
||||
} |
||||
|
||||
TraceStepManager.prototype.findStepOverForward = function (currentStep) { |
||||
if (this.isCallInstruction(currentStep)) { |
||||
return this.findStepOutForward(currentStep) |
||||
} else { |
||||
return currentStep + 1 |
||||
} |
||||
} |
||||
|
||||
TraceStepManager.prototype.findStepOutBack = function (currentStep) { |
||||
var i = currentStep - 1 |
||||
var depth = 0 |
||||
while (--i >= 0) { |
||||
if (this.isCallInstruction(i)) { |
||||
if (depth === 0) { |
||||
break |
||||
} else { |
||||
depth-- |
||||
} |
||||
} else if (this.isReturnInstruction(i)) { |
||||
depth++ |
||||
} |
||||
} |
||||
return i |
||||
} |
||||
|
||||
TraceStepManager.prototype.findStepOutForward = function (currentStep) { |
||||
var i = currentStep |
||||
var depth = 0 |
||||
while (++i < this.traceAnalyser.length) { |
||||
if (this.isReturnInstruction(i)) { |
||||
if (depth === 0) { |
||||
break |
||||
} else { |
||||
depth-- |
||||
} |
||||
} else if (this.isCallInstruction(i)) { |
||||
depth++ |
||||
} |
||||
} |
||||
return i + 1 |
||||
} |
||||
|
||||
module.exports = TraceStepManager |
@ -0,0 +1,86 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var Sticker = require('./sticker') |
||||
var style = require('./basicStyles') |
||||
var ASMCode = require('./asmCode') |
||||
var CalldataPanel = require('./calldataPanel') |
||||
var MemoryPanel = require('./memoryPanel') |
||||
var CallstackPanel = require('./callstackPanel') |
||||
var StackPanel = require('./stackPanel') |
||||
var StoragePanel = require('./storagePanel') |
||||
|
||||
module.exports = React.createClass({ |
||||
contextTypes: { |
||||
traceManager: React.PropTypes.object |
||||
}, |
||||
|
||||
getInitialState: function () { |
||||
return { |
||||
currentAddress: null |
||||
} |
||||
}, |
||||
|
||||
getDefaultProps: function () { |
||||
return { |
||||
currentStepIndex: -1 // index of the selected item in the vmtrace
|
||||
} |
||||
}, |
||||
|
||||
render: function () { |
||||
return ( |
||||
<div style={this.props.vmTrace === null ? style.hidden : style.display}> |
||||
<div style={style.container}> |
||||
<span style={style.address}>Current code: {this.state.currentAddress}</span> |
||||
</div> |
||||
<div style={style.container}> |
||||
<table> |
||||
<tbody> |
||||
<tr> |
||||
<td> |
||||
<ASMCode currentStepIndex={this.props.currentStepIndex} /> |
||||
<div style={Object.assign(style.inline, style.sticker)}> |
||||
<Sticker currentStepIndex={this.props.currentStepIndex} /> |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<CalldataPanel currentStepIndex={this.props.currentStepIndex} /> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<StackPanel currentStepIndex={this.props.currentStepIndex} /> |
||||
</td> |
||||
<td> |
||||
<CallstackPanel currentStepIndex={this.props.currentStepIndex} /> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<StoragePanel currentStepIndex={this.props.currentStepIndex} /> |
||||
</td> |
||||
<td> |
||||
<MemoryPanel currentStepIndex={this.props.currentStepIndex} /> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
) |
||||
}, |
||||
|
||||
componentWillReceiveProps: function (nextProps) { |
||||
if (nextProps.currentStepIndex < 0) return |
||||
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return |
||||
var self = this |
||||
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) { |
||||
if (error) { |
||||
console.log(error) |
||||
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { |
||||
self.setState({ |
||||
currentAddress: address |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
}) |
@ -1,13 +0,0 @@ |
||||
'use strict' |
||||
var React = require('react') |
||||
var AssemblyItemsBrowser = require('./assemblyItemsBrowser') |
||||
|
||||
module.exports = React.createClass({ |
||||
render: function () { |
||||
return ( |
||||
<div> |
||||
<AssemblyItemsBrowser vmTrace={this.props.vmTrace} /> |
||||
</div> |
||||
) |
||||
} |
||||
}) |
@ -1,8 +0,0 @@ |
||||
'use strict' |
||||
module.exports = { |
||||
retrieveVmTrace: function (blockNumber, txNumber, callBack) { |
||||
this.context.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { |
||||
callBack(error, result) |
||||
}) |
||||
} |
||||
} |
Loading…
Reference in new issue