Merge pull request #176 from yann300/sourceLOcation

source code debugging
pull/1/head
yann300 9 years ago committed by GitHub
commit cb05660b23
  1. 6
      assets/css/browser-solidity.css
  2. 2
      package.json
  3. 54
      src/app.js
  4. 2
      src/app/compiler-worker.js
  5. 24
      src/app/compiler.js
  6. 109
      src/app/debugger.js
  7. 8
      src/app/editor.js
  8. 34
      src/app/execution-context.js
  9. 19
      src/app/renderer.js

@ -434,3 +434,9 @@ input[readonly] {
input[type="file"] {
display: none;
}
.highlightcode {
position:absolute;
z-index:20;
background-color:#F4B9B7;
}

@ -20,7 +20,7 @@
"browserify": "^13.0.0",
"csslint": "^1.0.2",
"es6-shim": "^0.35.1",
"ethereum-remix": "0.0.2-alpha.0.0.7",
"ethereum-remix": "0.0.2-alpha.0.0.8",
"ethereumjs-abi": "^0.6.4",
"ethereumjs-block": "^1.2.2",
"ethereumjs-tx": "^1.1.1",

@ -18,7 +18,6 @@ var UniversalDApp = require('./universal-dapp.js');
var Debugger = require('./app/debugger');
var FormalVerification = require('./app/formalVerification');
var EventManager = require('./lib/eventManager');
// The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event.
var filesToLoad = null;
@ -110,9 +109,6 @@ var run = function () {
el.parent().find('li').removeClass('active');
$('#optionViews').attr('class', '').addClass(cls);
el.addClass('active');
} else {
el.removeClass('active');
$('#optionViews').removeClass(cls);
}
self.event.trigger('tabChanged', [cls]);
};
@ -234,10 +230,14 @@ var run = function () {
return false;
});
function swicthToFile (file) {
editor.setCacheFile(utils.fileKey(file));
updateFiles();
}
function showFileHandler (ev) {
ev.preventDefault();
editor.setCacheFile(utils.fileKey($(this).find('.name').text()));
updateFiles();
swicthToFile($(this).find('.name').text());
return false;
}
@ -416,26 +416,24 @@ var run = function () {
}
var executionContext = new ExecutionContext();
var transactionDebugger = new Debugger('#debugger', executionContext.event);
transactionDebugger.addProvider('VM', executionContext.vm());
transactionDebugger.switchProvider('VM');
transactionDebugger.addProvider('INTERNAL', executionContext.web3());
transactionDebugger.addProvider('EXTERNAL', executionContext.web3());
transactionDebugger.onDebugRequested = function () {
selectTab($('ul#options li.debugView'));
};
var compiler = new Compiler(editor, queryParams, handleGithubCall, updateFiles);
var formalVerification = new FormalVerification($('#verificationView'), compiler.event);
var transactionDebugger = new Debugger('#debugger', editor, compiler, executionContext.event, swicthToFile);
transactionDebugger.addProvider('vm', executionContext.vm());
transactionDebugger.switchProvider('vm');
transactionDebugger.addProvider('injected', executionContext.web3());
transactionDebugger.addProvider('web3', executionContext.web3());
var udapp = new UniversalDApp(executionContext, {
removable: false,
removable_instances: true
}, transactionDebugger);
udapp.event.register('debugRequested', this, function (data) {
transactionDebugger.debug(data);
udapp.event.register('debugRequested', this, function (txResult) {
startdebugging(txResult.transactionHash);
});
var compiler = new Compiler(editor, queryParams, handleGithubCall, updateFiles);
var formalVerification = new FormalVerification($('#verificationView'), compiler.event);
var renderer = new Renderer(editor, executionContext.web3(), updateFiles, udapp, executionContext, formalVerification.event, compiler.event); // eslint-disable-line
executionContext.event.register('contextChanged', this, function (context) {
@ -446,8 +444,9 @@ var run = function () {
compiler.compile();
});
executionContext.event.register('compilerLoaded', this, function (context) {
compiler.event.register('compilerLoaded', this, function (context) {
compiler.compile();
initWithQueryParams();
});
compiler.event.register('compilerLoaded', this, function (version) {
@ -455,6 +454,23 @@ var run = function () {
compiler.compile();
});
function initWithQueryParams () {
if (queryParams.get().endpointurl) {
executionContext.setEndPointUrl(queryParams.get().endpointurl);
}
if (queryParams.get().context) {
executionContext.setContext(queryParams.get().context);
}
if (queryParams.get().debugtx) {
startdebugging(queryParams.get().debugtx);
}
}
function startdebugging (txHash) {
transactionDebugger.debug(txHash);
selectTab($('ul#options li.debugView'));
}
function setVersionText (text) {
$('#version').text(text);
}

@ -34,7 +34,7 @@ module.exports = function (self) {
break;
case 'compile':
missingInputs.length = 0;
self.postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize), missingInputs: missingInputs});
self.postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize), missingInputs: missingInputs, source: data.source});
break;
}
}, false);

@ -91,24 +91,26 @@ function Compiler (editor, queryParams, handleGithubCall, updateFiles) {
result = { error: 'Uncaught JavaScript exception:\n' + exception };
}
compilationFinished(result, missingInputs);
compilationFinished(result, missingInputs, source);
};
onCompilerLoaded(compiler.version());
}
}
function compilationFinished (data, missingInputs) {
this.lastCompilationResult = {
data: null,
source: null
};
function compilationFinished (data, missingInputs, source) {
var noFatalErrors = true; // ie warnings are ok
if (data['error'] !== undefined) {
self.event.trigger('compilationFinished', [false, [data['error']], editor.getValue()]);
if (utils.errortype(data['error']) !== 'warning') {
noFatalErrors = false;
}
}
if (data['errors'] !== undefined) {
self.event.trigger('compilationFinished', [false, data['errors'], editor.getValue()]);
data['errors'].forEach(function (err) {
if (utils.errortype(err) !== 'warning') {
noFatalErrors = false;
@ -116,10 +118,16 @@ function Compiler (editor, queryParams, handleGithubCall, updateFiles) {
});
}
if (missingInputs !== undefined && missingInputs.length > 0) {
if (!noFatalErrors) {
self.event.trigger('compilationFinished', [false, data, source]);
} else if (missingInputs !== undefined && missingInputs.length > 0) {
compile(missingInputs);
} else if (noFatalErrors) {
self.event.trigger('compilationFinished', [true, data, editor.getValue()]);
} else {
self.lastCompilationResult = {
data: data,
source: source
};
self.event.trigger('compilationFinished', [true, data, source]);
}
}
@ -170,7 +178,7 @@ function Compiler (editor, queryParams, handleGithubCall, updateFiles) {
} catch (exception) {
result = { 'error': 'Invalid JSON output from the compiler: ' + exception };
}
compilationFinished(result, data.missingInputs);
compilationFinished(result, data.missingInputs, data.source);
break;
}
});

@ -1,39 +1,134 @@
var remix = require('ethereum-remix');
var utils = require('./utils');
var ace = require('brace');
var Range = ace.acequire('ace/range').Range;
function Debugger (id, executionContextEvent) {
/**
* Manage remix and source highlighting
*/
function Debugger (id, editor, compiler, executionContextEvent, switchToFile) {
this.el = document.querySelector(id);
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.cache = new Cache();
var self = this;
executionContextEvent.register('contextChanged', this, function (context) {
context = context === 'vm' ? 'VM' : context;
context = context === 'injected' ? 'EXTERNAL' : context;
context = context === 'web3' ? 'INTERNAL' : context;
self.switchProvider(context);
});
this.lastCompilationResult = null;
this.debugger.register('newTraceLoaded', this, function () {
self.cache.clear();
self.lastCompilationResult = self.compiler.lastCompilationResult;
});
this.debugger.register('traceUnloaded', this, function () {
self.removeCurrentMarker();
self.cache.clear();
});
this.editor.onChangeSetup(function () {
if (arguments.length > 0) { // if arguments.length === 0 this is a session change, we don't want to stop debugging in that case
self.debugger.unLoad();
}
});
// register selected code item, highlight the corresponding source location
this.debugger.codeManager.register('changed', this, function (code, address, index) {
if (self.lastCompilationResult) {
this.debugger.sourceLocationTracker.getSourceLocation(address, index, self.lastCompilationResult.data.contracts, function (error, rawLocation) {
if (!error) {
if (!self.cache.lineBreakPositionsByContent[address]) {
self.cache.lineBreakPositionsByContent[address] = self.sourceMappingDecoder.getLinebreakPositions(self.editor.getFile(self.lastCompilationResult.data.sourceList[rawLocation.file]));
}
var lineColumnPos = self.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, self.cache.lineBreakPositionsByContent[address]);
self.highlight(lineColumnPos, rawLocation);
} else {
self.removeCurrentMarker();
}
});
}
});
}
Debugger.prototype.debug = function (receipt) {
if (this.onDebugRequested) this.onDebugRequested();
/**
* Start debugging using Remix
*
* @param {String} txHash - hash of the transaction
*/
Debugger.prototype.debug = function (txHash) {
var self = this;
this.debugger.web3().eth.getTransaction(receipt.transactionHash, function (error, tx) {
this.debugger.web3().eth.getTransaction(txHash, function (error, tx) {
if (!error) {
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) {
var name = utils.fileNameFromKey(this.editor.getCacheFile()); // current opened tab
var source = this.lastCompilationResult.data.sourceList[rawLocation.file]; // auto switch to that tab
this.removeCurrentMarker();
if (name !== source) {
this.switchToFile(source); // command the app to swicth to the next file
}
this.currentRange = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column);
this.currentMarker = this.editor.addMarker(this.currentRange, 'highlightcode');
};
/**
* add a new web3 provider to remix
*
* @param {String} type - type/name of the provider to add
* @param {Object} obj - provider
*/
Debugger.prototype.addProvider = function (type, obj) {
this.debugger.addProvider(type, obj);
};
/**
* switch the provider
*
* @param {String} type - type/name of the provider to use
*/
Debugger.prototype.switchProvider = function (type) {
this.debugger.switchProvider(type);
};
/**
* get the current provider
*/
Debugger.prototype.web3 = function (type) {
return this.debugger.web3();
};
/**
* unhighlight the current highlighted statement
*/
Debugger.prototype.removeCurrentMarker = function () {
if (this.currentMarker) {
this.editor.removeMarker(this.currentMarker);
this.currentMarker = null;
}
};
function Cache () {
this.contentLineBreakPosition = {};
}
Cache.prototype.clear = function () {
this.lineBreakPositionsByContent = {};
};
module.exports = Debugger;

@ -15,6 +15,14 @@ function Editor (loadingFromGist, storage) {
setupStuff(getFiles());
this.addMarker = function (range, cssClass) {
return editor.session.addMarker(range, cssClass);
};
this.removeMarker = function (markerId) {
editor.session.removeMarker(markerId);
};
this.newFile = function () {
var untitledCount = '';
while (storage.exists(SOL_CACHE_UNTITLED + untitledCount)) {

@ -21,7 +21,6 @@ vm.stateManager.checkpoint();
/*
trigger contextChanged, web3EndpointChanged
*/
function ExecutionContext () {
var self = this;
this.event = new EventManager();
@ -39,6 +38,16 @@ function ExecutionContext () {
return vm;
};
this.setEndPointUrl = function (url) {
$web3endpoint.val(url);
};
this.setContext = function (context) {
executionContext = context;
executionContextChange(context);
setExecutionContextRadio();
};
var $injectedToggle = $('#injected-mode');
var $vmToggle = $('#vm-mode');
var $web3Toggle = $('#web3-mode');
@ -50,9 +59,9 @@ function ExecutionContext () {
setExecutionContextRadio();
$injectedToggle.on('change', executionContextChange);
$vmToggle.on('change', executionContextChange);
$web3Toggle.on('change', executionContextChange);
$injectedToggle.on('change', executionContextUIChange);
$vmToggle.on('change', executionContextUIChange);
$web3Toggle.on('change', executionContextUIChange);
$web3endpoint.on('change', function () {
setProviderFromEndpoint();
if (executionContext === 'web3') {
@ -60,20 +69,23 @@ function ExecutionContext () {
}
});
function executionContextChange (ev) {
if (ev.target.value === 'web3' && !confirm('Are you sure you want to connect to a local ethereum node?')) {
function executionContextUIChange (ev) {
executionContextChange(ev.target.value);
}
function executionContextChange (context) {
if (context === 'web3' && !confirm('Are you sure you want to connect to a local ethereum node?')) {
setExecutionContextRadio();
} else if (ev.target.value === 'injected' && injectedProvider === undefined) {
} else if (context === 'injected' && injectedProvider === undefined) {
setExecutionContextRadio();
} else {
executionContext = ev.target.value;
if (executionContext === 'web3') {
if (context === 'web3') {
setProviderFromEndpoint();
self.event.trigger('contextChanged', ['web3']);
} else if (executionContext === 'injected') {
} else if (context === 'injected') {
web3.setProvider(injectedProvider);
self.event.trigger('contextChanged', ['injected']);
} else if (executionContext === 'vm') {
} else if (context === 'vm') {
vm.stateManager.revert(function () {
vm.stateManager.checkpoint();
});

@ -20,9 +20,14 @@ function Renderer (editor, web3, updateFiles, udapp, executionContext, formalVer
if (success) {
self.contracts(data, source);
} else {
data.forEach(function (err) {
self.error(err);
});
if (data['error']) {
self.error(data['error']);
}
if (data['errors']) {
data['errors'].forEach(function (err) {
self.error(err);
});
}
}
});
}
@ -93,11 +98,17 @@ Renderer.prototype.contracts = function (data, source) {
$contractOutput.append(uiHelper.textRow('Web3 deploy', uiHelper.gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode), 'deploy'));
$contractOutput.append(uiHelper.textRow('uDApp', combined(contractName, contract['interface'], contract.bytecode), 'deploy'));
}
return $contractOutput.append(uiHelper.getDetails(contract, source, contractName));
var ctrSource = getSource(contractName, source, data);
return $contractOutput.append(uiHelper.getDetails(contract, ctrSource, contractName));
};
// //
var self = this;
var getSource = function (contractName, source, data) {
var currentFile = utils.fileNameFromKey(self.editor.getCacheFile());
return source.sources[currentFile];
};
var getAddress = function () { return $('#txorigin').val(); };
var getValue = function () {

Loading…
Cancel
Save