embark-area-51/lib/modules/debugger/index.js

296 lines
9.1 KiB
JavaScript
Raw Normal View History

var RemixDebug = require('remix-debug-debugtest');
var CmdLine = RemixDebug.CmdLine;
var DebuggerManager = require('./debugger_manager.js');
class TransactionDebugger {
constructor(embark, _options) {
const self = this
this.embark = embark
this.debugger_manager = new DebuggerManager("http://localhost:8545")
embark.events.on('contracts:compile:solc', this.debugger_manager.setInputJson.bind(this.debugger_manager))
embark.events.on('contracts:compiled:solc', this.debugger_manager.setOutputJson.bind(this.debugger_manager))
this.tx_tracker = {}
this.last_tx = ""
this.isDebugging = false
this.listenToEvents()
this.listenToCommands()
this.listentoAPI()
}
listenToEvents() {
const self = this
this.embark.events.on('blockchain:tx', (tx) => {
this.embark.events.request("contracts:contract", tx.name, (contract) => {
self.tx_tracker[tx.transactionHash] = {tx: tx, contract: contract}
self.last_tx = tx.transactionHash
if (tx.status !== '0x0') return
self.embark.logger.info("Transaction failed");
self.debugger_manager.getLastLine(tx.transactionHash, contract.filename, (lines, line, known_vars) => {
lines.forEach((line) => {
self.embark.logger.error(line)
})
self.find_vars_in_line(tx.transactionHash, line, known_vars, (found_vars) => {
if (!found_vars) return
self.embark.logger.info("vars:")
found_vars.forEach((variable) => {
self.embark.logger.info(`${variable.name}: ${variable.value}`)
})
});
})
})
})
}
find_vars_in_line(txHash, line, known_vars, cb) {
let found_vars = [];
this.getGlobals(txHash, (err, globals) => {
if (err) return cb([]);
for (let variable in globals) {
let value = globals[variable]
if (line.indexOf(variable) >= 0) {
found_vars.push({name: variable, value: value});
}
}
for (let variable in known_vars.locals) {
let value = known_vars.locals[variable];
let variable_name = variable.split(' ')[0]
if (line.indexOf(variable_name) >= 0) {
found_vars.push({name: variable, value: value});
}
}
for (let variable in known_vars.contract) {
let value = known_vars.contract[variable];
let variable_name = variable.split(' ')[0]
if (line.indexOf(variable_name) >= 0) {
found_vars.push({name: variable, value: value});
}
}
cb(found_vars);
})
}
listentoAPI() {
this.debuggerData = {}
this.apiDebugger = false
this.embark.registerAPICall('post', '/embark-api/debugger/start', (req, res) => {
let txHash = req.body.params.txHash
this.embark.events.request("contracts:contract:byTxHash", txHash, (err, contract) => {
if (err) {
this.embark.logger.error(err);
return res.send({error: err})
}
let filename = contract.filename
this.apiDebugger = this.debugger_manager.createDebuggerSession(txHash, filename, () => {
this.getGlobals(txHash, (err, globals) => {
if (err) return res.send({ok: false});
this.debuggerData.globals = globals;
res.send({ok :true})
})
})
})
});
this.embark.registerAPICall('post', '/embark-api/debugger/JumpBack', (req, res) => {
this.apiDebugger.stepJumpNextBreakpoint()
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/JumpForward', (req, res) => {
this.apiDebugger.stepJumpPreviousBreakpoint()
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepOverForward', (req, res) => {
this.apiDebugger.stepOverForward(true)
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepOverBackward', (req, res) => {
this.apiDebugger.stepOverBack(true)
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepIntoForward', (req, res) => {
this.apiDebugger.stepIntoForward(true)
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepIntoBackward', (req, res) => {
this.apiDebugger.stepIntoBack(true)
res.send({ok :true})
});
this.embark.registerAPICall('post', '/embark-api/debugger/breakpoint', (req, res) => {
console.dir("new breakpoint")
res.send({ok :true})
});
this.embark.registerAPICall('ws', '/embark-api/debugger', (ws, _req) => {
if (!this.apiDebugger) return
this.apiDebugger.events.on("source", (lineColumnPos, rawLocation) => {
this.debuggerData.sources = {lineColumnPos, rawLocation}
ws.send(JSON.stringify(this.debuggerData), () => {})
})
this.apiDebugger.events.on("locals", (data) => {
this.debuggerData.locals = this.simplifyDebuggerVars(data)
ws.send(JSON.stringify(this.debuggerData), () => {})
});
this.apiDebugger.events.on("globals", (data) => {
this.debuggerData.contract = this.simplifyDebuggerVars(data)
ws.send(JSON.stringify(this.debuggerData), () => {})
})
});
}
simplifyDebuggerVars(data) {
let new_data = {};
for (let key in data) {
let field = data[key];
new_data[`${key} (${field.type})`] = field.value
}
return new_data
}
listenToCommands() {
const self = this
this.cmdDebugger = false
this.currentCmdTxHash = ""
this.embark.registerConsoleCommand((cmd, _options) => {
let cmdName = cmd.split(" ")[0]
let txHash = cmd.split(" ")[1]
return {
match: () => cmdName === 'debug',
process: (cb) => {
if (txHash) {
this.embark.events.request("contracts:contract:byTxHash", txHash, (err, contract) => {
if (err) {
this.embark.logger.error(err);
return;
}
let filename = contract.filename
self.currentCmdTxHash = txHash
self.cmdDebugger = self.debugger_manager.createDebuggerSession(txHash, filename, () => {
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
})
});
return
}
self.currentCmdTxHash = self.last_tx
let filename = self.tx_tracker[self.last_tx].contract.filename
self.cmdDebugger = self.debugger_manager.createDebuggerSession(self.last_tx, filename, () => {
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
})
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'next' || cmd === 'n'),
process: (cb) => {
if (!self.cmdDebugger.currentStep()) {
console.dir("end of execution reached")
return self.cmdDebugger.unload()
}
self.cmdDebugger.stepOverForward(true)
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'previous' || cmd === 'p'),
process: (cb) => {
if (!self.cmdDebugger.currentStep()) {
console.dir("end of execution reached")
return self.cmdDebugger.unload()
}
self.cmdDebugger.stepOverBack(true)
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'var local' || cmd === 'v l' || cmd === 'vl'),
process: (cb) => {
self.cmdDebugger.displayLocals()
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'var global' || cmd === 'v g' || cmd === 'vg'),
process: (cb) => {
self.cmdDebugger.displayGlobals()
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'var all' || cmd === 'v a' || cmd === 'va'),
process: (cb) => {
self.getGlobals((err, globals) => {
if (err) return self.embark.logger.error(err);
console.dir(globals);
});
}
};
})
}
getGlobals(txHash, cb) {
let globals = {}
this.embark.events.request("blockchain:getTransaction", txHash, (err, tx) => {
if (err) return cb(err);
this.embark.events.request("blockchain:block:byHash", tx.blockHash, (err, block) => {
if (err) return cb(err);
globals["block.blockHash"] = tx.blockHash;
globals["block.number"] = tx.blockNumber;
globals["block.coinbase"] = block.miner;
globals["block.difficulty"] = block.difficulty;
globals["block.gaslimit"] = block.gasLimit;
globals["block.timestamp"] = block.timestamp;
globals["msg.sender"] = tx.from;
globals["msg.gas"] = tx.gas;
globals["msg.gasPrice"] = tx.gasPrice;
globals["msg.value"] = tx.value;
globals["now"] = block.timestamp;
cb(null, globals);
})
})
}
}
module.exports = TransactionDebugger