Merge pull request #418 from ethereum/refactor

use API [WIP]
pull/1/head
chriseth 8 years ago committed by GitHub
commit d55a6dc43b
  1. 105
      src/app.js
  2. 77
      src/app/debugger.js
  3. 10
      src/app/editor.js
  4. 54
      src/app/renderer.js
  5. 10
      src/app/staticanalysis/staticAnalysisView.js
  6. 4
      src/lib/offsetToLineColumnConverter.js
  7. 25
      src/universal-dapp.js
  8. 2
      test-browser/tests/staticanalysis.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
var staticanalysis = new StaticAnalysis(compiler.event, renderer, editor, offsetToLineColumnConverter)
// ----------------- 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 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')

@ -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

@ -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) {

@ -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 $('<div class="contractDetails"/>').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))
}
var getGasLimit = function (cb) {
cb(null, $('#gasLimit').val())
return $contractOutput
}
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)
}

@ -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() === '') {

@ -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])
}

@ -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 = $('<div class="udapp" />')
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)
}

@ -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 () {

Loading…
Cancel
Save