diff --git a/remix-debug/index.js b/remix-debug/index.js new file mode 100644 index 0000000000..b1286590a5 --- /dev/null +++ b/remix-debug/index.js @@ -0,0 +1,22 @@ +'use strict' +var remixCore = require('remix-core') +var EthDebugger = require('./src/Ethdebugger') + +/* + Use of breakPointManager : + + var breakPointManager = new BreakpointManager(this.debugger, (sourceLocation) => { + return line/column from offset (sourceLocation) + }) + this.debugger.setBreakpointManager(breakPointManager) +*/ +module.exports = { + EthDebugger: EthDebugger, + /** + * constructor + * + * @param {Object} _debugger - type of EthDebugger + * @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location + */ + BreakpointManager: remixCore.code.BreakpointManager +} diff --git a/remix-debug/package.json b/remix-debug/package.json new file mode 100644 index 0000000000..cc93324d07 --- /dev/null +++ b/remix-debug/package.json @@ -0,0 +1,96 @@ +{ + "name": "remix-debug", + "version": "0.0.1", + "description": "Ethereum IDE and tools for the web", + "contributors": [ + { + "name": "Yann Levreau", + "email": "yann@ethdev.com" + }, + { + "name": "Liana Husikyan", + "email": "liana@ethdev.com" + } + ], + "main": "./index.js", + "devDependencies": { + "babel-eslint": "^7.1.1", + "babel-plugin-transform-object-assign": "^6.22.0", + "babel-plugin-yo-yoify": "^0.3.3", + "babel-polyfill": "^6.22.0", + "babel-preset-env": "^1.6.1", + "babel-preset-es2015": "^6.24.0", + "babel-preset-stage-0": "^6.24.1", + "babelify": "^7.3.0", + "notify-error": "^1.2.0", + "npm-run-all": "^4.1.2", + "remix-core": "latest", + "remix-lib": "latest", + "remix-solidity": "latest", + "standard": "^7.0.1", + "standard-reporter": "^1.0.5" + }, + "scripts": { + "build": "mkdirp build; browserify index.js > build/app.js", + "lint": "standard | notify-error" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ethereum/remix.git" + }, + "author": "cpp-ethereum team", + "license": "MIT", + "bugs": { + "url": "https://github.com/ethereum/remix/issues" + }, + "homepage": "https://github.com/ethereum/remix#readme", + "standard": { + "ignore": [ + "node_modules/*", + "build/*", + "test/resources/*" + ] + }, + "babel": { + "plugins": [ + "transform-es2015-template-literals", + "transform-es2015-literals", + "transform-es2015-function-name", + "transform-es2015-arrow-functions", + "transform-es2015-block-scoped-functions", + "transform-es2015-classes", + "transform-es2015-object-super", + "transform-es2015-shorthand-properties", + "transform-es2015-duplicate-keys", + "transform-es2015-computed-properties", + "transform-es2015-for-of", + "transform-es2015-sticky-regex", + "transform-es2015-unicode-regex", + "check-es2015-constants", + "transform-es2015-spread", + "transform-es2015-parameters", + "transform-es2015-destructuring", + "transform-es2015-block-scoping", + "transform-object-assign" + ] + }, + "browserify": { + "transform": [ + [ + "babelify", + { + "sourceMapsAbsolute": false, + "sourceMaps": true, + "plugins": [ + [ + "transform-object-assign" + ] + ], + "presets": [ + "es2015" + ] + } + ] + ] + } +} diff --git a/remix-debug/src/Ethdebugger.js b/remix-debug/src/Ethdebugger.js new file mode 100644 index 0000000000..eb7dd6b5af --- /dev/null +++ b/remix-debug/src/Ethdebugger.js @@ -0,0 +1,146 @@ +'use strict' +var remixCore = require('remix-core') +var TraceManager = remixCore.trace.TraceManager +var remixLib = require('remix-lib') +var global = remixLib.global +var init = remixLib.init +var executionContext = remixLib.execution.executionContext +var EventManager = remixLib.EventManager +var Web3Providers = remixLib.vm.Web3Providers +var DummyProvider = remixLib.vm.DummyProvider +var CodeManager = remixCore.code.CodeManager +var remixSolidity = require('remix-solidity') +var SolidityProxy = remixSolidity.SolidityProxy +var InternalCallTree = remixSolidity.InternalCallTree + +/** + * Ethdebugger is a wrapper around a few classes that helps debugging a transaction + * + * - Web3Providers - define which environment (web3) the transaction will be retrieved from + * - TraceManager - Load / Analyze the trace and retrieve details of specific test + * - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index + * - SolidityProxy - Basically used to extract state variable from AST + * - Breakpoint Manager - Used to add / remove / jumpto breakpoint + * - InternalCallTree - Used to retrieved local variables + * + * @param {Map} opts - { function compilationResult } // + */ +function Ethdebugger (opts) { + this.opts = opts || {} + if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null } + + var self = this + this.event = new EventManager() + + this.tx + + 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) + + this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true }) + + this.event.register('indexChanged', this, function (index) { + self.codeManager.resolveStep(index, self.tx) + }) +} + +Ethdebugger.prototype.sourceLocationFromVMTraceIndex = function (address, stepIndex, callback) { + this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, this.solidityProxy.contracts, (error, rawLocation) => { + callback(error, rawLocation) + }) +} + +Ethdebugger.prototype.sourceLocationFromInstructionIndex = function (address, instIndex, callback) { + this.debugger.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, instIndex, this.solidityProxy.contracts, function (error, rawLocation) { + callback(error, rawLocation) + }) +} + +Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) { + this.breakpointManager = breakpointManager +} + +Ethdebugger.prototype.resolveStep = function (index) { + this.codeManager.resolveStep(index, this.tx) +} + +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.switchProvider = function (type) { + var self = this + this.web3Providers.get(type, function (error, obj) { + if (error) { + console.log('provider ' + type + ' not defined') + } else { + global.web3 = obj + executionContext.detectNetwork((error, network) => { + if (error || !network) { + global.web3Debug = obj + } else { + var webDebugNode = init.web3DebugNode(network.name) + global.web3Debug = !webDebugNode ? obj : webDebugNode + } + }) + 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.unLoad = function () { + this.traceManager.init() + this.codeManager.clear() + this.stepManager.reset() + this.event.trigger('traceUnloaded') +} + +Ethdebugger.prototype.debug = function (blockNumber, txIndex, tx) { + if (this.traceManager.isLoading) { + return + } + this.setCompilationResult(this.opts.compilationResult()) + console.log('loading trace...') + this.tx = tx + var self = this + this.traceManager.resolveTrace(tx, function (error, result) { + console.log('trace loaded ' + result) + if (result) { + self.event.trigger('newTraceLoaded', [self.traceManager.trace]) + if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) { + self.breakpointManager.jumpNextBreakpoint(false) + } + } else { + self.statusMessage = error ? error.message : 'Trace not loaded' + } + }) +} + +module.exports = Ethdebugger