|
|
@ -7,154 +7,163 @@ const BreakpointManager = require('../code/breakpointManager') |
|
|
|
const StepManager = require('./stepManager') |
|
|
|
const StepManager = require('./stepManager') |
|
|
|
const VmDebuggerLogic = require('./VmDebugger') |
|
|
|
const VmDebuggerLogic = require('./VmDebugger') |
|
|
|
|
|
|
|
|
|
|
|
function Debugger (options) { |
|
|
|
export class Debugger { |
|
|
|
this.event = new EventManager() |
|
|
|
|
|
|
|
|
|
|
|
event |
|
|
|
this.offsetToLineColumnConverter = options.offsetToLineColumnConverter |
|
|
|
offsetToLineColumnConverter |
|
|
|
/* |
|
|
|
compilationResult |
|
|
|
Returns a compilation result for a given address or the last one available if none are found |
|
|
|
debugger |
|
|
|
*/ |
|
|
|
breakPointManager |
|
|
|
this.compilationResult = options.compilationResult || function (contractAddress) { return null } |
|
|
|
step_manager |
|
|
|
|
|
|
|
vmDebuggerLogic |
|
|
|
this.debugger = new Ethdebugger({ |
|
|
|
|
|
|
|
web3: options.web3, |
|
|
|
constructor (options) { |
|
|
|
debugWithGeneratedSources: options.debugWithGeneratedSources, |
|
|
|
this.event = new EventManager() |
|
|
|
compilationResult: this.compilationResult, |
|
|
|
|
|
|
|
}) |
|
|
|
this.offsetToLineColumnConverter = options.offsetToLineColumnConverter |
|
|
|
|
|
|
|
/* |
|
|
|
const {traceManager, callTree, solidityProxy} = this.debugger |
|
|
|
Returns a compilation result for a given address or the last one available if none are found |
|
|
|
this.breakPointManager = new BreakpointManager({traceManager, callTree, solidityProxy, locationToRowConverter: async (sourceLocation) => { |
|
|
|
*/ |
|
|
|
const compilationResult = await this.compilationResult() |
|
|
|
this.compilationResult = options.compilationResult || function (contractAddress) { return null } |
|
|
|
if (!compilationResult) return { start: null, end: null } |
|
|
|
|
|
|
|
return this.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, compilationResult.source.sources, compilationResult.data.sources) |
|
|
|
this.debugger = new Ethdebugger({ |
|
|
|
}}) |
|
|
|
web3: options.web3, |
|
|
|
|
|
|
|
debugWithGeneratedSources: options.debugWithGeneratedSources, |
|
|
|
this.breakPointManager.event.register('managersChanged', () => { |
|
|
|
compilationResult: this.compilationResult, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const {traceManager, callTree, solidityProxy} = this.debugger |
|
|
|
const {traceManager, callTree, solidityProxy} = this.debugger |
|
|
|
this.breakPointManager.setManagers({traceManager, callTree, solidityProxy}) |
|
|
|
this.breakPointManager = new BreakpointManager({traceManager, callTree, solidityProxy, locationToRowConverter: async (sourceLocation) => { |
|
|
|
}) |
|
|
|
const compilationResult = await this.compilationResult() |
|
|
|
|
|
|
|
if (!compilationResult) return { start: null, end: null } |
|
|
|
|
|
|
|
return this.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, compilationResult.source.sources, compilationResult.data.sources) |
|
|
|
|
|
|
|
}}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.breakPointManager.event.register('managersChanged', () => { |
|
|
|
|
|
|
|
const {traceManager, callTree, solidityProxy} = this.debugger |
|
|
|
|
|
|
|
this.breakPointManager.setManagers({traceManager, callTree, solidityProxy}) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
this.breakPointManager.event.register('breakpointStep', (step) => { |
|
|
|
this.breakPointManager.event.register('breakpointStep', (step) => { |
|
|
|
this.step_manager.jumpTo(step) |
|
|
|
this.step_manager.jumpTo(step) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
this.debugger.setBreakpointManager(this.breakPointManager) |
|
|
|
this.debugger.setBreakpointManager(this.breakPointManager) |
|
|
|
|
|
|
|
|
|
|
|
this.debugger.event.register('newTraceLoaded', this, () => { |
|
|
|
this.debugger.event.register('newTraceLoaded', this, () => { |
|
|
|
this.event.trigger('debuggerStatus', [true]) |
|
|
|
this.event.trigger('debuggerStatus', [true]) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
this.debugger.event.register('traceUnloaded', this, () => { |
|
|
|
this.debugger.event.register('traceUnloaded', this, () => { |
|
|
|
this.event.trigger('debuggerStatus', [false]) |
|
|
|
this.event.trigger('debuggerStatus', [false]) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async registerAndHighlightCodeItem (index) { |
|
|
|
|
|
|
|
// register selected code item, highlight the corresponding source location
|
|
|
|
|
|
|
|
// this.debugger.traceManager.getCurrentCalledAddressAt(index, async (error, address) => {
|
|
|
|
|
|
|
|
|
|
|
|
Debugger.prototype.registerAndHighlightCodeItem = async function (index) { |
|
|
|
try { |
|
|
|
// register selected code item, highlight the corresponding source location
|
|
|
|
const address = this.debugger.traceManager.getCurrentCalledAddressAt(index) |
|
|
|
// this.debugger.traceManager.getCurrentCalledAddressAt(index, async (error, address) => {
|
|
|
|
const compilationResultForAddress = await this.compilationResult(address) |
|
|
|
|
|
|
|
if (!compilationResultForAddress) return |
|
|
|
try { |
|
|
|
|
|
|
|
const address = this.debugger.traceManager.getCurrentCalledAddressAt(index) |
|
|
|
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then((rawLocation) => { |
|
|
|
const compilationResultForAddress = await this.compilationResult(address) |
|
|
|
if (compilationResultForAddress && compilationResultForAddress.data) { |
|
|
|
if (!compilationResultForAddress) return |
|
|
|
const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) |
|
|
|
|
|
|
|
const astSources = Object.assign({}, compilationResultForAddress.data.sources) |
|
|
|
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then((rawLocation) => { |
|
|
|
const sources = Object.assign({}, compilationResultForAddress.source.sources) |
|
|
|
if (compilationResultForAddress && compilationResultForAddress.data) { |
|
|
|
if (generatedSources) { |
|
|
|
const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) |
|
|
|
for (const genSource of generatedSources) { |
|
|
|
const astSources = Object.assign({}, compilationResultForAddress.data.sources) |
|
|
|
astSources[genSource.name] = { id: genSource.id, ast: genSource.ast } |
|
|
|
const sources = Object.assign({}, compilationResultForAddress.source.sources) |
|
|
|
sources[genSource.name] = { content: genSource.contents } |
|
|
|
if (generatedSources) { |
|
|
|
} |
|
|
|
for (const genSource of generatedSources) { |
|
|
|
|
|
|
|
astSources[genSource.name] = { id: genSource.id, ast: genSource.ast } |
|
|
|
|
|
|
|
sources[genSource.name] = { content: genSource.contents } |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var lineColumnPos = this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources) |
|
|
|
|
|
|
|
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources]) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.event.trigger('newSourceLocation', [null]) |
|
|
|
} |
|
|
|
} |
|
|
|
var lineColumnPos = this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources) |
|
|
|
}).catch((_error) => { |
|
|
|
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources]) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.event.trigger('newSourceLocation', [null]) |
|
|
|
this.event.trigger('newSourceLocation', [null]) |
|
|
|
} |
|
|
|
}) |
|
|
|
}).catch((_error) => { |
|
|
|
// })
|
|
|
|
this.event.trigger('newSourceLocation', [null]) |
|
|
|
} catch (error) { |
|
|
|
}) |
|
|
|
return console.log(error) |
|
|
|
// })
|
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
|
|
|
|
return console.log(error) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Debugger.prototype.updateWeb3 = function (web3) { |
|
|
|
updateWeb3 (web3) { |
|
|
|
this.debugger.web3 = web3 |
|
|
|
this.debugger.web3 = web3 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Debugger.prototype.debug = function (blockNumber, txNumber, tx, loadingCb) { |
|
|
|
debug (blockNumber, txNumber, tx, loadingCb) { |
|
|
|
const web3 = this.debugger.web3 |
|
|
|
const web3 = this.debugger.web3 |
|
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
if (this.debugger.traceManager.isLoading) { |
|
|
|
if (this.debugger.traceManager.isLoading) { |
|
|
|
return resolve() |
|
|
|
return resolve() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (tx) { |
|
|
|
if (tx) { |
|
|
|
if (!tx.to) { |
|
|
|
if (!tx.to) { |
|
|
|
tx.to = traceHelper.contractCreationToken('0') |
|
|
|
tx.to = traceHelper.contractCreationToken('0') |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.debugTx(tx, loadingCb) |
|
|
|
|
|
|
|
return resolve() |
|
|
|
} |
|
|
|
} |
|
|
|
this.debugTx(tx, loadingCb) |
|
|
|
|
|
|
|
return resolve() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
if (txNumber.indexOf('0x') !== -1) { |
|
|
|
if (txNumber.indexOf('0x') !== -1) { |
|
|
|
return web3.eth.getTransaction(txNumber, (_error, tx) => { |
|
|
|
return web3.eth.getTransaction(txNumber, (_error, tx) => { |
|
|
|
|
|
|
|
if (_error) return reject(_error) |
|
|
|
|
|
|
|
if (!tx) return reject('cannot find transaction ' + txNumber) |
|
|
|
|
|
|
|
this.debugTx(tx, loadingCb) |
|
|
|
|
|
|
|
return resolve() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
web3.eth.getTransactionFromBlock(blockNumber, txNumber, (_error, tx) => { |
|
|
|
if (_error) return reject(_error) |
|
|
|
if (_error) return reject(_error) |
|
|
|
if (!tx) return reject('cannot find transaction ' + txNumber) |
|
|
|
if (!tx) return reject('cannot find transaction ' + blockNumber + ' ' + txNumber) |
|
|
|
this.debugTx(tx, loadingCb) |
|
|
|
this.debugTx(tx, loadingCb) |
|
|
|
return resolve() |
|
|
|
return resolve() |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
return reject(e.message) |
|
|
|
} |
|
|
|
} |
|
|
|
web3.eth.getTransactionFromBlock(blockNumber, txNumber, (_error, tx) => { |
|
|
|
}) |
|
|
|
if (_error) return reject(_error) |
|
|
|
} |
|
|
|
if (!tx) return reject('cannot find transaction ' + blockNumber + ' ' + txNumber) |
|
|
|
|
|
|
|
this.debugTx(tx, loadingCb) |
|
|
|
|
|
|
|
return resolve() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
return reject(e.message) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Debugger.prototype.debugTx = function (tx, loadingCb) { |
|
|
|
debugTx (tx, loadingCb) { |
|
|
|
this.step_manager = new StepManager(this.debugger, this.debugger.traceManager) |
|
|
|
this.step_manager = new StepManager(this.debugger, this.debugger.traceManager) |
|
|
|
|
|
|
|
|
|
|
|
this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => { |
|
|
|
this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => { |
|
|
|
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, this.step_manager.currentStepIndex, this.debugger.solidityProxy.contracts).then((sourceLocation) => { |
|
|
|
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, this.step_manager.currentStepIndex, this.debugger.solidityProxy.contracts).then((sourceLocation) => { |
|
|
|
this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [sourceLocation]) |
|
|
|
this.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.vmDebuggerLogic = new VmDebuggerLogic(this.debugger, tx, this.step_manager, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree) |
|
|
|
this.vmDebuggerLogic.start() |
|
|
|
this.vmDebuggerLogic.start() |
|
|
|
|
|
|
|
|
|
|
|
this.step_manager.event.register('stepChanged', this, (stepIndex) => { |
|
|
|
this.step_manager.event.register('stepChanged', this, (stepIndex) => { |
|
|
|
if (typeof stepIndex !== 'number' || stepIndex >= this.step_manager.traceLength) { |
|
|
|
if (typeof stepIndex !== 'number' || stepIndex >= this.step_manager.traceLength) { |
|
|
|
return this.event.trigger('endDebug') |
|
|
|
return this.event.trigger('endDebug') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.debugger.codeManager.resolveStep(stepIndex, tx) |
|
|
|
this.debugger.codeManager.resolveStep(stepIndex, tx) |
|
|
|
this.step_manager.event.trigger('indexChanged', [stepIndex]) |
|
|
|
this.step_manager.event.trigger('indexChanged', [stepIndex]) |
|
|
|
this.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex]) |
|
|
|
this.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex]) |
|
|
|
this.vmDebuggerLogic.debugger.event.trigger('indexChanged', [stepIndex]) |
|
|
|
this.vmDebuggerLogic.debugger.event.trigger('indexChanged', [stepIndex]) |
|
|
|
this.registerAndHighlightCodeItem(stepIndex) |
|
|
|
this.registerAndHighlightCodeItem(stepIndex) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
loadingCb() |
|
|
|
loadingCb() |
|
|
|
this.debugger.debug(tx) |
|
|
|
this.debugger.debug(tx) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Debugger.prototype.unload = function () { |
|
|
|
unload () { |
|
|
|
this.debugger.unLoad() |
|
|
|
this.debugger.unLoad() |
|
|
|
this.event.trigger('debuggerUnloaded') |
|
|
|
this.event.trigger('debuggerUnloaded') |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = Debugger |
|
|
|
|
|
|
|