diff --git a/src/app.js b/src/app.js index f2c1c78c2a..04158f3e32 100644 --- a/src/app.js +++ b/src/app.js @@ -494,29 +494,122 @@ var run = function () { var executionContext = new ExecutionContext() var compiler = new Compiler(handleImportCall) var formalVerification = new FormalVerification($('#verificationView'), compiler.event) - var offsetToLineColumnConverter = new OffsetToLineColumnConverter(compiler.event) - var transactionDebugger = new Debugger('#debugger', editor, compiler, executionContext.event, switchToFile, offsetToLineColumnConverter) + // ----------------- Debugger ----------------- + var debugAPI = { + statementMarker: null, + fullLineMarker: null, + currentSourceLocation: (lineColumnPos, location) => { + if (this.statementMarker) editor.removeMarker(this.statementMarker) + if (this.fullLineMarker) editor.removeMarker(this.fullLineMarker) + this.statementMarker = null + this.fullLineMarker = null + if (lineColumnPos) { + var name = editor.getCacheFile() // current opened tab + var source = compiler.lastCompilationResult.data.sourceList[location.file] // auto switch to that tab + if (name !== source) { + switchToFile(source) + } + this.statementMarker = editor.addMarker(lineColumnPos, 'highlightcode') + if (lineColumnPos.start.line === lineColumnPos.end.line) { + this.fullLineMarker = editor.addMarker({ + start: { + line: lineColumnPos.start.line, + column: 0 + }, + end: { + line: lineColumnPos.start.line + 1, + column: 0 + } + }, 'highlightcode_fullLine') + } + } + }, + lastCompilationResult: () => { + return compiler.lastCompilationResult + }, + offsetToLineColumn: (location, file) => { + return offsetToLineColumnConverter.offsetToLineColumn(location, file, compiler.lastCompilationResult) + } + } + var transactionDebugger = new Debugger('#debugger', debugAPI, executionContext.event, editor.event) transactionDebugger.addProvider('vm', executionContext.vm()) transactionDebugger.addProvider('injected', executionContext.web3()) transactionDebugger.addProvider('web3', executionContext.web3()) transactionDebugger.switchProvider(executionContext.getProvider()) + // ----------------- UniversalDApp ----------------- var udapp = new UniversalDApp(executionContext, { removable: false, removable_instances: true - }, transactionDebugger) + }) udapp.event.register('debugRequested', this, function (txResult) { startdebugging(txResult.transactionHash) }) - var renderer = new Renderer(editor, updateFiles, udapp, executionContext, formalVerification.event, compiler.event) // eslint-disable-line + // ----------------- Renderer ----------------- + var transactionContextAPI = { + getAddress: (cb) => { + cb(null, $('#txorigin').val()) + }, + getValue: (cb) => { + try { + var comp = $('#value').val().split(' ') + cb(null, executionContext.web3().toWei(comp[0], comp.slice(1).join(' '))) + } catch (e) { + cb(e) + } + }, + getGasLimit: (cb) => { + cb(null, $('#gasLimit').val()) + } + } - var staticanalysis = new StaticAnalysis(compiler.event, renderer, editor, offsetToLineColumnConverter) + var rendererAPI = { + error: (file, error) => { + if (file === editor.getCacheFile()) { + editor.addAnnotation(error) + } + }, + errorClick: (errFile, errLine, errCol) => { + if (errFile !== editor.getCacheFile() && editor.hasFile(errFile)) { + switchToFile(errFile) + } + editor.gotoLine(errLine, errCol) + }, + currentCompiledSourceCode: () => { + if (compiler.lastCompilationResult.source) { + return compiler.lastCompilationResult.source.sources[compiler.lastCompilationResult.source.target] + } + return '' + }, + resetDapp: (udappContracts, renderOutputModifier) => { + udapp.reset(udappContracts, transactionContextAPI, renderOutputModifier) + }, + renderDapp: () => { + return udapp.render() + }, + getAccounts: (callback) => { + udapp.getAccounts(callback) + } + } + var renderer = new Renderer(rendererAPI, formalVerification.event, compiler.event) + + // ----------------- StaticAnalysis ----------------- + var staticAnalysisAPI = { + renderWarning: (label, warningContainer, type) => { + return renderer.error(label, warningContainer, type) + }, + offsetToLineColumn: (location, file) => { + return offsetToLineColumnConverter.offsetToLineColumn(location, file, compiler.lastCompilationResult) + } + } + var staticanalysis = new StaticAnalysis(staticAnalysisAPI, compiler.event) $('#staticanalysisView').append(staticanalysis.render()) + // ----------------- autoCompile ----------------- var autoCompile = document.querySelector('#autoCompile').checked if (config.exists('autoCompile')) { autoCompile = config.get('autoCompile') diff --git a/src/app/debugger.js b/src/app/debugger.js index 7003505522..1a12d231a1 100644 --- a/src/app/debugger.js +++ b/src/app/debugger.js @@ -1,22 +1,16 @@ 'use strict' var remix = require('ethereum-remix') -var ace = require('brace') -var Range = ace.acequire('ace/range').Range /** * Manage remix and source highlighting */ -function Debugger (id, editor, compiler, executionContextEvent, switchToFile, offsetToLineColumnConverter) { +function Debugger (id, appAPI, executionContextEvent, editorEvent) { this.el = document.querySelector(id) - this.offsetToLineColumnConverter = offsetToLineColumnConverter this.debugger = new remix.ui.Debugger() this.sourceMappingDecoder = new remix.util.SourceMappingDecoder() this.el.appendChild(this.debugger.render()) - this.editor = editor - this.switchToFile = switchToFile - this.compiler = compiler - this.markers = {} + this.appAPI = appAPI var self = this executionContextEvent.register('contextChanged', this, function (context) { @@ -24,23 +18,23 @@ function Debugger (id, editor, compiler, executionContextEvent, switchToFile, of }) this.debugger.event.register('traceUnloaded', this, function () { - self.removeMarkers() + self.appAPI.currentSourceLocation(null) }) // unload if a file has changed (but not if tabs were switched) - editor.event.register('contentChanged', function () { + editorEvent.register('contentChanged', function () { self.debugger.unLoad() }) // register selected code item, highlight the corresponding source location this.debugger.codeManager.event.register('changed', this, function (code, address, index) { - if (self.compiler.lastCompilationResult) { - this.debugger.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, index, self.compiler.lastCompilationResult.data.contracts, function (error, rawLocation) { + if (self.appAPI.lastCompilationResult()) { + this.debugger.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, index, self.appAPI.lastCompilationResult().data.contracts, function (error, rawLocation) { if (!error) { - var lineColumnPos = self.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, self.editor, self.compiler.lastCompilationResult.data) - self.highlight(lineColumnPos, rawLocation) + var lineColumnPos = self.appAPI.offsetToLineColumn(rawLocation, rawLocation.file) + self.appAPI.currentSourceLocation(lineColumnPos, rawLocation) } else { - self.unhighlight() + self.appAPI.currentSourceLocation(null) } }) } @@ -56,44 +50,12 @@ Debugger.prototype.debug = function (txHash) { var self = this this.debugger.web3().eth.getTransaction(txHash, function (error, tx) { if (!error) { - self.debugger.setCompilationResult(self.compiler.lastCompilationResult.data) + self.debugger.setCompilationResult(self.appAPI.lastCompilationResult().data) self.debugger.debug(tx) } }) } -/** - * highlight the given @arg lineColumnPos - * - * @param {Object} lineColumnPos - position of the source code to hightlight {start: {line, column}, end: {line, column}} - * @param {Object} rawLocation - raw position of the source code to hightlight {start, length, file, jump} - */ -Debugger.prototype.highlight = function (lineColumnPos, rawLocation) { - this.unhighlight() - var name = this.editor.getCacheFile() // current opened tab - var source = this.compiler.lastCompilationResult.data.sourceList[rawLocation.file] // auto switch to that tab - if (name !== source) { - this.switchToFile(source) // command the app to swicth to the next file - } - var range = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column) - this.markers['highlightcode'] = this.editor.addMarker(range, 'highlightcode') - if (lineColumnPos.start.line === lineColumnPos.end.line) { - var fullrange = new Range(lineColumnPos.start.line, 0, lineColumnPos.start.line + 1, 0) - this.markers['highlightcode_fullLine'] = this.editor.addMarker(fullrange, 'highlightcode_fullLine') - } -} - -/** - * unhighlight the given @arg lineColumnPos - * - * @param {Object} lineColumnPos - position of the source code to hightlight {start: {line, column}, end: {line, column}} - * @param {Object} rawLocation - raw position of the source code to hightlight {start, length, file, jump} - */ -Debugger.prototype.unhighlight = function (lineColumnPos, rawLocation, cssCode) { - this.removeMarker('highlightcode') - this.removeMarker('highlightcode_fullLine') -} - /** * add a new web3 provider to remix * @@ -120,23 +82,4 @@ Debugger.prototype.web3 = function (type) { return this.debugger.web3() } -/** - * unhighlight highlighted statements - */ -Debugger.prototype.removeMarkers = function () { - for (var k in this.markers) { - this.removeMarker(k) - } -} - -/** - * unhighlight the current highlighted statement - */ -Debugger.prototype.removeMarker = function (key) { - if (this.markers[key]) { - this.editor.removeMarker(this.markers[key]) - this.markers[key] = null - } -} - module.exports = Debugger diff --git a/src/app/editor.js b/src/app/editor.js index 7db3a0fb51..02a3c61b72 100644 --- a/src/app/editor.js +++ b/src/app/editor.js @@ -5,6 +5,7 @@ var EventManager = require('../lib/eventManager') var examples = require('./example-contracts') var ace = require('brace') +var Range = ace.acequire('ace/range').Range require('../mode-solidity.js') function Editor (doNotLoadStorage, storage) { @@ -17,8 +18,9 @@ function Editor (doNotLoadStorage, storage) { var sessions = {} var sourceAnnotations = [] - this.addMarker = function (range, cssClass) { - return editor.session.addMarker(range, cssClass) + this.addMarker = function (lineColumnPos, cssClass) { + var currentRange = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column) + return editor.session.addMarker(currentRange, cssClass) } this.removeMarker = function (markerId) { @@ -149,9 +151,9 @@ function Editor (doNotLoadStorage, storage) { editor.getSession().setAnnotations(sourceAnnotations) } - this.handleErrorClick = function (errLine, errCol) { + this.gotoLine = function (line, col) { editor.focus() - editor.gotoLine(errLine + 1, errCol - 1, true) + editor.gotoLine(line + 1, col - 1, true) } function newEditorSession (filekey) { diff --git a/src/app/renderer.js b/src/app/renderer.js index 02822f049e..277e91a560 100644 --- a/src/app/renderer.js +++ b/src/app/renderer.js @@ -4,11 +4,8 @@ var $ = require('jquery') var utils = require('./utils') -function Renderer (editor, updateFiles, udapp, executionContext, formalVerificationEvent, compilerEvent) { - this.editor = editor - this.updateFiles = updateFiles - this.udapp = udapp - this.executionContext = executionContext +function Renderer (appAPI, formalVerificationEvent, compilerEvent) { + this.appAPI = appAPI var self = this formalVerificationEvent.register('compilationFinished', this, function (success, message, container, options) { if (!success) { @@ -55,8 +52,8 @@ Renderer.prototype.error = function (message, container, options) { var errFile = err[1] var errLine = parseInt(err[2], 10) - 1 var errCol = err[4] ? parseInt(err[4], 10) : 0 - if (!opt.noAnnotations && (errFile === '' || errFile === self.editor.getCacheFile())) { - self.editor.addAnnotation({ + if (!opt.noAnnotations) { + self.appAPI.error(errFile, { row: errLine, column: errCol, text: message, @@ -64,12 +61,7 @@ Renderer.prototype.error = function (message, container, options) { }) } $error.click(function (ev) { - if (errFile !== '' && errFile !== self.editor.getCacheFile() && self.editor.hasFile(errFile)) { - // Switch to file - self.editor.setCacheFile(errFile) - self.updateFiles() - } - self.editor.handleErrorClick(errLine, errCol) + self.appAPI.errorClick(errFile, errLine, errCol) }) } $error.find('.close').click(function (ev) { @@ -263,6 +255,7 @@ Renderer.prototype.contracts = function (data, source) { return $('
').append(button).append(details) } + var self = this var renderOutputModifier = function (contractName, $contractOutput) { var contract = data.contracts[contractName] if (contract.bytecode) { @@ -281,41 +274,20 @@ Renderer.prototype.contracts = function (data, source) { } } - var ctrSource = getSource(contractName, source, data) - return $contractOutput.append(getDetails(contract, ctrSource, contractName)) - } - - var self = this - - var getSource = function (contractName, source, data) { - var currentFile = self.editor.getCacheFile() - return source.sources[currentFile] - } - - var getAddress = function (cb) { - cb(null, $('#txorigin').val()) - } - - var getValue = function (cb) { - try { - var comp = $('#value').val().split(' ') - cb(null, self.executionContext.web3().toWei(comp[0], comp.slice(1).join(' '))) - } catch (e) { - cb(e) + var ctrSource = self.appAPI.currentCompiledSourceCode() + if (ctrSource) { + $contractOutput.append(getDetails(contract, ctrSource, contractName)) } + return $contractOutput } - var getGasLimit = function (cb) { - cb(null, $('#gasLimit').val()) - } - - this.udapp.reset(udappContracts, getAddress, getValue, getGasLimit, renderOutputModifier) + this.appAPI.resetDapp(udappContracts, renderOutputModifier) - var $contractOutput = this.udapp.render() + var $contractOutput = this.appAPI.renderDapp() var $txOrigin = $('#txorigin') - this.udapp.getAccounts(function (err, accounts) { + this.appAPI.getAccounts(function (err, accounts) { if (err) { self.error(err.message) } diff --git a/src/app/staticanalysis/staticAnalysisView.js b/src/app/staticanalysis/staticAnalysisView.js index 3d7d570eaf..bd0c8e3621 100644 --- a/src/app/staticanalysis/staticAnalysisView.js +++ b/src/app/staticanalysis/staticAnalysisView.js @@ -3,12 +3,10 @@ var StaticAnalysisRunner = require('./staticAnalysisRunner.js') var yo = require('yo-yo') var $ = require('jquery') -function staticAnalysisView (compilerEvent, renderer, editor, offsetToColumnConverter) { +function staticAnalysisView (appAPI, compilerEvent) { this.view = null - this.renderer = renderer - this.editor = editor + this.appAPI = appAPI this.runner = new StaticAnalysisRunner() - this.offsetToColumnConverter = offsetToColumnConverter this.modulesView = renderModules(this.runner.modules()) this.lastCompilationResult = null var self = this @@ -75,10 +73,10 @@ staticAnalysisView.prototype.run = function () { start: parseInt(split[0]), length: parseInt(split[1]) } - location = self.offsetToColumnConverter.offsetToLineColumn(location, file, self.editor, self.lastCompilationResult) + location = self.appAPI.offsetToLineColumn(location, file) location = self.lastCompilationResult.sourceList[file] + ':' + (location.start.line + 1) + ':' + (location.start.column + 1) + ':' } - self.renderer.error(location + ' ' + item.warning, warningContainer, {type: 'warning', useSpan: true, isHTML: true}) + self.appAPI.renderWarning(location + ' ' + item.warning, warningContainer, {type: 'warning', useSpan: true, isHTML: true}) }) }) if (warningContainer.html() === '') { diff --git a/src/lib/offsetToLineColumnConverter.js b/src/lib/offsetToLineColumnConverter.js index 6a9fd85f4b..fa80bef8d9 100644 --- a/src/lib/offsetToLineColumnConverter.js +++ b/src/lib/offsetToLineColumnConverter.js @@ -10,9 +10,9 @@ function offsetToColumnConverter (compilerEvent) { }) } -offsetToColumnConverter.prototype.offsetToLineColumn = function (rawLocation, file, editor, compilationResult) { +offsetToColumnConverter.prototype.offsetToLineColumn = function (rawLocation, file, compilationResult) { if (!this.lineBreakPositionsByContent[file]) { - this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(editor.getFile(compilationResult.sourceList[file])) + this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(compilationResult.source.sources[compilationResult.data.sourceList[file]]) } return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) } diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 70f4c907fd..3d453b01dd 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -13,7 +13,7 @@ var TxRunner = require('./app/txRunner') /* trigger debugRequested */ -function UniversalDApp (executionContext, options, txdebugger) { +function UniversalDApp (executionContext, options) { this.event = new EventManager() var self = this @@ -21,10 +21,7 @@ function UniversalDApp (executionContext, options, txdebugger) { self.$el = $('') self.personalMode = self.options.personalMode || false self.contracts - self.getAddress - self.getValue - self.getGasLimit - self.txdebugger = txdebugger // temporary: will not be needed anymore when we'll add memory support to the VM + self.transactionContextAPI var defaultRenderOutputModifier = function (name, content) { return content } self.renderOutputModifier = defaultRenderOutputModifier self.web3 = executionContext.web3() @@ -39,12 +36,10 @@ function UniversalDApp (executionContext, options, txdebugger) { }) } -UniversalDApp.prototype.reset = function (contracts, getAddress, getValue, getGasLimit, renderer) { +UniversalDApp.prototype.reset = function (contracts, transactionContextAPI, renderer) { this.$el.empty() this.contracts = contracts - this.getAddress = getAddress - this.getValue = getValue - this.getGasLimit = getGasLimit + this.transactionContextAPI = transactionContextAPI this.renderOutputModifier = renderer this.accounts = {} if (this.executionContext.isVM()) { @@ -734,8 +729,8 @@ UniversalDApp.prototype.runTx = function (args, cb) { function (callback) { tx.gasLimit = 3000000 - if (self.getGasLimit) { - self.getGasLimit(function (err, ret) { + if (self.transactionContextAPI.getGasLimit) { + self.transactionContextAPI.getGasLimit(function (err, ret) { if (err) { return callback(err) } @@ -751,8 +746,8 @@ UniversalDApp.prototype.runTx = function (args, cb) { function (callback) { tx.value = 0 - if (self.getValue) { - self.getValue(function (err, ret) { + if (self.transactionContextAPI.getValue) { + self.transactionContextAPI.getValue(function (err, ret) { if (err) { return callback(err) } @@ -766,8 +761,8 @@ UniversalDApp.prototype.runTx = function (args, cb) { }, // query address function (callback) { - if (self.getAddress) { - self.getAddress(function (err, ret) { + if (self.transactionContextAPI.getAddress) { + self.transactionContextAPI.getAddress(function (err, ret) { if (err) { return callback(err) } diff --git a/test-browser/tests/staticanalysis.js b/test-browser/tests/staticanalysis.js index 2ea97c9762..d71ccc4e9a 100644 --- a/test-browser/tests/staticanalysis.js +++ b/test-browser/tests/staticanalysis.js @@ -37,7 +37,7 @@ function runTests (browser) { .click('.staticanalysisView') .click('#staticanalysisView button') .waitForElementPresent('#staticanalysisresult .warning', 2000, true, function () { - dom.listSelectorContains(['Untitled:1:34: use of tx.origin', + dom.listSelectorContains(['Untitled:2:33: use of tx.origin', 'Fallback function of contract Untitled:TooMuchGas requires too much gas'], '#staticanalysisresult .warning span', browser, function () {