From e6adf1da192b94c372b9e86a0a6e43cccbedb27d Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 4 Aug 2016 15:16:00 +0200 Subject: [PATCH 1/4] add event manager --- src/app.js | 80 ++++++-- src/app/compiler.js | 50 ++--- src/app/execution-context.js | 35 ++-- src/app/formalVerification.js | 18 +- src/app/renderer.js | 334 +++++++++------------------------- src/app/ui-helper.js | 161 ++++++++++++++++ src/lib/eventManager.js | 41 +++++ src/lib/util.js | 14 ++ src/universal-dapp.js | 86 +++++---- 9 files changed, 475 insertions(+), 344 deletions(-) create mode 100644 src/app/ui-helper.js create mode 100644 src/lib/eventManager.js create mode 100644 src/lib/util.js diff --git a/src/app.js b/src/app.js index 0909bbdbee..9660f606db 100644 --- a/src/app.js +++ b/src/app.js @@ -13,9 +13,10 @@ var Editor = require('./app/editor'); var Renderer = require('./app/renderer'); var Compiler = require('./app/compiler'); var ExecutionContext = require('./app/execution-context'); +var UniversalDApp = require('./universal-dapp.js'); var Debugger = require('./app/debugger'); var FormalVerification = require('./app/formalVerification'); -var EthJSVM = require('ethereumjs-vm'); +var util = require('./lib/util'); // The event listener needs to be registered as early as possible, because the // parent will send the message upon the "load" event. @@ -26,8 +27,12 @@ window.addEventListener('message', function (ev) { loadFilesCallback(ev.data[1]); } }, false); - +/* + trigger selectTab +*/ var run = function () { + var self = this; + util.makeEventCapable(this); var storage = new Storage(updateFiles); function loadFiles (files) { @@ -108,6 +113,7 @@ var run = function () { el.removeClass('active'); $('#optionViews').removeClass(cls); } + self.event.trigger('tabChanged', [cls]); }; // ------------------ gist publish -------------- @@ -199,7 +205,7 @@ var run = function () { $fileNameInputEl.off('keyup'); 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 rename: ' + originalName + ' to ' + newName + '?')) { storage.rename(utils.fileKey(originalName), utils.fileKey(newName)); @@ -279,7 +285,7 @@ var run = function () { } // function widthOfHidden () { - // return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi()); + // return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi()) // } function widthOfVisible () { @@ -408,8 +414,6 @@ var run = function () { if (!hidingRHP) compiler.compile(); }); - function getHidingRHP () { return hidingRHP; } - // ----------------- editor resize --------------- function onResize () { @@ -433,19 +437,63 @@ var run = function () { 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 }); - vm.stateManager.checkpoint(); - transactionDebugger.addProvider('VM', vm); + var executionContext = new ExecutionContext(); + + transactionDebugger.addProvider('VM', executionContext.vm()); transactionDebugger.switchProvider('VM'); - var executionContext = new ExecutionContext(transactionDebugger); + transactionDebugger.addProvider('INTERNAL', executionContext.web3()); transactionDebugger.addProvider('EXTERNAL', executionContext.web3()); transactionDebugger.onDebugRequested = function () { selectTab($('ul#options li.debugView')); }; - var renderer = new Renderer(editor, executionContext, updateFiles, transactionDebugger, vm); - var formalVerification = new FormalVerification($('#verificationView'), renderer); - var compiler = new Compiler(editor, renderer, queryParams, handleGithubCall, $('#output'), getHidingRHP, formalVerification, updateFiles); - executionContext.setCompiler(compiler); + + var udapp = new UniversalDApp(executionContext, { + removable: false, + removable_instances: true + }, transactionDebugger); + + udapp.event.register('debugRequested', this, function (data) { + transactionDebugger.debug(data); + }); + + var renderer = new Renderer(editor, executionContext.web3(), updateFiles, udapp, executionContext); + var formalVerification = new FormalVerification($('#verificationView')); + + formalVerification.event.register('compilationError', this, function (message, container, noAnnotations) { + renderer.error(message, container, noAnnotations); + }); + + var compiler = new Compiler(editor, queryParams, handleGithubCall, updateFiles); + + executionContext.event.register('contextChanged', this, function (context) { + $('#output').empty(); + context = context === 'vm' ? 'VM' : context; + context = context === 'injected' ? 'EXTERNAL' : context; + context = context === 'web3' ? 'INTERNAL' : context; + transactionDebugger.switchProvider(context); + compiler.compile(); + }); + executionContext.event.register('web3EndpointChanged', this, function (context) { + $('#output').empty(); + compiler.compile(); + }); + compiler.event.register('compilerLoaded', this, function (version) { + setVersionText(version); + compiler.compile(); + }); + compiler.event.register('compilationError', this, function (data) { + renderer.error(data); + }); + compiler.event.register('compilationSucceed', this, function (data, source) { + if (!hidingRHP) { + renderer.contracts(data, source); + formalVerification.compilationFinished(data); + } + }); + compiler.event.register('isCompiling', this, function () { + $('#output').empty(); + formalVerification.compiling(); + }); function setVersionText (text) { $('#version').text(text); @@ -459,9 +507,9 @@ var run = function () { // Workers cannot load js on "file:"-URLs and we get a // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium, // resort to non-worker version in that case. - compiler.loadVersion(true, version, setVersionText); + compiler.loadVersion(true, version); } else { - compiler.loadVersion(false, version, setVersionText); + compiler.loadVersion(false, version); } }; diff --git a/src/app/compiler.js b/src/app/compiler.js index cd6cf7d3ee..02a432c02f 100644 --- a/src/app/compiler.js +++ b/src/app/compiler.js @@ -5,7 +5,15 @@ var utils = require('./utils'); var Base64 = require('js-base64').Base64; -function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, hidingRHP, formalVerification, updateFiles) { +var util = require('../lib/util'); + +/* + trigger compilationError, compilationSucceed, compilerLoaded, isCompiling +*/ +function Compiler (editor, queryParams, handleGithubCall, updateFiles) { + var self = this; + util.makeEventCapable(this); + var compileJSON; var compilerAcceptsMultipleFiles; @@ -36,19 +44,15 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, var compile = function (missingInputs) { editor.clearAnnotations(); - outputField.empty(); - if (formalVerification) { - formalVerification.compiling(); - } + self.event.trigger('isCompiling', []); var input = editor.getValue(); editor.setCacheFileContent(input); var files = {}; files[utils.fileNameFromKey(editor.getCacheFile())] = input; gatherImports(files, missingInputs, function (input, error) { - outputField.empty(); if (input === null) { - renderer.error(error); + this.event.trigger('compilationError', [error]); } else { var optimize = queryParams.get().optimize; compileJSON(input, optimize ? 1 : 0); @@ -62,13 +66,12 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, } this.setCompileJSON = setCompileJSON; // this is exposed for testing - function onCompilerLoaded (setVersionText, version) { - setVersionText(version); + function onCompilerLoaded (version) { previousInput = ''; - onChange(); + self.event.trigger('compilerLoaded', [version]); } - function onInternalCompilerLoaded (setVersionText) { + function onInternalCompilerLoaded () { if (worker === null) { var compiler = solc(window.Module); @@ -91,7 +94,7 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, 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 if (data['error'] !== undefined) { - renderer.error(data['error']); + self.event.trigger('compilationError', [data['error']]); if (utils.errortype(data['error']) !== 'warning') { noFatalErrors = false; } } if (data['errors'] !== undefined) { data['errors'].forEach(function (err) { - renderer.error(err); + self.event.trigger('compilationError', [err]); if (utils.errortype(err) !== 'warning') { noFatalErrors = false; } @@ -115,13 +118,12 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, if (missingInputs !== undefined && missingInputs.length > 0) { compile(missingInputs); - } else if (noFatalErrors && !hidingRHP()) { - renderer.contracts(data, editor.getValue()); - formalVerification.compilationFinished(data); + } else if (noFatalErrors) { + self.event.trigger('compilationSucceed', [data, editor.getValue()]); } } - this.loadVersion = function (usingWorker, version, setVersionText) { + this.loadVersion = function (usingWorker, version) { var url; if (version !== 'soljson.js') { 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')); if (usingWorker) { - loadWorker(url, setVersionText); + loadWorker(url); } else { - loadInternal(url, setVersionText); + loadInternal(url); } }; - function loadInternal (url, setVersionText) { + function loadInternal (url) { delete window.Module; // Set a safe fallback until the new one is loaded setCompileJSON(function (source, optimize) { compilationFinished('{}'); }); @@ -151,11 +153,11 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, return; } window.clearInterval(check); - onInternalCompilerLoaded(setVersionText); + onInternalCompilerLoaded(); }, 200); } - function loadWorker (url, setVersionText) { + function loadWorker (url) { if (worker !== null) { worker.terminate(); } @@ -165,7 +167,7 @@ function Compiler (editor, renderer, queryParams, handleGithubCall, outputField, switch (data.cmd) { case 'versionLoaded': compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles; - onCompilerLoaded(setVersionText, data.data); + onCompilerLoaded(data.data); break; case 'compiled': var result; diff --git a/src/app/execution-context.js b/src/app/execution-context.js index 70591307df..f0b4ac5e60 100644 --- a/src/app/execution-context.js +++ b/src/app/execution-context.js @@ -2,6 +2,8 @@ var $ = require('jquery'); var Web3 = require('web3'); +var util = require('../lib/util'); +var EthJSVM = require('ethereumjs-vm'); var injectedProvider; @@ -13,14 +15,17 @@ if (typeof window.web3 !== 'undefined') { web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); } -function ExecutionContext (_txDebugger) { - var txDebugger = _txDebugger; - var compiler; - var executionContext = injectedProvider ? 'injected' : 'vm'; +var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true }); +vm.stateManager.checkpoint(); - this.setCompiler = function (_compiler) { - compiler = _compiler; - }; +/* + trigger contextChanged +*/ + +function ExecutionContext () { + var self = this; + util.makeEventCapable(this); + var executionContext = injectedProvider ? 'injected' : 'vm'; this.isVM = function () { return executionContext === 'vm'; @@ -30,6 +35,10 @@ function ExecutionContext (_txDebugger) { return web3; }; + this.vm = function () { + return vm; + }; + var $injectedToggle = $('#injected-mode'); var $vmToggle = $('#vm-mode'); var $web3Toggle = $('#web3-mode'); @@ -47,7 +56,7 @@ function ExecutionContext (_txDebugger) { $web3endpoint.on('change', function () { setProviderFromEndpoint(); if (executionContext === 'web3') { - compiler.compile(); + self.event.trigger('web3EndpointChanged'); } }); @@ -60,15 +69,17 @@ function ExecutionContext (_txDebugger) { executionContext = ev.target.value; if (executionContext === 'web3') { setProviderFromEndpoint(); - txDebugger.switchProvider('EXTERNAL'); + self.event.trigger('contextChanged', ['web3']); } else if (executionContext === 'injected') { web3.setProvider(injectedProvider); - txDebugger.switchProvider('EXTERNAL'); + self.event.trigger('contextChanged', ['injected']); } else if (executionContext === 'vm') { - txDebugger.switchProvider('VM'); + vm.stateManager.revert(function () { + vm.stateManager.checkpoint(); + }); + self.event.trigger('contextChanged', ['vm']); } } - compiler.compile(); } function setProviderFromEndpoint () { diff --git a/src/app/formalVerification.js b/src/app/formalVerification.js index c69ab863c1..20e1193ca7 100644 --- a/src/app/formalVerification.js +++ b/src/app/formalVerification.js @@ -1,8 +1,12 @@ var $ = require('jquery'); +var util = require('../lib/util'); -function FormalVerification (outputElement, renderer) { +/* + trigger compilationError +*/ +function FormalVerification (outputElement) { + util.makeEventCapable(this); this.outputElement = outputElement; - this.renderer = renderer; } FormalVerification.prototype.compiling = function () { @@ -14,23 +18,19 @@ FormalVerification.prototype.compiling = function () { FormalVerification.prototype.compilationFinished = function (compilationResult) { if (compilationResult.formal === undefined) { - this.renderer.error( - 'Formal verification not supported by this compiler version.', - $('#formalVerificationErrors'), - true - ); + this.event.trigger('compilationError', ['Formal verification not supported by this compiler version.', $('#formalVerificationErrors'), true]); } else { if (compilationResult.formal['why3'] !== undefined) { $('#formalVerificationInput', this.outputElement).val( '(* copy this to http://why3.lri.fr/try/ *)' + compilationResult.formal['why3'] ) - .show(); + .show(); } if (compilationResult.formal.errors !== undefined) { var errors = compilationResult.formal.errors; for (var i = 0; i < errors.length; i++) { - this.renderer.error(errors[i], $('#formalVerificationErrors'), true); + this.event.trigger('compilationError', [errors[i], $('#formalVerificationErrors'), true]); } } } diff --git a/src/app/renderer.js b/src/app/renderer.js index ab9872cc75..3058be48db 100644 --- a/src/app/renderer.js +++ b/src/app/renderer.js @@ -1,276 +1,118 @@ var $ = require('jquery'); -var UniversalDApp = require('../universal-dapp.js'); - var utils = require('./utils'); +var uiHelper = require('./ui-helper'); + +function Renderer (editor, web3, updateFiles, udapp, executionContext) { + this.editor = editor; + this.web3 = web3; + this.updateFiles = updateFiles; + this.udapp = udapp; + this.executionContext = executionContext; +} -function Renderer (editor, executionContext, updateFiles, transactionDebugger, vm) { - var detailsOpen = {}; - - function renderError (message, container, noAnnotations) { - var type = utils.errortype(message); - var $pre = $('
').text(message);
-    var $error = $('
').prepend($pre); - if (container === undefined) { - container = $('#output'); - } - 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(editor.getCacheFile()))) { - editor.addAnnotation({ - row: errLine, - column: errCol, - 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; +Renderer.prototype.error = function (message, container, noAnnotations) { + var type = utils.errortype(message); + var $pre = $('
').text(message);
+  var $error = $('
').prepend($pre); + if (container === undefined) { + container = $('#output'); + } + 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) { + if (errFile !== '' && errFile !== utils.fileNameFromKey(this.editor.getCacheFile()) && this.editor.hasFile(errFile)) { + // Switch to file + this.editor.setCacheFile(utils.fileKey(errFile)); + this.updateFiles(); + // @TODO could show some error icon in files with errors + } + this.editor.handleErrorClick(errLine, errCol); + }); + $error.find('.close').click(function (ev) { + ev.preventDefault(); + $error.remove(); + return false; + }); + } +}; + +Renderer.prototype.contracts = function (data, source) { + var udappContracts = []; + for (var contractName in data.contracts) { + var contract = data.contracts[contractName]; + udappContracts.push({ + name: contractName, + interface: contract['interface'], + bytecode: contract.bytecode + }); } - this.error = renderError; + // rendering function used by udapp. they need data and source var combined = function (contractName, jsonInterface, bytecode) { return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }]); }; - function renderContracts (data, source) { - var udappContracts = []; - for (var contractName in data.contracts) { - var contract = data.contracts[contractName]; - udappContracts.push({ - name: contractName, - interface: contract['interface'], - bytecode: contract.bytecode - }); + var renderOutputModifier = function (contractName, $contractOutput) { + var contract = data.contracts[contractName]; + if (contract.bytecode) { + $contractOutput.append(uiHelper.textRow('Bytecode', contract.bytecode)); } - vm.stateManager.revert(function () { - vm.stateManager.checkpoint(); - }); - var dapp = new UniversalDApp(udappContracts, { - mode: executionContext.isVM() ? 'vm' : 'web3', - 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); - - var $contractOutput = dapp.render(); - - var $txOrigin = $('#txorigin'); + $contractOutput.append(uiHelper.textRow('Interface', contract['interface'])); - function renderAccounts (err, accounts) { - if (err) { - renderError(err.message); - } - if (accounts && accounts[0]) { - $txOrigin.empty(); - for (var a in accounts) { $txOrigin.append($('