Merge pull request #167 from yann300/refactoring_event

Refactoring:: add events
pull/1/head
chriseth 9 years ago committed by GitHub
commit 0611230d62
  1. 65
      src/app.js
  2. 50
      src/app/compiler.js
  3. 10
      src/app/debugger.js
  4. 35
      src/app/execution-context.js
  5. 37
      src/app/formalVerification.js
  6. 350
      src/app/renderer.js
  7. 161
      src/app/ui-helper.js
  8. 64
      src/lib/eventManager.js
  9. 89
      src/universal-dapp.js
  10. 4
      test/compiler-test.js

@ -13,9 +13,10 @@ var Editor = require('./app/editor');
var Renderer = require('./app/renderer'); var Renderer = require('./app/renderer');
var Compiler = require('./app/compiler'); var Compiler = require('./app/compiler');
var ExecutionContext = require('./app/execution-context'); var ExecutionContext = require('./app/execution-context');
var UniversalDApp = require('./universal-dapp.js');
var Debugger = require('./app/debugger'); var Debugger = require('./app/debugger');
var FormalVerification = require('./app/formalVerification'); var FormalVerification = require('./app/formalVerification');
var EthJSVM = require('ethereumjs-vm'); var EventManager = require('./lib/eventManager');
// The event listener needs to be registered as early as possible, because the // The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event. // parent will send the message upon the "load" event.
@ -26,8 +27,12 @@ window.addEventListener('message', function (ev) {
loadFilesCallback(ev.data[1]); loadFilesCallback(ev.data[1]);
} }
}, false); }, false);
/*
trigger tabChanged
*/
var run = function () { var run = function () {
var self = this;
this.event = new EventManager();
var storage = new Storage(updateFiles); var storage = new Storage(updateFiles);
function loadFiles (files) { function loadFiles (files) {
@ -108,6 +113,7 @@ var run = function () {
el.removeClass('active'); el.removeClass('active');
$('#optionViews').removeClass(cls); $('#optionViews').removeClass(cls);
} }
self.event.trigger('tabChanged', [cls]);
}; };
// ------------------ gist publish -------------- // ------------------ gist publish --------------
@ -199,7 +205,7 @@ var run = function () {
$fileNameInputEl.off('keyup'); $fileNameInputEl.off('keyup');
if (newName !== originalName && confirm( if (newName !== originalName && confirm(
storage.exists(utils.fileKey(newName)) storage.exists(utils.fileKey(newName))
? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?' ? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?'
: 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) { : 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) {
storage.rename(utils.fileKey(originalName), utils.fileKey(newName)); storage.rename(utils.fileKey(originalName), utils.fileKey(newName));
@ -279,7 +285,7 @@ var run = function () {
} }
// function widthOfHidden () { // function widthOfHidden () {
// return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi()); // return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi())
// } // }
function widthOfVisible () { function widthOfVisible () {
@ -405,11 +411,8 @@ var run = function () {
setEditorSize(hidingRHP ? 0 : storage.getEditorSize()); setEditorSize(hidingRHP ? 0 : storage.getEditorSize());
$('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP); $('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP);
$('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP); $('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP);
if (!hidingRHP) compiler.compile();
}); });
function getHidingRHP () { return hidingRHP; }
// ----------------- editor resize --------------- // ----------------- editor resize ---------------
function onResize () { function onResize () {
@ -432,20 +435,46 @@ var run = function () {
$('#output').append($('<div/>').append($('<pre/>').text('Loading github.com/' + root + '/' + path + ' ...'))); $('#output').append($('<div/>').append($('<pre/>').text('Loading github.com/' + root + '/' + path + ' ...')));
return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb); return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb);
} }
var transactionDebugger = new Debugger('#debugger');
var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true }); var executionContext = new ExecutionContext();
vm.stateManager.checkpoint(); var transactionDebugger = new Debugger('#debugger', executionContext.event);
transactionDebugger.addProvider('VM', vm); transactionDebugger.addProvider('VM', executionContext.vm());
transactionDebugger.switchProvider('VM'); transactionDebugger.switchProvider('VM');
var executionContext = new ExecutionContext(transactionDebugger); transactionDebugger.addProvider('INTERNAL', executionContext.web3());
transactionDebugger.addProvider('EXTERNAL', executionContext.web3()); transactionDebugger.addProvider('EXTERNAL', executionContext.web3());
transactionDebugger.onDebugRequested = function () { transactionDebugger.onDebugRequested = function () {
selectTab($('ul#options li.debugView')); selectTab($('ul#options li.debugView'));
}; };
var renderer = new Renderer(editor, executionContext, updateFiles, transactionDebugger, vm);
var formalVerification = new FormalVerification($('#verificationView'), renderer); var udapp = new UniversalDApp(executionContext, {
var compiler = new Compiler(editor, renderer, queryParams, handleGithubCall, $('#output'), getHidingRHP, formalVerification, updateFiles); removable: false,
executionContext.setCompiler(compiler); removable_instances: true
}, transactionDebugger);
udapp.event.register('debugRequested', this, function (data) {
transactionDebugger.debug(data);
});
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) {
compiler.compile();
});
executionContext.event.register('web3EndpointChanged', this, function (context) {
compiler.compile();
});
executionContext.event.register('compilerLoaded', this, function (context) {
compiler.compile();
});
compiler.event.register('compilerLoaded', this, function (version) {
setVersionText(version);
compiler.compile();
});
function setVersionText (text) { function setVersionText (text) {
$('#version').text(text); $('#version').text(text);
@ -462,9 +491,9 @@ var run = function () {
// Workers cannot load js on "file:"-URLs and we get a // Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium, // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case. // resort to non-worker version in that case.
compiler.loadVersion(true, version, setVersionText); compiler.loadVersion(true, version);
} else { } else {
compiler.loadVersion(false, version, setVersionText); compiler.loadVersion(false, version);
} }
}; };

@ -5,7 +5,15 @@ var utils = require('./utils');
var Base64 = require('js-base64').Base64; var Base64 = require('js-base64').Base64;
function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, hidingRHP, formalVerification, updateFiles) { var EventManager = require('../lib/eventManager');
/*
trigger compilationFinished, compilerLoaded, compilationStarted
*/
function Compiler (editor, queryParams, handleGithubCall, updateFiles) {
var self = this;
this.event = new EventManager();
var compileJSON; var compileJSON;
var compilerAcceptsMultipleFiles; var compilerAcceptsMultipleFiles;
@ -36,19 +44,15 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
var compile = function (missingInputs) { var compile = function (missingInputs) {
editor.clearAnnotations(); editor.clearAnnotations();
outputField.empty(); self.event.trigger('compilationStarted', []);
if (formalVerification) {
formalVerification.compiling();
}
var input = editor.getValue(); var input = editor.getValue();
editor.setCacheFileContent(input); editor.setCacheFileContent(input);
var files = {}; var files = {};
files[utils.fileNameFromKey(editor.getCacheFile())] = input; files[utils.fileNameFromKey(editor.getCacheFile())] = input;
gatherImports(files, missingInputs, function (input, error) { gatherImports(files, missingInputs, function (input, error) {
outputField.empty();
if (input === null) { if (input === null) {
renderer.error(error); this.event.trigger('compilationFinished', [false, error, editor.getValue()]);
} else { } else {
var optimize = queryParams.get().optimize; var optimize = queryParams.get().optimize;
compileJSON(input, optimize ? 1 : 0); compileJSON(input, optimize ? 1 : 0);
@ -62,13 +66,12 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
} }
this.setCompileJSON = setCompileJSON; // this is exposed for testing this.setCompileJSON = setCompileJSON; // this is exposed for testing
function onCompilerLoaded (setVersionText, version) { function onCompilerLoaded (version) {
setVersionText(version);
previousInput = ''; previousInput = '';
onChange(); self.event.trigger('compilerLoaded', [version]);
} }
function onInternalCompilerLoaded (setVersionText) { function onInternalCompilerLoaded () {
if (worker === null) { if (worker === null) {
var compiler = solc(window.Module); var compiler = solc(window.Module);
@ -91,7 +94,7 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
compilationFinished(result, missingInputs); compilationFinished(result, missingInputs);
}; };
onCompilerLoaded(setVersionText, compiler.version()); onCompilerLoaded(compiler.version());
} }
} }
@ -99,14 +102,14 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
var noFatalErrors = true; // ie warnings are ok var noFatalErrors = true; // ie warnings are ok
if (data['error'] !== undefined) { if (data['error'] !== undefined) {
renderer.error(data['error']); self.event.trigger('compilationFinished', [false, [data['error']], editor.getValue()]);
if (utils.errortype(data['error']) !== 'warning') { if (utils.errortype(data['error']) !== 'warning') {
noFatalErrors = false; noFatalErrors = false;
} }
} }
if (data['errors'] !== undefined) { if (data['errors'] !== undefined) {
self.event.trigger('compilationFinished', [false, data['errors'], editor.getValue()]);
data['errors'].forEach(function (err) { data['errors'].forEach(function (err) {
renderer.error(err);
if (utils.errortype(err) !== 'warning') { if (utils.errortype(err) !== 'warning') {
noFatalErrors = false; noFatalErrors = false;
} }
@ -115,13 +118,12 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
if (missingInputs !== undefined && missingInputs.length > 0) { if (missingInputs !== undefined && missingInputs.length > 0) {
compile(missingInputs); compile(missingInputs);
} else if (noFatalErrors && !hidingRHP()) { } else if (noFatalErrors) {
renderer.contracts(data, editor.getValue()); self.event.trigger('compilationFinished', [true, data, editor.getValue()]);
formalVerification.compilationFinished(data);
} }
} }
this.loadVersion = function (usingWorker, version, setVersionText) { this.loadVersion = function (usingWorker, version) {
var url; var url;
if (version !== 'soljson.js') { if (version !== 'soljson.js') {
url = 'https://ethereum.github.io/solc-bin/bin/' + version; url = 'https://ethereum.github.io/solc-bin/bin/' + version;
@ -131,13 +133,13 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')); console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker'));
if (usingWorker) { if (usingWorker) {
loadWorker(url, setVersionText); loadWorker(url);
} else { } else {
loadInternal(url, setVersionText); loadInternal(url);
} }
}; };
function loadInternal (url, setVersionText) { function loadInternal (url) {
delete window.Module; delete window.Module;
// Set a safe fallback until the new one is loaded // Set a safe fallback until the new one is loaded
setCompileJSON(function (source, optimize) { compilationFinished('{}'); }); setCompileJSON(function (source, optimize) { compilationFinished('{}'); });
@ -151,11 +153,11 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
return; return;
} }
window.clearInterval(check); window.clearInterval(check);
onInternalCompilerLoaded(setVersionText); onInternalCompilerLoaded();
}, 200); }, 200);
} }
function loadWorker (url, setVersionText) { function loadWorker (url) {
if (worker !== null) { if (worker !== null) {
worker.terminate(); worker.terminate();
} }
@ -165,7 +167,7 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField,
switch (data.cmd) { switch (data.cmd) {
case 'versionLoaded': case 'versionLoaded':
compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles; compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
onCompilerLoaded(setVersionText, data.data); onCompilerLoaded(data.data);
break; break;
case 'compiled': case 'compiled':
var result; var result;

@ -1,9 +1,17 @@
var remix = require('ethereum-remix'); var remix = require('ethereum-remix');
function Debugger (id) { function Debugger (id, executionContextEvent) {
this.el = document.querySelector(id); this.el = document.querySelector(id);
this.debugger = new remix.ui.Debugger(); this.debugger = new remix.ui.Debugger();
this.el.appendChild(this.debugger.render()); this.el.appendChild(this.debugger.render());
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);
});
} }
Debugger.prototype.debug = function (receipt) { Debugger.prototype.debug = function (receipt) {

@ -2,6 +2,8 @@
var $ = require('jquery'); var $ = require('jquery');
var Web3 = require('web3'); var Web3 = require('web3');
var EventManager = require('../lib/eventManager');
var EthJSVM = require('ethereumjs-vm');
var injectedProvider; var injectedProvider;
@ -13,14 +15,17 @@ if (typeof window.web3 !== 'undefined') {
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
} }
function ExecutionContext (_txDebugger) { var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true });
var txDebugger = _txDebugger; vm.stateManager.checkpoint();
var compiler;
var executionContext = injectedProvider ? 'injected' : 'vm';
this.setCompiler = function (_compiler) { /*
compiler = _compiler; trigger contextChanged, web3EndpointChanged
}; */
function ExecutionContext () {
var self = this;
this.event = new EventManager();
var executionContext = injectedProvider ? 'injected' : 'vm';
this.isVM = function () { this.isVM = function () {
return executionContext === 'vm'; return executionContext === 'vm';
@ -30,6 +35,10 @@ function ExecutionContext (_txDebugger) {
return web3; return web3;
}; };
this.vm = function () {
return vm;
};
var $injectedToggle = $('#injected-mode'); var $injectedToggle = $('#injected-mode');
var $vmToggle = $('#vm-mode'); var $vmToggle = $('#vm-mode');
var $web3Toggle = $('#web3-mode'); var $web3Toggle = $('#web3-mode');
@ -47,7 +56,7 @@ function ExecutionContext (_txDebugger) {
$web3endpoint.on('change', function () { $web3endpoint.on('change', function () {
setProviderFromEndpoint(); setProviderFromEndpoint();
if (executionContext === 'web3') { if (executionContext === 'web3') {
compiler.compile(); self.event.trigger('web3EndpointChanged');
} }
}); });
@ -60,15 +69,17 @@ function ExecutionContext (_txDebugger) {
executionContext = ev.target.value; executionContext = ev.target.value;
if (executionContext === 'web3') { if (executionContext === 'web3') {
setProviderFromEndpoint(); setProviderFromEndpoint();
txDebugger.switchProvider('EXTERNAL'); self.event.trigger('contextChanged', ['web3']);
} else if (executionContext === 'injected') { } else if (executionContext === 'injected') {
web3.setProvider(injectedProvider); web3.setProvider(injectedProvider);
txDebugger.switchProvider('EXTERNAL'); self.event.trigger('contextChanged', ['injected']);
} else if (executionContext === 'vm') { } else if (executionContext === 'vm') {
txDebugger.switchProvider('VM'); vm.stateManager.revert(function () {
vm.stateManager.checkpoint();
});
self.event.trigger('contextChanged', ['vm']);
} }
} }
compiler.compile();
} }
function setProviderFromEndpoint () { function setProviderFromEndpoint () {

@ -1,37 +1,44 @@
var $ = require('jquery'); var $ = require('jquery');
var EventManager = require('../lib/eventManager');
function FormalVerification (outputElement, renderer) { /*
trigger compilationFinished
*/
function FormalVerification (outputElement, compilerEvent) {
this.event = new EventManager();
this.outputElement = outputElement; this.outputElement = outputElement;
this.renderer = renderer; var self = this;
} compilerEvent.register('compilationFinished', this, function (success, data, source) {
if (success) {
FormalVerification.prototype.compiling = function () { self.compilationFinished(data);
$('#formalVerificationInput', this.outputElement) }
});
compilerEvent.register('compilationStarted', this, function () {
$('#formalVerificationInput', self.outputElement)
.val('') .val('')
.hide(); .hide();
$('#formalVerificationErrors').empty(); $('#formalVerificationErrors').empty();
}; });
}
FormalVerification.prototype.compilationFinished = function (compilationResult) { FormalVerification.prototype.compilationFinished = function (compilationResult) {
if (compilationResult.formal === undefined) { if (compilationResult.formal === undefined) {
this.renderer.error( this.event.trigger('compilationFinished', [false, 'Formal verification not supported by this compiler version.', $('#formalVerificationErrors'), true]);
'Formal verification not supported by this compiler version.',
$('#formalVerificationErrors'),
true
);
} else { } else {
if (compilationResult.formal['why3'] !== undefined) { if (compilationResult.formal['why3'] !== undefined) {
$('#formalVerificationInput', this.outputElement).val( $('#formalVerificationInput', this.outputElement).val(
'(* copy this to http://why3.lri.fr/try/ *)' + '(* copy this to http://why3.lri.fr/try/ *)' +
compilationResult.formal['why3'] compilationResult.formal['why3']
) )
.show(); .show();
} }
if (compilationResult.formal.errors !== undefined) { if (compilationResult.formal.errors !== undefined) {
var errors = compilationResult.formal.errors; var errors = compilationResult.formal.errors;
for (var i = 0; i < errors.length; i++) { for (var i = 0; i < errors.length; i++) {
this.renderer.error(errors[i], $('#formalVerificationErrors'), true); this.event.trigger('compilationFinished', [false, errors[i], $('#formalVerificationErrors'), true]);
} }
} else {
this.event.trigger('compilationFinished', [true, null, null, true]);
} }
} }
}; };

@ -1,276 +1,134 @@
var $ = require('jquery'); var $ = require('jquery');
var UniversalDApp = require('../universal-dapp.js');
var utils = require('./utils'); var utils = require('./utils');
var uiHelper = require('./ui-helper');
function Renderer (editor, executionContext, updateFiles, transactionDebugger, vm) {
var detailsOpen = {}; function Renderer (editor, web3, updateFiles, udapp, executionContext, formalVerificationEvent, compilerEvent) {
this.editor = editor;
function renderError (message, container, noAnnotations) { this.web3 = web3;
var type = utils.errortype(message); this.updateFiles = updateFiles;
var $pre = $('<pre />').text(message); this.udapp = udapp;
var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre); this.executionContext = executionContext;
if (container === undefined) { var self = this;
container = $('#output'); formalVerificationEvent.register('compilationFinished', this, function (success, message, container, noAnnotations) {
} if (!success) {
container.append($error); self.error(message, container, noAnnotations);
var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /); }
if (err) { });
var errFile = err[1]; compilerEvent.register('compilationFinished', this, function (success, data, source) {
var errLine = parseInt(err[2], 10) - 1; $('#output').empty();
var errCol = err[4] ? parseInt(err[4], 10) : 0; if (success) {
if (!noAnnotations && (errFile === '' || errFile === utils.fileNameFromKey(editor.getCacheFile()))) { self.contracts(data, source);
editor.addAnnotation({ } else {
row: errLine, data.forEach(function (err) {
column: errCol, self.error(err);
text: message,
type: type
});
}
$error.click(function (ev) {
if (errFile !== '' && errFile !== utils.fileNameFromKey(editor.getCacheFile()) && editor.hasFile(errFile)) {
// Switch to file
editor.setCacheFile(utils.fileKey(errFile));
updateFiles();
// @TODO could show some error icon in files with errors
}
editor.handleErrorClick(errLine, errCol);
});
$error.find('.close').click(function (ev) {
ev.preventDefault();
$error.remove();
return false;
}); });
} }
} });
this.error = renderError; }
var combined = function (contractName, jsonInterface, bytecode) {
return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }]);
};
function renderContracts (data, source) { Renderer.prototype.error = function (message, container, noAnnotations) {
var udappContracts = []; var type = utils.errortype(message);
for (var contractName in data.contracts) { var $pre = $('<pre />').text(message);
var contract = data.contracts[contractName]; var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre);
udappContracts.push({ if (container === undefined) {
name: contractName, container = $('#output');
interface: contract['interface'], }
bytecode: contract.bytecode container.append($error);
var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /);
if (err) {
var errFile = err[1];
var errLine = parseInt(err[2], 10) - 1;
var errCol = err[4] ? parseInt(err[4], 10) : 0;
if (!noAnnotations && (errFile === '' || errFile === utils.fileNameFromKey(this.editor.getCacheFile()))) {
this.editor.addAnnotation({
row: errLine,
column: errCol,
text: message,
type: type
}); });
} }
$error.click(function (ev) {
vm.stateManager.revert(function () { if (errFile !== '' && errFile !== utils.fileNameFromKey(this.editor.getCacheFile()) && this.editor.hasFile(errFile)) {
vm.stateManager.checkpoint(); // Switch to file
}); this.editor.setCacheFile(utils.fileKey(errFile));
var dapp = new UniversalDApp(udappContracts, { this.updateFiles();
mode: executionContext.isVM() ? 'vm' : 'web3', // @TODO could show some error icon in files with errors
web3: executionContext.web3(),
removable: false,
getAddress: function () { return $('#txorigin').val(); },
getValue: function () {
var comp = $('#value').val().split(' ');
return executionContext.web3().toWei(comp[0], comp.slice(1).join(' '));
},
getGasLimit: function () { return $('#gasLimit').val(); },
removable_instances: true,
renderOutputModifier: function (contractName, $contractOutput) {
var contract = data.contracts[contractName];
if (contract.bytecode) {
$contractOutput.append(textRow('Bytecode', contract.bytecode));
}
$contractOutput.append(textRow('Interface', contract['interface']));
if (contract.bytecode) {
$contractOutput.append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode), 'deploy'));
$contractOutput.append(textRow('uDApp', combined(contractName, contract['interface'], contract.bytecode), 'deploy'));
}
return $contractOutput.append(getDetails(contract, source, contractName));
} }
}, transactionDebugger, vm); this.editor.handleErrorClick(errLine, errCol);
});
var $contractOutput = dapp.render(); $error.find('.close').click(function (ev) {
ev.preventDefault();
var $txOrigin = $('#txorigin'); $error.remove();
return false;
function renderAccounts (err, accounts) { });
if (err) { }
renderError(err.message); };
}
if (accounts && accounts[0]) { Renderer.prototype.contracts = function (data, source) {
$txOrigin.empty(); var udappContracts = [];
for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])); } for (var contractName in data.contracts) {
$txOrigin.val(accounts[0]); var contract = data.contracts[contractName];
} else { udappContracts.push({
$txOrigin.val('unknown'); name: contractName,
} interface: contract['interface'],
} bytecode: contract.bytecode
});
dapp.getAccounts(renderAccounts);
$contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide'); });
$('#output').append($contractOutput);
$('.col2 input,textarea').click(function () { this.select(); });
} }
this.contracts = renderContracts;
var tableRowItems = function (first, second, cls) { // rendering function used by udapp. they need data and source
return $('<div class="crow"/>') var combined = function (contractName, jsonInterface, bytecode) {
.addClass(cls) return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }]);
.append($('<div class="col1">').append(first))
.append($('<div class="col2">').append(second));
};
var tableRow = function (description, data) {
return tableRowItems(
$('<span/>').text(description),
$('<input readonly="readonly"/>').val(data));
};
var textRow = function (description, data, cls) {
return tableRowItems(
$('<strong/>').text(description),
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
cls);
}; };
var getDetails = function (contract, source, contractName) { var renderOutputModifier = function (contractName, $contractOutput) {
var button = $('<button>Toggle Details</button>'); var contract = data.contracts[contractName];
var details = $('<div style="display: none;"/>') if (contract.bytecode) {
.append(tableRow('Solidity Interface', contract.solidity_interface)); $contractOutput.append(uiHelper.textRow('Bytecode', contract.bytecode));
if (contract.opcodes !== '') {
details.append(tableRow('Opcodes', contract.opcodes));
} }
var funHashes = ''; $contractOutput.append(uiHelper.textRow('Interface', contract['interface']));
for (var fun in contract.functionHashes) {
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
}
details.append($('<span class="col1">Functions</span>'));
details.append($('<pre/>').text(funHashes));
var gasEstimates = formatGasEstimates(contract.gasEstimates);
if (gasEstimates) {
details.append($('<span class="col1">Gas Estimates</span>'));
details.append($('<pre/>').text(gasEstimates));
}
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) { if (contract.bytecode) {
details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode)); $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 self = this;
if (contract.assembly !== null) { var getAddress = function () { return $('#txorigin').val(); };
details.append($('<span class="col1">Assembly</span>'));
var assembly = $('<pre/>').text(formatAssemblyText(contract.assembly, '', source));
details.append(assembly);
}
button.click(function () { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); }); var getValue = function () {
if (detailsOpen[contractName]) { var comp = $('#value').val().split(' ');
details.show(); return self.executionContext.web3().toWei(comp[0], comp.slice(1).join(' '));
}
return $('<div class="contractDetails"/>').append(button).append(details);
}; };
var formatGasEstimates = function (data) { var getGasLimit = function () { return $('#gasLimit').val(); };
// FIXME: the whole gasEstimates object should be nil instead
if (data.creation === undefined && data.external === undefined && data.internal === undefined) {
return;
}
var gasToText = function (g) { return g === null ? 'unknown' : g; };
var text = '';
var fun;
if ('creation' in data) {
text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n';
}
if ('external' in data) { this.udapp.reset(udappContracts, getAddress, getValue, getGasLimit, renderOutputModifier);
text += 'External:\n';
for (fun in data.external) {
text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n';
}
}
if ('internal' in data) { var $contractOutput = this.udapp.render();
text += 'Internal:\n';
for (fun in data.internal) {
text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n';
}
}
return text; var $txOrigin = $('#txorigin');
};
var formatAssemblyText = function (asm, prefix, source) { this.udapp.getAccounts(function (err, accounts) {
if (typeof asm === typeof '' || asm === null || asm === undefined) { if (err) {
return prefix + asm + '\n'; self.error(err.message);
} }
var text = prefix + '.code\n'; if (accounts && accounts[0]) {
$.each(asm['.code'], function (i, item) { $txOrigin.empty();
var v = item.value === undefined ? '' : item.value; for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])); }
var src = ''; $txOrigin.val(accounts[0]);
if (item.begin !== undefined && item.end !== undefined) { } else {
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g'); $txOrigin.val('unknown');
}
if (src.length > 30) {
src = src.slice(0, 30) + '...';
}
if (item.name !== 'tag') {
text += ' ';
}
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n';
});
text += prefix + '.data\n';
if (asm['.data']) {
$.each(asm['.data'], function (i, item) {
text += ' ' + prefix + '' + i + ':\n';
text += formatAssemblyText(item, prefix + ' ', source);
});
} }
});
return text; $contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide'); });
}; $('#output').append($contractOutput);
$('.col2 input,textarea').click(function () { this.select(); });
function gethDeploy (contractName, jsonInterface, bytecode) { };
var code = '';
var funABI = getConstructorInterface(JSON.parse(jsonInterface));
funABI.inputs.forEach(function (inp) {
code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n';
});
code += 'var ' + contractName + 'Contract = web3.eth.contract(' + jsonInterface.replace('\n', '') + ');' +
'\nvar ' + contractName + ' = ' + contractName + 'Contract.new(';
funABI.inputs.forEach(function (inp) {
code += '\n ' + inp.name + ',';
});
code += '\n {' +
'\n from: web3.eth.accounts[0], ' +
'\n data: \'' + bytecode + '\', ' +
'\n gas: 4700000' +
'\n }, function (e, contract){' +
'\n console.log(e, contract);' +
'\n if (typeof contract.address !== \'undefined\') {' +
'\n console.log(\'Contract mined! address: \' + contract.address + \' transactionHash: \' + contract.transactionHash);' +
'\n }' +
'\n })';
return code;
}
function getConstructorInterface (abi) {
var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] };
for (var i = 0; i < abi.length; i++) {
if (abi[i].type === 'constructor') {
funABI.inputs = abi[i].inputs || [];
break;
}
}
return funABI;
}
}
module.exports = Renderer; module.exports = Renderer;

@ -0,0 +1,161 @@
var $ = require('jquery');
module.exports = {
tableRowItems: function (first, second, cls) {
return $('<div class="crow"/>')
.addClass(cls)
.append($('<div class="col1">').append(first))
.append($('<div class="col2">').append(second));
},
tableRow: function (description, data) {
return this.tableRowItems(
$('<span/>').text(description),
$('<input readonly="readonly"/>').val(data));
},
textRow: function (description, data, cls) {
return this.tableRowItems(
$('<strong/>').text(description),
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
cls);
},
formatAssemblyText: function (asm, prefix, source) {
var self = this;
if (typeof asm === typeof '' || asm === null || asm === undefined) {
return prefix + asm + '\n';
}
var text = prefix + '.code\n';
$.each(asm['.code'], function (i, item) {
var v = item.value === undefined ? '' : item.value;
var src = '';
if (item.begin !== undefined && item.end !== undefined) {
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g');
}
if (src.length > 30) {
src = src.slice(0, 30) + '...';
}
if (item.name !== 'tag') {
text += ' ';
}
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n';
});
text += prefix + '.data\n';
if (asm['.data']) {
$.each(asm['.data'], function (i, item) {
text += ' ' + prefix + '' + i + ':\n';
text += self.formatAssemblyText(item, prefix + ' ', source);
});
}
return text;
},
gethDeploy: function (contractName, jsonInterface, bytecode) {
var code = '';
var funABI = this.getConstructorInterface(JSON.parse(jsonInterface));
funABI.inputs.forEach(function (inp) {
code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n';
});
code += 'var ' + contractName + 'Contract = web3.eth.contract(' + jsonInterface.replace('\n', '') + ');' +
'\nvar ' + contractName + ' = ' + contractName + 'Contract.new(';
funABI.inputs.forEach(function (inp) {
code += '\n ' + inp.name + ',';
});
code += '\n {' +
'\n from: web3.eth.accounts[0], ' +
"\n data: '" + bytecode + "', " +
'\n gas: 4700000' +
'\n }, function (e, contract){' +
'\n console.log(e, contract);' +
"\n if (typeof contract.address !== 'undefined') {" +
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
'\n }' +
'\n })';
return code;
},
getConstructorInterface: function (abi) {
var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] };
for (var i = 0; i < abi.length; i++) {
if (abi[i].type === 'constructor') {
funABI.inputs = abi[i].inputs || [];
break;
}
}
return funABI;
},
formatGasEstimates: function (data) {
// FIXME: the whole gasEstimates object should be nil instead
if (data.creation === undefined && data.external === undefined && data.internal === undefined) {
return;
}
var gasToText = function (g) { return g === null ? 'unknown' : g; };
var text = '';
var fun;
if ('creation' in data) {
text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n';
}
if ('external' in data) {
text += 'External:\n';
for (fun in data.external) {
text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n';
}
}
if ('internal' in data) {
text += 'Internal:\n';
for (fun in data.internal) {
text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n';
}
}
return text;
},
detailsOpen: {},
getDetails: function (contract, source, contractName) {
var button = $('<button>Toggle Details</button>');
var details = $('<div style="display: none;"/>')
.append(this.tableRow('Solidity Interface', contract.solidity_interface));
if (contract.opcodes !== '') {
details.append(this.tableRow('Opcodes', contract.opcodes));
}
var funHashes = '';
for (var fun in contract.functionHashes) {
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
}
details.append($('<span class="col1">Functions</span>'));
details.append($('<pre/>').text(funHashes));
var gasEstimates = this.formatGasEstimates(contract.gasEstimates);
if (gasEstimates) {
details.append($('<span class="col1">Gas Estimates</span>'));
details.append($('<pre/>').text(gasEstimates));
}
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) {
details.append(this.tableRow('Runtime Bytecode', contract.runtimeBytecode));
}
if (contract.assembly !== null) {
details.append($('<span class="col1">Assembly</span>'));
var assembly = $('<pre/>').text(this.formatAssemblyText(contract.assembly, '', source));
details.append(assembly);
}
button.click(function () { this.detailsOpen[contractName] = !this.detailsOpen[contractName]; details.toggle(); });
if (this.detailsOpen[contractName]) {
details.show();
}
return $('<div class="contractDetails"/>').append(button).append(details);
}
};

@ -0,0 +1,64 @@
'use strict';
function eventManager () {
this.registered = {};
}
/*
* Unregister a listenner.
* Note that if obj is a function. the unregistration will be applied to the dummy obj {}.
*
* @param {String} eventName - the event name
* @param {Object or Func} obj - object that will listen on this event
* @param {Func} func - function of the listenners that will be executed
*/
eventManager.prototype.unregister = function (eventName, obj, func) {
if (obj instanceof Function) {
func = obj;
obj = {};
}
for (var reg in this.registered[eventName]) {
if (this.registered[eventName][reg] &&
this.registered[eventName][reg].obj === obj && (!func || this.registered[eventName][reg].func === func)) {
this.registered[eventName].splice(reg, 1);
return;
}
}
};
/*
* Register a new listenner.
* Note that if obj is a function, the function registration will be associated with the dummy object {}
*
* @param {String} eventName - the event name
* @param {Object or Func} obj - object that will listen on this event
* @param {Func} func - function of the listenners that will be executed
*/
eventManager.prototype.register = function (eventName, obj, func) {
if (!this.registered[eventName]) {
this.registered[eventName] = [];
}
if (obj instanceof Function) {
func = obj;
obj = {};
}
this.registered[eventName].push({
obj: obj,
func: func
});
};
/*
* trigger event.
* Every listenner have their associated function executed
*
* @param {String} eventName - the event name
* @param {Array}j - argument that will be passed to the exectued function.
*/
eventManager.prototype.trigger = function (eventName, args) {
for (var listener in this.registered[eventName]) {
var l = this.registered[eventName][listener];
l.func.apply(l.obj, args);
}
};
module.exports = eventManager;

@ -6,31 +6,46 @@ var EthJSTX = require('ethereumjs-tx');
var ethJSABI = require('ethereumjs-abi'); var ethJSABI = require('ethereumjs-abi');
var EthJSBlock = require('ethereumjs-block'); var EthJSBlock = require('ethereumjs-block');
var BN = ethJSUtil.BN; var BN = ethJSUtil.BN;
var EventManager = require('./lib/eventManager');
function UniversalDApp (contracts, options, transactionDebugger, vm) { /*
trigger debugRequested
*/
function UniversalDApp (executionContext, options, txdebugger) {
this.event = new EventManager();
var self = this; var self = this;
self.options = options || {}; self.options = options || {};
self.$el = $('<div class="udapp" />'); self.$el = $('<div class="udapp" />');
self.contracts = contracts; self.contracts;
self.renderOutputModifier = options.renderOutputModifier || function (name, content) { return content; }; self.getAddress;
self.getValue;
self.web3 = options.web3; self.getGasLimit;
self.transactionDebugger = transactionDebugger; self.txdebugger = txdebugger; // temporary: will not be needed anymore when we'll add memory support to the VM
var defaultRenderOutputModifier = function (name, content) { return content; };
if (options.mode === 'vm') { self.renderOutputModifier = defaultRenderOutputModifier;
// FIXME: use `options.vm` or `self.vm` consistently self.web3 = executionContext.web3();
options.vm = true; self.vm = executionContext.vm();
self.executionContext = executionContext;
self.accounts = {}; self.executionContext.event.register('contextChanged', this, function (context) {
self.vm = vm; self.reset(self.contracts);
self.addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511'); });
self.addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c');
} else if (options.mode !== 'web3') {
throw new Error('Either VM or Web3 mode must be selected');
}
} }
UniversalDApp.prototype.reset = function (contracts, getAddress, getValue, getGasLimit, renderer) {
this.$el.empty();
this.contracts = contracts;
this.getAddress = getAddress;
this.getValue = getValue;
this.getGasLimit = getGasLimit;
this.renderOutputModifier = renderer;
this.accounts = {};
if (this.executionContext.isVM()) {
this.addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511');
this.addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c');
}
};
UniversalDApp.prototype.addAccount = function (privateKey, balance) { UniversalDApp.prototype.addAccount = function (privateKey, balance) {
var self = this; var self = this;
@ -48,7 +63,7 @@ UniversalDApp.prototype.addAccount = function (privateKey, balance) {
UniversalDApp.prototype.getAccounts = function (cb) { UniversalDApp.prototype.getAccounts = function (cb) {
var self = this; var self = this;
if (!self.vm) { if (!self.executionContext.isVM()) {
self.web3.eth.getAccounts(cb); self.web3.eth.getAccounts(cb);
} else { } else {
if (!self.accounts) { if (!self.accounts) {
@ -64,7 +79,7 @@ UniversalDApp.prototype.getBalance = function (address, cb) {
address = ethJSUtil.stripHexPrefix(address); address = ethJSUtil.stripHexPrefix(address);
if (!self.vm) { if (!self.executionContext.isVM()) {
self.web3.eth.getBalance(address, function (err, res) { self.web3.eth.getBalance(address, function (err, res) {
if (err) { if (err) {
cb(err); cb(err);
@ -194,7 +209,7 @@ UniversalDApp.prototype.getInstanceInterface = function (contract, address, $tar
$close.click(function () { $instance.remove(); }); $close.click(function () { $instance.remove(); });
$instance.append($close); $instance.append($close);
} }
var context = self.options.vm ? 'memory' : 'blockchain'; var context = self.executionContext.isVM() ? 'memory' : 'blockchain';
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex'); address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex');
var $title = $('<span class="title"/>').text(contract.name + ' at ' + address + ' (' + context + ')'); var $title = $('<span class="title"/>').text(contract.name + ' at ' + address + ' (' + context + ')');
@ -221,7 +236,7 @@ UniversalDApp.prototype.getInstanceInterface = function (contract, address, $tar
$events.append($event); $events.append($event);
}; };
if (self.options.vm) { if (self.executionContext.isVM()) {
// FIXME: support indexed events // FIXME: support indexed events
var eventABI = {}; var eventABI = {};
@ -354,7 +369,7 @@ UniversalDApp.prototype.getCallButton = function (args) {
var $debugTx = $('<div class="debugTx">'); var $debugTx = $('<div class="debugTx">');
var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>'); var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>');
$button.click(function () { $button.click(function () {
self.transactionDebugger.debug(result); self.event.trigger('debugRequested', [result]);
}); });
$debugTx.append($button); $debugTx.append($button);
return $debugTx; return $debugTx;
@ -364,7 +379,7 @@ UniversalDApp.prototype.getCallButton = function (args) {
var $debugTx = $('<div class="debugCall">'); var $debugTx = $('<div class="debugCall">');
var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>'); var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>');
$button.click(function () { $button.click(function () {
self.transactionDebugger.debug(result); self.event.trigger('debugRequested', [result]);
}); });
$debugTx.append($button); $debugTx.append($button);
return $debugTx; return $debugTx;
@ -504,16 +519,16 @@ UniversalDApp.prototype.getCallButton = function (args) {
if (err) { if (err) {
replaceOutput($result, $('<span/>').text(err).addClass('error')); replaceOutput($result, $('<span/>').text(err).addClass('error'));
// VM only // VM only
} else if (self.options.vm && result.vm.exception === 0 && result.vm.exceptionError) { } else if (self.executionContext.isVM() && result.vm.exception === 0 && result.vm.exceptionError) {
replaceOutput($result, $('<span/>').text('VM Exception: ' + result.vm.exceptionError).addClass('error')); replaceOutput($result, $('<span/>').text('VM Exception: ' + result.vm.exceptionError).addClass('error'));
// VM only // VM only
} else if (self.options.vm && result.vm.return === undefined) { } else if (self.executionContext.isVM() && result.vm.return === undefined) {
replaceOutput($result, $('<span/>').text('Exception during execution.').addClass('error')); replaceOutput($result, $('<span/>').text('Exception during execution.').addClass('error'));
} else if (isConstructor) { } else if (isConstructor) {
replaceOutput($result, getGasUsedOutput(result, result.vm)); replaceOutput($result, getGasUsedOutput(result, result.vm));
$result.append(getDebugTransaction(result)); $result.append(getDebugTransaction(result));
args.appendFunctions(self.options.vm ? result.createdAddress : result.contractAddress); args.appendFunctions(self.executionContext.isVM() ? result.createdAddress : result.contractAddress);
} else if (self.options.vm) { } else if (self.executionContext.isVM()) {
var outputObj = '0x' + result.vm.return.toString('hex'); var outputObj = '0x' + result.vm.return.toString('hex');
clearOutput($result); clearOutput($result);
$result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result, result.vm)); $result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result, result.vm));
@ -611,7 +626,7 @@ UniversalDApp.prototype.deployLibrary = function (contractName, cb) {
if (err) { if (err) {
return cb(err); return cb(err);
} }
var address = self.options.vm ? result.createdAddress : result.contractAddress; var address = self.executionContext.isVM() ? result.createdAddress : result.contractAddress;
self.getContractByName(contractName).address = address; self.getContractByName(contractName).address = address;
cb(err, address); cb(err, address);
}); });
@ -644,27 +659,27 @@ UniversalDApp.prototype.runTx = function (data, args, cb) {
} }
var gasLimit = 3000000; var gasLimit = 3000000;
if (self.options.getGasLimit) { if (self.getGasLimit) {
try { try {
gasLimit = self.options.getGasLimit(); gasLimit = self.getGasLimit();
} catch (e) { } catch (e) {
return cb(e); return cb(e);
} }
} }
var value = 0; var value = 0;
if (self.options.getValue) { if (self.getValue) {
try { try {
value = self.options.getValue(); value = self.getValue();
} catch (e) { } catch (e) {
return cb(e); return cb(e);
} }
} }
var tx; var tx;
if (!self.vm) { if (!self.executionContext.isVM()) {
tx = { tx = {
from: self.options.getAddress ? self.options.getAddress() : self.web3.eth.accounts[0], from: self.getAddress ? self.getAddress() : self.web3.eth.accounts[0],
to: to, to: to,
data: data, data: data,
value: value value: value
@ -695,7 +710,7 @@ UniversalDApp.prototype.runTx = function (data, args, cb) {
} }
} else { } else {
try { try {
var address = self.options.getAddress ? self.options.getAddress() : self.getAccounts()[0]; var address = self.getAddress ? self.getAddress() : Object.keys(self.accounts)[0];
var account = self.accounts[address]; var account = self.accounts[address];
tx = new EthJSTX({ tx = new EthJSTX({
nonce: new BN(account.nonce++), nonce: new BN(account.nonce++),
@ -715,7 +730,7 @@ UniversalDApp.prototype.runTx = function (data, args, cb) {
uncleHeaders: [] uncleHeaders: []
}); });
self.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) { self.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) {
result.transactionHash = self.transactionDebugger.web3().releaseCurrentHash(); // used to keep track of the transaction result.transactionHash = self.txdebugger.web3().releaseCurrentHash(); // used to keep track of the transaction
cb(err, result); cb(err, result);
}); });
} catch (e) { } catch (e) {

@ -1,6 +1,7 @@
var test = require('tape'); var test = require('tape');
var Compiler = require('../src/app/compiler'); var Compiler = require('../src/app/compiler');
var EventManager = require('../src/lib/eventManager');
test('compiler.compile smoke', function (t) { test('compiler.compile smoke', function (t) {
t.plan(1); t.plan(1);
@ -8,9 +9,8 @@ test('compiler.compile smoke', function (t) {
var noop = function () {}; var noop = function () {};
var getCacheFile = function () { return 'fakeCacheFile'; }; var getCacheFile = function () { return 'fakeCacheFile'; };
var fakeEditor = {onChangeSetup: noop, clearAnnotations: noop, getValue: noop, setCacheFileContent: noop, getCacheFile: getCacheFile}; var fakeEditor = {onChangeSetup: noop, clearAnnotations: noop, getValue: noop, setCacheFileContent: noop, getCacheFile: getCacheFile};
var fakeOutputField = {empty: noop};
var fakeQueryParams = {get: function () { return {}; }}; var fakeQueryParams = {get: function () { return {}; }};
var compiler = new Compiler(fakeEditor, null, fakeQueryParams, null, fakeOutputField); var compiler = new Compiler(fakeEditor, fakeQueryParams, null, null, new EventManager());
compiler.setCompileJSON(noop); compiler.setCompileJSON(noop);
compiler.compile(); compiler.compile();
t.ok(compiler); t.ok(compiler);

Loading…
Cancel
Save