diff --git a/.circleci/config.yml b/.circleci/config.yml index 58c75f61521..b6eca0c9171 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,10 +26,10 @@ jobs: - checkout - restore_cache: keys: - - dep-bundle-24-{{ checksum "package.json" }} + - dep-bundle-27-{{ checksum "package.json" }} - run: npm install - save_cache: - key: dep-bundle-24-{{ checksum "package.json" }} + key: dep-bundle-27-{{ checksum "package.json" }} paths: - ~/repo/node_modules - run: npm run lint && npm run test && npm run make-mock-compiler && npm run build diff --git a/ci/makeMockCompiler.js b/ci/makeMockCompiler.js index b8e3a6b4dba..987f408d49c 100644 --- a/ci/makeMockCompiler.js +++ b/ci/makeMockCompiler.js @@ -2,14 +2,18 @@ var fs = require('fs') var compiler = require('solc') - var compilerInput = require('remix-solidity').CompilerInput -var compilationResult = {} -gatherCompilationResults('./test-browser/tests/', compilationResult) -gatherCompilationResults('./test-browser/tests/units/', compilationResult) -replaceSolCompiler(compilationResult) +var defaultVersion = 'v0.5.1+commit.c8a2cb62' + +compiler.loadRemoteVersion(defaultVersion, (error, solcSnapshot) => { + if (error) console.log(error) + var compilationResult = {} + gatherCompilationResults('./test-browser/tests/', compilationResult, solcSnapshot) + gatherCompilationResults('./test-browser/tests/units/', compilationResult, solcSnapshot) + replaceSolCompiler(compilationResult, solcSnapshot) +}) -function gatherCompilationResults (dir, compilationResult, callback) { +function gatherCompilationResults (dir, compilationResult, solcSnapshot) { var filenames = fs.readdirSync(dir, 'utf8') filenames.map(function (item, i) { if (item.endsWith('.js')) { @@ -17,10 +21,10 @@ function gatherCompilationResults (dir, compilationResult, callback) { if ('@sources' in testDef) { var sources = testDef['@sources']() for (var files in sources) { - compile(sources[files], true, function (result) { + compile(solcSnapshot, sources[files], true, function (result) { compilationResult[result.key] = result }) - compile(sources[files], false, function (result) { + compile(solcSnapshot, sources[files], false, function (result) { compilationResult[result.key] = result }) } @@ -30,11 +34,11 @@ function gatherCompilationResults (dir, compilationResult, callback) { return compilationResult } -function compile (source, optimization, addCompilationResult) { +function compile (solcSnapshot, source, optimization, addCompilationResult) { var missingInputs = [] try { var input = compilerInput(source, {optimize: optimization}) - var result = compiler.compileStandardWrapper(input, function (path) { + var result = solcSnapshot.compileStandardWrapper(input, function (path) { missingInputs.push(path) }) input = input.replace(/(\t)|(\n)|(\\n)|( )/g, '') @@ -51,15 +55,15 @@ function compile (source, optimization, addCompilationResult) { addCompilationResult(ret) } -function replaceSolCompiler (results) { +function replaceSolCompiler (results, solcSnapshot) { fs.readFile('./test-browser/mockcompiler/compiler.js', 'utf8', function (error, data) { if (error) { console.log(error) process.exit(1) return } - console.log(compiler.version()) - data = data + '\n\nvar mockCompilerVersion = \'' + compiler.version() + '\'' + console.log(solcSnapshot.version()) + data = data + '\n\nvar mockCompilerVersion = \'' + solcSnapshot.version() + '\'' data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n' fs.writeFile('./soljson.js', data, 'utf8', function (error) { if (error) { diff --git a/package.json b/package.json index ee04e5d03f4..31624ddaa82 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "remix-lib": "0.4.1", "remix-solidity": "0.3.1", "remix-tests": "0.1.1", - "remixd": "git+https://github.com/ethereum/remixd.git", + "remixd": "0.1.8-alpha.6", "request": "^2.83.0", "rimraf": "^2.6.1", "selenium-standalone": "^6.0.1", @@ -61,7 +61,7 @@ }, "dependencies": { "http-server": "0.9.0", - "remixd": "git+https://github.com/ethereum/remixd.git" + "remixd": "0.1.8-alpha.6" }, "repository": { "type": "git", @@ -156,7 +156,7 @@ "build_debugger": "browserify src/app/debugger/remix-debugger/index.js -o src/app/debugger/remix-debugger/build/app.js", "browsertest": "sleep 5 && npm run nightwatch_local", "csslint": "csslint --ignore=order-alphabetical --errors='errors,duplicate-properties,empty-rules' --exclude-list='assets/css/font-awesome.min.css' assets/css/", - "downloadsolc_root": "wget --no-check-certificate https://solc-bin.ethereum.org/soljson.js", + "downloadsolc_root": "wget --no-check-certificate https://solc-bin.ethereum.org/bin/soljson-v0.5.1+commit.c8a2cb62.js -O soljson.js", "lint": "standard | notify-error", "make-mock-compiler": "node ci/makeMockCompiler.js", "minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false", diff --git a/src/app.js b/src/app.js index 773352f0892..a0bcec313ee 100644 --- a/src/app.js +++ b/src/app.js @@ -1,12 +1,10 @@ 'use strict' -var $ = require('jquery') var csjs = require('csjs-inject') var yo = require('yo-yo') var async = require('async') var request = require('request') var remixLib = require('remix-lib') -var remixTests = require('remix-tests') var EventManager = require('./lib/events') var registry = require('./global/registry') @@ -14,18 +12,15 @@ var UniversalDApp = require('./universal-dapp.js') var UniversalDAppUI = require('./universal-dapp-ui.js') var Remixd = require('./lib/remixd') var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') - var QueryParams = require('./lib/query-params') var GistHandler = require('./lib/gist-handler') var helper = require('./lib/helper') var Storage = remixLib.Storage var Browserfiles = require('./app/files/browser-files') var BrowserfilesTree = require('./app/files/browser-files-tree') -var chromeCloudStorageSync = require('./app/files/chromeCloudStorageSync') var SharedFolder = require('./app/files/shared-folder') var Config = require('./config') var Renderer = require('./app/ui/renderer') -var Compiler = require('remix-solidity').Compiler var executionContext = require('./execution-context') var FilePanel = require('./app/panels/file-panel') var EditorPanel = require('./app/panels/editor-panel') @@ -35,13 +30,22 @@ var modalDialogCustom = require('./app/ui/modal-dialog-custom') var TxLogger = require('./app/execution/txLogger') var Txlistener = remixLib.execution.txListener var EventsDecoder = remixLib.execution.EventsDecoder -var CompilerImport = require('./app/compiler/compiler-imports') var FileManager = require('./app/files/fileManager') var BasicReadOnlyExplorer = require('./app/files/basicReadOnlyExplorer') var NotPersistedExplorer = require('./app/files/NotPersistedExplorer') var toolTip = require('./app/ui/tooltip') var TransactionReceiptResolver = require('./transactionReceiptResolver') +const CompilerAbstract = require('./app/compiler/compiler-abstract') +const PluginManager = require('./app/plugin/pluginManager') +const CompileTab = require('./app/tabs/compile-tab') +const SettingsTab = require('./app/tabs/settings-tab') +const AnalysisTab = require('./app/tabs/analysis-tab') +const DebuggerTab = require('./app/tabs/debugger-tab') +const SupportTab = require('./app/tabs/support-tab') +const TestTab = require('./app/tabs/test-tab') +const RunTab = require('./app/tabs/run-tab') + var styleGuide = require('./app/ui/styles-guide/theme-chooser') var styles = styleGuide.chooser() @@ -126,8 +130,6 @@ class App { executionContext.init(self._components.config) executionContext.listenOnLastBlock() - self._components.compilerImport = new CompilerImport() - registry.put({api: self._components.compilerImport, name: 'compilerimport'}) self._components.gistHandler = new GistHandler() self._components.filesProviders = {} @@ -228,34 +230,6 @@ class App { self._adjustLayout('right', self.data._layout.right.offset) return self._view.el } - runCompiler () { - const self = this - if (self._components.righthandpanel.debugger().isDebuggerActive()) return - - self._components.fileManager.saveCurrentFile() - self._components.editorpanel.getEditor().clearAnnotations() - var currentFile = self._components.config.get('currentFile') - if (currentFile) { - if (/.(.sol)$/.exec(currentFile)) { - // only compile *.sol file. - var target = currentFile - var sources = {} - var provider = self._components.fileManager.fileProviderOf(currentFile) - if (provider) { - provider.get(target, (error, content) => { - if (error) { - console.log(error) - } else { - sources[target] = { content } - self._components.compiler.compile(sources, target) - } - }) - } else { - console.log('cannot compile ' + currentFile + '. Does not belong to any explorer') - } - } - } - } startdebugging (txHash) { const self = this self.event.trigger('debuggingRequested', []) @@ -299,55 +273,6 @@ class App { if (callback) callback(error) }) } - importExternal (url, cb) { - const self = this - self._components.compilerImport.import(url, - (loadingMsg) => { - toolTip(loadingMsg) - }, - (error, content, cleanUrl, type, url) => { - if (!error) { - if (self._components.filesProviders[type]) { - self._components.filesProviders[type].addReadOnly(cleanUrl, content, url) - } - cb(null, content) - } else { - cb(error) - } - }) - } - importFileCb (url, filecb) { - const self = this - if (url.indexOf('/remix_tests.sol') !== -1) { - return filecb(null, remixTests.assertLibCode) - } - var provider = self._components.fileManager.fileProviderOf(url) - if (provider) { - if (provider.type === 'localhost' && !provider.isConnected()) { - return filecb(`file provider ${provider.type} not available while trying to resolve ${url}`) - } - provider.exists(url, (error, exist) => { - if (error) return filecb(error) - if (exist) { - return provider.get(url, filecb) - } else { - self.importExternal(url, filecb) - } - }) - } else if (self._components.compilerImport.isRelativeImport(url)) { - // try to resolve localhost modules (aka truffle imports) - var splitted = /([^/]+)\/(.*)$/g.exec(url) - async.tryEach([ - (cb) => { self.importFileCb('localhost/installed_contracts/' + url, cb) }, - (cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { self.importFileCb('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], cb) } }, - (cb) => { self.importFileCb('localhost/node_modules/' + url, cb) }, - (cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { self.importFileCb('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], cb) } }], - (error, result) => { filecb(error, result) } - ) - } else { - self.importExternal(url, filecb) - } - } } module.exports = App @@ -377,24 +302,13 @@ Please make a backup of your contracts and start using http://remix.ethereum.org return 'Are you sure you want to leave?' } - // Run the compiler instead of trying to save the website - $(window).keydown(function (e) { - // ctrl+s or command+s - if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) { - e.preventDefault() - self.runCompiler() - } - }) - registry.put({api: msg => self._components.editorpanel.logHtmlMessage(msg), name: 'logCallback'}) - // ----------------- Compiler ----------------- - self._components.compiler = new Compiler((url, cb) => self.importFileCb(url, cb)) - registry.put({api: self._components.compiler, name: 'compiler'}) - - var offsetToLineColumnConverter = new OffsetToLineColumnConverter(self._components.compiler.event) + // helper for converting offset to line/column + var offsetToLineColumnConverter = new OffsetToLineColumnConverter() registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'}) + // json structure for hosting the last compilattion result self._components.compilersArtefacts = {} // store all the possible compilation data (key represent a compiler name) registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) @@ -448,6 +362,23 @@ Please make a backup of your contracts and start using http://remix.ethereum.org var fileManager = self._components.fileManager registry.put({api: fileManager, name: 'filemanager'}) + // ---------------- Plugin Manager ------------------------------- + + let pluginManager = new PluginManager( + self, + self._components.compilersArtefacts, + txlistener, + self._components.fileProviders, + self._components.fileManager, + udapp) + registry.put({api: pluginManager, name: 'pluginmanager'}) + + pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { + // TODO check whether the tab is configured + let compiler = new CompilerAbstract(languageVersion, data, source) + self._components.compilersArtefacts['__last'] = compiler + }) + self._components.editorpanel.init() self._components.fileManager.init() @@ -485,65 +416,28 @@ Please make a backup of your contracts and start using http://remix.ethereum.org var renderer = new Renderer() registry.put({api: renderer, name: 'renderer'}) + // ---------------- Tabs ------------------------------- + let compileTab = new CompileTab(self._components.registry) + let tabs = { + compile: compileTab, + run: new RunTab(self._components.registry), + settings: new SettingsTab(self._components.registry), + analysis: new AnalysisTab(self._components.registry), + debug: new DebuggerTab(self._components.registry), + support: new SupportTab(self._components.registry), + test: new TestTab(self._components.registry, compileTab) + } + // ---------------- Righthand-panel -------------------- - self._components.righthandpanel = new RighthandPanel() + self._components.righthandpanel = new RighthandPanel({ tabs, pluginManager }) self._view.rightpanel.appendChild(self._components.righthandpanel.render()) self._components.righthandpanel.init() self._components.righthandpanel.event.register('resize', delta => self._adjustLayout('right', delta)) var txLogger = new TxLogger() // eslint-disable-line - executionContext.event.register('contextChanged', this, function (context) { - self.runCompiler() - }) - - // rerun the compiler when the environement changed - executionContext.event.register('web3EndpointChanged', this, function (context) { - self.runCompiler() - }) - var queryParams = new QueryParams() - // check init query parameters from the URL once the compiler is loaded - self._components.compiler.event.register('compilerLoaded', this, function (version) { - self.runCompiler() - - if (queryParams.get().context) { - let context = queryParams.get().context - let endPointUrl = queryParams.get().endPointUrl - executionContext.setContext(context, endPointUrl, - () => { - modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { - if (!endPointUrl) { - endPointUrl = 'http://localhost:8545' - } - modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', endPointUrl, (target) => { - executionContext.setProviderFromEndpoint(target, context) - }, () => {}) - }, () => {}) - }, - (alertMsg) => { - modalDialogCustom.alert(alertMsg) - }) - } - - if (queryParams.get().debugtx) { - self.startdebugging(queryParams.get().debugtx) - } - - if (queryParams.get().pluginurl) { - var title = queryParams.get().plugintitle - var url = queryParams.get().pluginurl - modalDialogCustom.confirm(null, `Remix is going to load the extension "${title}" located at ${queryParams.get().pluginurl}. Are you sure to load this external extension?`, () => { - self._components.righthandpanel.loadPlugin({title, url}) - }) - } - }) - - // chrome app - window.syncStorage = chromeCloudStorageSync - chromeCloudStorageSync() - var loadingFromGist = self.loadFromGist(queryParams.get()) if (!loadingFromGist) { // insert ballot contract if there are no files to show diff --git a/src/app/compiler/compiler-abstract.js b/src/app/compiler/compiler-abstract.js index 9cd28c4a597..aa3b1cfaa75 100644 --- a/src/app/compiler/compiler-abstract.js +++ b/src/app/compiler/compiler-abstract.js @@ -3,9 +3,10 @@ var remixLib = require('remix-lib') var txHelper = remixLib.execution.txHelper module.exports = class CompilerAbstract { - constructor (languageversion, data) { + constructor (languageversion, data, source) { this.languageversion = languageversion this.data = data + this.source = source // source code } getContracts () { @@ -23,4 +24,19 @@ module.exports = class CompilerAbstract { getData () { return this.data } + + getAsts () { + return this.data.sources // ast + } + + getSourceName (fileIndex) { + if (this.data && this.data.sources) { + return Object.keys(this.data.sources)[fileIndex] + } + return null + } + + getSourceCode () { + return this.source + } } diff --git a/src/app/debugger/debuggerUI.js b/src/app/debugger/debuggerUI.js index 3aeeb47509b..1055169a18c 100644 --- a/src/app/debugger/debuggerUI.js +++ b/src/app/debugger/debuggerUI.js @@ -90,16 +90,10 @@ class DebuggerUI { this.contextManager = new ContextManager() - this.debugger = new Debugger({ - web3: this.contextManager.getWeb3(), - offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api, - compiler: this.registry.get('compiler').api - }) - this.contextManager.initProviders() this.contextManager.event.register('providerChanged', () => { - this.debugger.updateWeb3(this.contextManager.getWeb3()) + if (this.debugger) this.debugger.updateWeb3(this.contextManager.getWeb3()) }) this.isActive = false @@ -116,7 +110,6 @@ class DebuggerUI { container.appendChild(this.render()) this.setEditor() - this.listenToEvents() } setEditor () { @@ -124,20 +117,22 @@ class DebuggerUI { this.editor = this.registry.get('editor').api self.editor.event.register('breakpointCleared', (fileName, row) => { - self.debugger.breakPointManager.remove({fileName: fileName, row: row}) + if (self.debugger) self.debugger.breakPointManager.remove({fileName: fileName, row: row}) }) self.editor.event.register('breakpointAdded', (fileName, row) => { - self.debugger.breakPointManager.add({fileName: fileName, row: row}) + if (self.debugger) self.debugger.breakPointManager.add({fileName: fileName, row: row}) }) self.editor.event.register('contentChanged', function () { - self.debugger.unload() + if (self.debugger) self.debugger.unload() }) } listenToEvents () { const self = this + if (!self.debugger) return + this.debugger.event.register('debuggerStatus', function (isActive) { self.sourceHighlighter.currentSourceLocation(null) self.isActive = isActive @@ -156,12 +151,12 @@ class DebuggerUI { this.txBrowser = txBrowser txBrowser.event.register('requestDebug', function (blockNumber, txNumber, tx) { - self.debugger.unload() + if (self.debugger) self.debugger.unload() self.startDebugging(blockNumber, txNumber, tx) }) txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) { - self.debugger.unload() + if (self.debugger) self.debugger.unload() }) } @@ -171,6 +166,20 @@ class DebuggerUI { startDebugging (blockNumber, txNumber, tx) { const self = this + if (this.debugger) delete this.debugger + + let compilers = this.registry.get('compilersartefacts').api + let lastCompilationResult + if (compilers['__last']) lastCompilationResult = compilers['__last'] + + // TODO debugging with source highlight is disabled. see line 98 + this.debugger = new Debugger({ + web3: this.contextManager.getWeb3(), + offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api, + compiler: { lastCompilationResult } + }) + + this.listenToEvents() this.debugger.debugger.updateWeb3(this.executionContext.web3()) this.debugger.debug(blockNumber, txNumber, tx, () => { diff --git a/src/app/editor/contextView.js b/src/app/editor/contextView.js index 68f861681bf..671538a0438 100644 --- a/src/app/editor/contextView.js +++ b/src/app/editor/contextView.js @@ -21,7 +21,7 @@ class ContextView { self.contextualListener = opts.contextualListener self.editor = opts.editor self._deps = { - compiler: self._components.registry.get('compiler').api, + compilersArtefacts: self._components.registry.get('compilersartefacts').api, offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api, config: self._components.registry.get('config').api, fileManager: self._components.registry.get('filemanager').api @@ -97,9 +97,14 @@ class ContextView { self.editor.gotoLine(lineColumn.start.line, lineColumn.end.column + 1) } } - if (self._deps.compiler.lastCompilationResult && self._deps.compiler.lastCompilationResult.data) { - var lineColumn = self._deps.offsetToLineColumnConverter.offsetToLineColumn(position, position.file, self._deps.compiler.lastCompilationResult.source.sources, self._deps.compiler.lastCompilationResult.data.sources) - var filename = self._deps.compiler.getSourceName(position.file) + let lastCompilationResult = self._deps.compilersArtefacts['__last'] + if (lastCompilationResult && lastCompilationResult.data) { + var lineColumn = self._deps.offsetToLineColumnConverter.offsetToLineColumn( + position, + position.file, + lastCompilationResult.getSourceCode().sources, + lastCompilationResult.getAsts()) + var filename = lastCompilationResult.getSourceName(position.file) // TODO: refactor with rendererAPI.errorClick if (filename !== self._deps.config.get('currentFile')) { var provider = self._deps.fileManager.fileProviderOf(filename) diff --git a/src/app/editor/contextualListener.js b/src/app/editor/contextualListener.js index bd7e6e2b46b..ed33c4b15da 100644 --- a/src/app/editor/contextualListener.js +++ b/src/app/editor/contextualListener.js @@ -15,8 +15,9 @@ class ContextualListener { self._components = {} self._components.registry = localRegistry || globalRegistry self.editor = opts.editor + self.pluginManager = opts.pluginManager self._deps = { - compiler: self._components.registry.get('compiler').api, + compilersArtefacts: self._components.registry.get('compilersartefacts').api, config: self._components.registry.get('config').api, offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api } @@ -26,15 +27,13 @@ class ContextualListener { } this._activeHighlights = [] - self._deps.compiler.event.register('compilationFinished', (success, data, source) => { + self.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { this._stopHighlighting() this._index = { Declarations: {}, FlatReferences: {} } - if (success) { - this._buildIndex(data, source) - } + this._buildIndex(data, source) }) self.editor.event.register('contentChanged', () => { this._stopHighlighting() }) @@ -42,7 +41,9 @@ class ContextualListener { this.sourceMappingDecoder = new SourceMappingDecoder() this.astWalker = new AstWalker() setInterval(() => { - this._highlightItems(self.editor.getCursorPosition(), self._deps.compiler.lastCompilationResult, self._deps.config.get('currentFile')) + if (self._deps.compilersArtefacts['__last']) { + this._highlightItems(self.editor.getCursorPosition(), self._deps.compilersArtefacts['__last'], self._deps.config.get('currentFile')) + } }, 1000) } @@ -106,15 +107,17 @@ class ContextualListener { var self = this var position = this.sourceMappingDecoder.decode(node.src) var eventId = this._highlightInternal(position, node) - if (eventId) { - this._activeHighlights.push({ eventId, position, fileTarget: self._deps.compiler.getSourceName(position.file), nodeId: node.id }) + let lastCompilationResult = self._deps.compilersArtefacts['__last'] + if (eventId && lastCompilationResult) { + this._activeHighlights.push({ eventId, position, fileTarget: lastCompilationResult.getSourceName(position.file), nodeId: node.id }) } } _highlightInternal (position, node) { var self = this - if (self._deps.compiler.lastCompilationResult && self._deps.compiler.lastCompilationResult.data) { - var lineColumn = self._deps.offsetToLineColumnConverter.offsetToLineColumn(position, position.file, self._deps.compiler.lastCompilationResult.source.sources, self._deps.compiler.lastCompilationResult.data.sources) + let lastCompilationResult = self._deps.compilersArtefacts['__last'] + if (lastCompilationResult) { + var lineColumn = self._deps.offsetToLineColumnConverter.offsetToLineColumn(position, position.file, lastCompilationResult.getSourceCode().sources, lastCompilationResult.getAsts()) var css = 'highlightreference' if (node.children && node.children.length) { // If node has children, highlight the entire line. if not, just highlight the current source position of the node. @@ -130,7 +133,7 @@ class ContextualListener { } } } - var fileName = self._deps.compiler.getSourceName(position.file) + var fileName = lastCompilationResult.getSourceName(position.file) if (fileName) { return self.editor.addMarker(lineColumn, fileName, css) } diff --git a/src/app/editor/sourceHighlighter.js b/src/app/editor/sourceHighlighter.js index 8a64d52580e..d99f469f3e4 100644 --- a/src/app/editor/sourceHighlighter.js +++ b/src/app/editor/sourceHighlighter.js @@ -14,7 +14,7 @@ class SourceHighlighter { editor: self._components.registry.get('editor').api, config: self._components.registry.get('config').api, fileManager: self._components.registry.get('filemanager').api, - compiler: self._components.registry.get('compiler').api + compilerArtefacts: self._components.registry.get('compilersartefacts').api } this.statementMarker = null this.fullLineMarker = null @@ -24,8 +24,9 @@ class SourceHighlighter { currentSourceLocation (lineColumnPos, location) { if (this.statementMarker) this._deps.editor.removeMarker(this.statementMarker, this.source) if (this.fullLineMarker) this._deps.editor.removeMarker(this.fullLineMarker, this.source) - if (location && location.file !== undefined) { - var path = this._deps.compiler.getSourceName(location.file) + let lastCompilationResult = this._deps.compilerArtefacts['__last'] + if (location && location.file !== undefined && lastCompilationResult) { + var path = lastCompilationResult.getSourceName(location.file) if (path) { this.currentSourceLocationFromfileName(lineColumnPos, path) } diff --git a/src/app/execution/txLogger.js b/src/app/execution/txLogger.js index bae928b8d87..a0774b8ab8a 100644 --- a/src/app/execution/txLogger.js +++ b/src/app/execution/txLogger.js @@ -142,7 +142,7 @@ class TxLogger { editorPanel: this._components.registry.get('editorpanel').api, txListener: this._components.registry.get('txlistener').api, eventsDecoder: this._components.registry.get('eventsdecoder').api, - compiler: this._components.registry.get('compiler').api, + compilersArtefacts: this._components.registry.get('compilersartefacts').api, app: this._components.registry.get('app').api } @@ -216,8 +216,8 @@ function log (self, tx, receipt) { var resolvedTransaction = self._deps.txListener.resolvedTransaction(tx.hash) if (resolvedTransaction) { var compiledContracts = null - if (self._deps.compiler.lastCompilationResult && self._deps.compiler.lastCompilationResult.data) { - compiledContracts = self._deps.compiler.lastCompilationResult.data.contracts + if (self._deps.compilersArtefacts['__last']) { + compiledContracts = self._deps.compilersArtefacts['__last'].getContracts() } self._deps.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, (error, logs) => { if (!error) { diff --git a/src/app/files/chromeCloudStorageSync.js b/src/app/files/chromeCloudStorageSync.js deleted file mode 100644 index e12acb5e090..00000000000 --- a/src/app/files/chromeCloudStorageSync.js +++ /dev/null @@ -1,64 +0,0 @@ -/* global chrome */ -'use strict' -var modalDialogCustom = require('../ui/modal-dialog-custom') - -module.exports = function (filesProviders) { - if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) { - return - } - - var obj = {} - var done = false - var count = 0 - - function check (key) { - chrome.storage.sync.get(key, function (resp) { - console.log('comparing to cloud', key, resp) - - function confirmDialog (callback) { - modalDialogCustom.confirm('', 'Overwrite "' + key + '"? Click Ok to overwrite local file with file from cloud. Cancel will push your local file to the cloud.', () => { callback(true) }, () => { callback(false) }) - } - - if (typeof resp[key] !== 'undefined' && obj[key] !== resp[key]) { - confirmDialog((result) => { - if (result) { - console.log('Overwriting', key) - filesProviders['browser'].set(key, resp[key]) - } - }) - } else { - console.log('add to obj', obj, key) - filesProviders['browser'].get(key, (error, content) => { - if (error) { - console.log(error) - } else { - obj[key] = content - } - }) - } - - done++ - if (done >= count) { - chrome.storage.sync.set(obj, function () { - console.log('updated cloud files with: ', obj, this, arguments) - }) - } - }) - } - - filesProviders['browser'].resolve('browser', (error, files) => { - if (!error) { - Object.keys(files).forEach((path) => { - filesProviders['browser'].get(path, (error, content) => { - if (error) { - console.log(error) - } else { - obj[path] = content - count++ - check(path) - } - }) - }) - } - }) -} diff --git a/src/app/files/compiler-metadata.js b/src/app/files/compiler-metadata.js index 81d98bc6181..3790651c3aa 100644 --- a/src/app/files/compiler-metadata.js +++ b/src/app/files/compiler-metadata.js @@ -1,23 +1,23 @@ 'use strict' var executionContext = require('../../execution-context') +var CompilerAbstract = require('../compiler/compiler-abstract') class CompilerMetadata { - constructor (events, opts) { + constructor (opts) { var self = this - self._events = events self._opts = opts self.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'Custom'] } syncContractMetadata () { var self = this - self._events.compiler.register('compilationFinished', (success, data, source) => { - if (!success) return + self._opts.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { if (!self._opts.config.get('settings/generate-contract-metadata')) return + let compiler = new CompilerAbstract(languageVersion, data, source) var provider = self._opts.fileManager.currentFileProvider() var path = self._opts.fileManager.currentPath() if (provider && path) { - self._opts.compiler.visitContracts((contract) => { + compiler.visitContracts((contract) => { if (contract.file !== source.target) return var fileName = path + '/' + contract.name + '.json' diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 3cb159bafc4..5361093123a 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -4,6 +4,7 @@ var $ = require('jquery') var yo = require('yo-yo') var EventManager = require('../../lib/events') var globalRegistry = require('../../global/registry') +var CompilerImport = require('../compiler/compiler-imports') /* attach to files event (removed renamed) @@ -15,13 +16,13 @@ class FileManager { this.tabbedFiles = {} this.event = new EventManager() this._components = {} + this._components.compilerImport = new CompilerImport() this._components.registry = localRegistry || globalRegistry } init () { var self = this self._deps = { - compilerImport: self._components.registry.get('compilerimport').api, editor: self._components.registry.get('editor').api, config: self._components.registry.get('config').api, browserExplorer: self._components.registry.get('fileproviders/browser').api, @@ -182,7 +183,7 @@ class FileManager { if (provider !== null && this._deps.filesProviders[provider[0]]) { return this._deps.filesProviders[provider[0]] } else { - for (var handler of this._deps.compilerImport.handlers()) { + for (var handler of this._components.compilerImport.handlers()) { if (handler.match.exec(file)) { return this._deps.filesProviders[handler.type] } diff --git a/src/app/panels/editor-panel.js b/src/app/panels/editor-panel.js index 7dd62e0d125..8c4b2ef55de 100644 --- a/src/app/panels/editor-panel.js +++ b/src/app/panels/editor-panel.js @@ -26,7 +26,7 @@ class EditorPanel { txListener: self._components.registry.get('txlistener').api, fileManager: self._components.registry.get('filemanager').api, udapp: self._components.registry.get('udapp').api, - compiler: self._components.registry.get('compiler').api + pluginManager: self._components.registry.get('pluginmanager').api } self.data = { _FILE_SCROLL_DELTA: 200, @@ -40,16 +40,18 @@ class EditorPanel { self._view = {} var editor = new Editor({}) self._components.registry.put({api: editor, name: 'editor'}) - var contextualListener = new ContextualListener({editor: editor}) + + var contextualListener = new ContextualListener({editor, pluginManager: self._deps.pluginManager}) + var contextView = new ContextView({contextualListener, editor}) + self._components = { editor: editor, contextualListener: contextualListener, - contextView: new ContextView({contextualListener: contextualListener, editor: editor}), + contextView: contextView, + // TODO list of compilers is always empty; should find a path to add plugin compiler here terminal: new Terminal({ udapp: self._deps.udapp, - compilers: { - 'solidity': self._deps.compiler - } + compilers: {} }, { getPosition: (event) => { diff --git a/src/app/panels/file-panel.js b/src/app/panels/file-panel.js index ee3806a74fb..a1586dd56be 100644 --- a/src/app/panels/file-panel.js +++ b/src/app/panels/file-panel.js @@ -49,7 +49,7 @@ function filepanel (localRegistry) { fileProviders: self._components.registry.get('fileproviders').api, fileManager: self._components.registry.get('filemanager').api, config: self._components.registry.get('config').api, - compiler: self._components.registry.get('compiler').api + pluginManager: self._components.registry.get('pluginmanager').api } var fileExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['browser']) var fileSystemExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['localhost']) @@ -62,12 +62,9 @@ function filepanel (localRegistry) { // ----------------- editor panel ---------------------- self._compilerMetadata = new CompilerMetadata( - { - compiler: self._deps.compiler.event - }, { fileManager: self._deps.fileManager, - compiler: self._deps.compiler, + pluginManager: self._deps.pluginManager, config: self._deps.config } ) diff --git a/src/app/panels/righthand-panel.js b/src/app/panels/righthand-panel.js index e81f841108d..b995c77d739 100644 --- a/src/app/panels/righthand-panel.js +++ b/src/app/panels/righthand-panel.js @@ -5,22 +5,14 @@ const EventManager = require('../../lib/events') var globalRegistry = require('../../global/registry') const styleguide = require('../ui/styles-guide/theme-chooser') -const PluginManager = require('../plugin/pluginManager') const TabbedMenu = require('../tabs/tabbed-menu') -const CompileTab = require('../tabs/compile-tab') -const SettingsTab = require('../tabs/settings-tab') -const AnalysisTab = require('../tabs/analysis-tab') -const DebuggerTab = require('../tabs/debugger-tab') -const SupportTab = require('../tabs/support-tab') const PluginTab = require('../tabs/plugin-tab') -const TestTab = require('../tabs/test-tab') -const RunTab = require('../tabs/run-tab') const DraggableContent = require('../ui/draggableContent') const styles = styleguide.chooser() module.exports = class RighthandPanel { - constructor (localRegistry) { + constructor ({pluginManager, tabs}, localRegistry) { const self = this self._components = {} self._components.registry = localRegistry || globalRegistry @@ -33,57 +25,25 @@ module.exports = class RighthandPanel { dragbar: null } - self._deps = { - fileProviders: self._components.registry.get('fileproviders').api, - fileManager: self._components.registry.get('filemanager').api, - compiler: self._components.registry.get('compiler').api, - udapp: self._components.registry.get('udapp').api, - app: self._components.registry.get('app').api, - txlistener: self._components.registry.get('txlistener').api - } - var tabbedMenu = new TabbedMenu(self._components.registry) - var pluginManager = new PluginManager( - self._deps.app, - self._deps.compiler, - self._deps.txlistener, - self._deps.fileProviders, - self._deps.fileManager, - self._deps.udapp - ) - - self._components.registry.put({api: pluginManager, name: 'pluginmanager'}) - - var analysisTab = new AnalysisTab(self._components.registry) - analysisTab.event.register('newStaticAnaysisWarningMessage', (msg, settings) => { self._components.compile.addWarning(msg, settings) }) - - self._components.debuggerTab = new DebuggerTab(self._components.registry) - self._components = { - pluginManager: pluginManager, tabbedMenu: tabbedMenu, - compile: new CompileTab(self._components.registry), - run: new RunTab(self._components.registry), - settings: new SettingsTab(self._components.registry), - analysis: analysisTab, - debug: self._components.debuggerTab, - support: new SupportTab(self._components.registry), - test: new TestTab(self._components.registry) + tabs } - self._components.settings.event.register('plugin-loadRequest', json => { + self._components.tabs.settings.event.register('plugin-loadRequest', json => { self.loadPlugin(json) }) self.loadPlugin = function (json) { var modal = new DraggableContent(() => { - self._components.pluginManager.unregister(json) + pluginManager.unregister(json) }) var tab = new PluginTab(json) var content = tab.render() document.querySelector('body').appendChild(modal.render(json.title, json.url, content)) - self._components.pluginManager.register(json, modal, content) + pluginManager.register(json, modal, content) } self._view.dragbar = yo`
` @@ -96,7 +56,7 @@ module.exports = class RighthandPanel { ` - const { compile, run, settings, analysis, debug, support, test } = self._components + const { compile, run, settings, analysis, debug, support, test } = tabs self._components.tabbedMenu.addTab('Compile', 'compileView', compile.render()) self._components.tabbedMenu.addTab('Run', 'runView', run.render()) self._components.tabbedMenu.addTab('Analysis', 'staticanalysisView', analysis.render()) @@ -106,11 +66,7 @@ module.exports = class RighthandPanel { self._components.tabbedMenu.addTab('Support', 'supportView', support.render()) self._components.tabbedMenu.selectTabByTitle('Compile') } - // showDebugger () { - // const self = this - // if (!self._components.tabbedMenu) return - // self._components.tabbedMenu.selectTab(self._view.el.querySelector('li.debugView')) - // } + render () { const self = this if (self._view.element) return self._view.element @@ -118,7 +74,7 @@ module.exports = class RighthandPanel { } debugger () { - return this._components.debug.debugger() + return this._components.tabs.debug.debugger() } focusOn (x) { diff --git a/src/app/plugin/pluginAPI.js b/src/app/plugin/pluginAPI.js index 91ecba552ec..cf708523e0c 100644 --- a/src/app/plugin/pluginAPI.js +++ b/src/app/plugin/pluginAPI.js @@ -4,7 +4,7 @@ var SourceHighlighter = require('../editor/sourceHighlighter') /* Defines available API. `key` / `type` */ -module.exports = (pluginManager, fileProviders, fileManager, compiler, udapp) => { +module.exports = (pluginManager, fileProviders, fileManager, compilesrArtefacts, udapp) => { let highlighters = {} return { app: { @@ -51,7 +51,7 @@ module.exports = (pluginManager, fileProviders, fileManager, compiler, udapp) => }, compiler: { getCompilationResult: (mod, cb) => { - cb(null, compiler.lastCompilationResult) + cb(null, compilesrArtefacts['__last']) }, sendCompilationResult: (mod, file, source, languageVersion, data, cb) => { pluginManager.receivedDataFrom('sendCompilationResult', mod, [file, source, languageVersion, data]) diff --git a/src/app/plugin/pluginManager.js b/src/app/plugin/pluginManager.js index 6685de78d26..852c0e73b0e 100644 --- a/src/app/plugin/pluginManager.js +++ b/src/app/plugin/pluginManager.js @@ -79,14 +79,14 @@ const PluginAPI = require('./pluginAPI') * */ module.exports = class PluginManager { - constructor (app, compiler, txlistener, fileProviders, fileManager, udapp) { + constructor (app, compilersArtefacts, txlistener, fileProviders, fileManager, udapp) { const self = this self.event = new EventManager() var pluginAPI = new PluginAPI( this, fileProviders, fileManager, - compiler, + compilersArtefacts, udapp ) self._components = { pluginAPI } @@ -101,14 +101,6 @@ module.exports = class PluginManager { value: [ file ] })) }) - compiler.event.register('compilationFinished', (success, data, source) => { - self.broadcast(JSON.stringify({ - action: 'notification', - key: 'compiler', - type: 'compilationFinished', - value: [ success, data, source ] - })) - }) txlistener.event.register('newTransaction', (tx) => { self.broadcast(JSON.stringify({ @@ -119,38 +111,6 @@ module.exports = class PluginManager { })) }) - app.event.register('tabChanged', (tabName) => { - // TODO Fix this cause this event is no longer triggered - if (self.inFocus && self.inFocus !== tabName) { - // trigger unfocus - self.post(self.inFocus, JSON.stringify({ - action: 'notification', - key: 'app', - type: 'unfocus', - value: [] - })) - } - if (self.plugins[tabName]) { - // trigger focus - self.post(tabName, JSON.stringify({ - action: 'notification', - key: 'app', - type: 'focus', - value: [] - })) - self.inFocus = tabName - pluginAPI.compiler.getCompilationResult(tabName, (error, data) => { - if (!error) return - self.post(tabName, JSON.stringify({ - action: 'notification', - key: 'compiler', - type: 'compilationData', - value: [data] - })) - }) - } - }) - window.addEventListener('message', (event) => { if (event.type !== 'message') return var extension = self.origins[event.origin] @@ -202,7 +162,13 @@ module.exports = class PluginManager { receivedDataFrom (methodName, mod, argumentsArray) { // TODO check whether 'mod' as right to do that console.log(argumentsArray) - this.event.trigger(methodName, argumentsArray) + this.event.trigger(methodName, argumentsArray) // forward to internal modules + this.broadcast(JSON.stringify({ // forward to plugins + action: 'notification', + key: mod, + type: methodName, + value: argumentsArray + })) } post (name, value) { const self = this diff --git a/src/app/staticanalysis/staticAnalysisView.js b/src/app/staticanalysis/staticAnalysisView.js index 6e9eb28d23f..8e5abef71a5 100644 --- a/src/app/staticanalysis/staticAnalysisView.js +++ b/src/app/staticanalysis/staticAnalysisView.js @@ -25,21 +25,19 @@ function staticAnalysisView (localRegistry) { self._components.registry = localRegistry || globlalRegistry // dependencies self._deps = { - compiler: self._components.registry.get('compiler').api, + pluginManager: self._components.registry.get('pluginmanager').api, renderer: self._components.registry.get('renderer').api, offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api } - self._deps.compiler.event.register('compilationFinished', function (success, data, source) { + self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { self.lastCompilationResult = null self.lastCompilationSource = null $('#staticanalysisresult').empty() - if (success) { - self.lastCompilationResult = data - self.lastCompilationSource = source - if (self.view.querySelector('#autorunstaticanalysis').checked) { - self.run() - } + self.lastCompilationResult = data + self.lastCompilationSource = source + if (self.view.querySelector('#autorunstaticanalysis').checked) { + self.run() } }) } @@ -113,7 +111,10 @@ staticAnalysisView.prototype.run = function () { start: parseInt(split[0]), length: parseInt(split[1]) } - location = self._deps.offsetToLineColumnConverter.offsetToLineColumn(location, parseInt(file), self._deps.compiler.lastCompilationResult.source.sources, self._deps.compiler.lastCompilationResult.data.sources) + location = self._deps.offsetToLineColumnConverter.offsetToLineColumn(location, + parseInt(file), + self.lastCompilationSource.sources, + self.lastCompilationResult.sources) location = Object.keys(self.lastCompilationResult.contracts)[file] + ':' + (location.start.line + 1) + ':' + (location.start.column + 1) + ':' } warningCount++ diff --git a/src/app/tabs/analysis-tab.js b/src/app/tabs/analysis-tab.js index 3a16661ea75..f2d3b8d6bde 100644 --- a/src/app/tabs/analysis-tab.js +++ b/src/app/tabs/analysis-tab.js @@ -13,17 +13,15 @@ module.exports = class AnalysisTab { self.data = {} self._components = {} self._components.registry = localRegistry || globalRegistry - self._deps = { - rightHandPanel: self._components.registry.get('righthandpanel').api - } + self._deps = {} } render () { const self = this var staticanalysis = new StaticAnalysis() staticanalysis.event.register('staticAnaysisWarning', (count) => { if (count > 0) { - const msg = `Static Analysis raised ${count} warning(s) that requires your attention. Click here to show the warning(s).` - const settings = { type: 'staticAnalysisWarning', click: () => self._deps.rightHandPanel.focusOn('staticanalysisView'), useSpan: true } + const msg = `Static Analysis raised ${count} warning(s) that requires your attention. Check Solidity Static Analysis Module for more information.` + const settings = { type: 'staticAnalysisWarning', useSpan: true } self.event.trigger('newStaticAnaysisWarningMessage', [msg, settings]) } }) diff --git a/src/app/tabs/compile-tab.js b/src/app/tabs/compile-tab.js index 7e4b7e5a6ee..f5b9aa5365a 100644 --- a/src/app/tabs/compile-tab.js +++ b/src/app/tabs/compile-tab.js @@ -1,9 +1,13 @@ /* global Worker */ +const async = require('async') +const $ = require('jquery') const yo = require('yo-yo') const csjs = require('csjs-inject') const copy = require('clipboard-copy') var minixhr = require('minixhr') -var tooltip = require('../ui/tooltip') +var remixTests = require('remix-tests') +var Compiler = require('remix-solidity').Compiler +var CompilerImport = require('../compiler/compiler-imports') var QueryParams = require('../../lib/query-params') var globalRegistry = require('../../global/registry') const TreeView = require('../ui/TreeView') @@ -40,15 +44,18 @@ module.exports = class CompileTab { self._components = {} self._components.registry = localRegistry || globalRegistry self._components.queryParams = new QueryParams() + self._components.compilerImport = new CompilerImport() + self._components.compiler = new Compiler((url, cb) => self.importFileCb(url, cb)) + // dependencies self._deps = { - app: self._components.registry.get('app').api, editor: self._components.registry.get('editor').api, config: self._components.registry.get('config').api, - compiler: self._components.registry.get('compiler').api, renderer: self._components.registry.get('renderer').api, swarmfileProvider: self._components.registry.get('fileproviders/swarm').api, - fileManager: self._components.registry.get('filemanager').api + fileManager: self._components.registry.get('filemanager').api, + fileProviders: self._components.registry.get('fileproviders').api, + pluginManager: self._components.registry.get('pluginmanager').api } self.data = { hideWarnings: self._deps.config.get('hideWarnings') || false, @@ -59,21 +66,22 @@ module.exports = class CompileTab { timeout: 300, allversions: null, selectedVersion: null, + defaultVersion: 'soljson-v0.5.1+commit.c8a2cb62.js', // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler baseurl: 'https://solc-bin.ethereum.org/bin' } self.data.optimize = self._components.queryParams.get().optimize self.data.optimize = self.data.optimize === 'true' self._components.queryParams.update({ optimize: self.data.optimize }) - self._deps.compiler.setOptimize(self.data.optimize) + self._components.compiler.setOptimize(self.data.optimize) self._deps.editor.event.register('contentChanged', scheduleCompilation) self._deps.editor.event.register('sessionSwitched', scheduleCompilation) function scheduleCompilation () { if (!self._deps.config.get('autoCompile')) return if (self.data.compileTimeout) window.clearTimeout(self.data.compileTimeout) - self.data.compileTimeout = window.setTimeout(() => self._deps.app.runCompiler(), self.data.timeout) + self.data.compileTimeout = window.setTimeout(() => self.runCompiler(), self.data.timeout) } - self._deps.compiler.event.register('compilationDuration', function tabHighlighting (speed) { + self._components.compiler.event.register('compilationDuration', function tabHighlighting (speed) { if (!self._view.warnCompilationSlow) return if (speed > self.data.maxTime) { const msg = `Last compilation took ${speed}ms. We suggest to turn off autocompilation.` @@ -87,13 +95,13 @@ module.exports = class CompileTab { if (!self._view.compileIcon) return self._view.compileIcon.classList.add(`${css.bouncingIcon}`) // @TODO: compileView tab }) - self._deps.compiler.event.register('loadingCompiler', function start () { + self._components.compiler.event.register('loadingCompiler', function start () { if (!self._view.compileIcon) return self._view.compileIcon.classList.add(`${css.spinningIcon}`) self._view.warnCompilationSlow.style.visibility = 'hidden' self._view.compileIcon.setAttribute('title', 'compiler is loading, please wait a few moments.') }) - self._deps.compiler.event.register('compilationStarted', function start () { + self._components.compiler.event.register('compilationStarted', function start () { if (!self._view.compileIcon) return self._view.errorContainer.innerHTML = '' self._view.errorContainerHead.innerHTML = '' @@ -101,12 +109,12 @@ module.exports = class CompileTab { self._view.compileIcon.classList.add(`${css.spinningIcon}`) self._view.compileIcon.setAttribute('title', 'compiling...') }) - self._deps.compiler.event.register('compilerLoaded', function loaded () { + self._components.compiler.event.register('compilerLoaded', function loaded () { if (!self._view.compileIcon) return self._view.compileIcon.classList.remove(`${css.spinningIcon}`) self._view.compileIcon.setAttribute('title', '') }) - self._deps.compiler.event.register('compilationFinished', function finish (success, data, source) { + self._components.compiler.event.register('compilationFinished', function finish (success, data, source) { if (self._view.compileIcon) { const compileTab = document.querySelector('.compileView') compileTab.style.color = styles.colors.black @@ -120,9 +128,11 @@ module.exports = class CompileTab { // refill the dropdown list self._view.contractNames.innerHTML = '' if (success) { + // TODO consider using compile tab as a proper module instead of just forwarding event + self._deps.pluginManager.receivedDataFrom('sendCompilationResult', 'solidity-compiler', [data.target, source, self.data.selectedVersion, data]) self._view.contractNames.removeAttribute('disabled') - self._deps.compiler.visitContracts(contract => { - self.data.contractsDetails[contract.name] = parseContracts(contract.name, contract.object, self._deps.compiler.getSource(contract.file)) + self._components.compiler.visitContracts(contract => { + self.data.contractsDetails[contract.name] = parseContracts(contract.name, contract.object, self._components.compiler.getSource(contract.file)) var contractName = yo`` self._view.contractNames.appendChild(contractName) }) @@ -160,11 +170,20 @@ module.exports = class CompileTab { }) } if (!error && data.contracts) { - self._deps.compiler.visitContracts((contract) => { + self._components.compiler.visitContracts((contract) => { self._deps.renderer.error(contract.name, self._view.errorContainer, {type: 'success'}) }) } }) + + // Run the compiler instead of trying to save the website + $(window).keydown(function (e) { + // ctrl+s or command+s + if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) { + e.preventDefault() + self.runCompiler() + } + }) } addWarning (msg, settings) { const self = this @@ -182,11 +201,11 @@ module.exports = class CompileTab { function onchangeOptimize (event) { self.data.optimize = !!self._view.optimize.checked self._components.queryParams.update({ optimize: self.data.optimize }) - self._deps.compiler.setOptimize(self.data.optimize) - self._deps.app.runCompiler() + self._components.compiler.setOptimize(self.data.optimize) + self.runCompiler() } - self._deps.compiler.event.register('compilerLoaded', (version) => self.setVersionText(version)) + self._components.compiler.event.register('compilerLoaded', (version) => self.setVersionText(version)) self.fetchAllVersion((allversions, selectedVersion) => { self.data.allversions = allversions self.data.selectedVersion = selectedVersion @@ -200,7 +219,6 @@ module.exports = class CompileTab { ` - if (self.data.allversions && self.data.selectedVersion) self._updateVersionSelector() self._view.version = yo`` self._view.warnCompilationSlow = yo`` @@ -279,7 +297,7 @@ module.exports = class CompileTab { 'web3Deploy': 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract' } function updateAutoCompile (event) { self._deps.config.set('autoCompile', self._view.autoCompile.checked) } - function compile (event) { self._deps.app.runCompiler() } + function compile (event) { self.runCompiler() } function hideWarnings (event) { self._deps.config.set('hideWarnings', self._view.hideWarningsBox.checked) compile() @@ -424,10 +442,10 @@ module.exports = class CompileTab { // 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. - self._deps.compiler.loadVersion(true, url) + self._components.compiler.loadVersion(true, url) self.setVersionText('(loading using worker)') } else { - self._deps.compiler.loadVersion(false, url) + self._components.compiler.loadVersion(false, url) self.setVersionText('(loading)') } } @@ -440,10 +458,10 @@ module.exports = class CompileTab { try { const data = JSON.parse(json) allversions = data.builds.slice().reverse() - selectedVersion = data.releases[data.latestRelease] + selectedVersion = self.data.defaultVersion if (self._components.queryParams.get().version) selectedVersion = self._components.queryParams.get().version } catch (e) { - tooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload.') + addTooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload.') } } else { allversions = [{ path: 'builtin', longVersion: 'latest local version' }] @@ -452,6 +470,81 @@ module.exports = class CompileTab { callback(allversions, selectedVersion) }) } + runCompiler () { + const self = this + self._deps.fileManager.saveCurrentFile() + self._deps.editor.clearAnnotations() + var currentFile = self._deps.config.get('currentFile') + if (currentFile) { + if (/.(.sol)$/.exec(currentFile)) { + // only compile *.sol file. + var target = currentFile + var sources = {} + var provider = self._deps.fileManager.fileProviderOf(currentFile) + if (provider) { + provider.get(target, (error, content) => { + if (error) { + console.log(error) + } else { + sources[target] = { content } + self._components.compiler.compile(sources, target) + } + }) + } else { + console.log('cannot compile ' + currentFile + '. Does not belong to any explorer') + } + } + } + } + importExternal (url, cb) { + const self = this + self._components.compilerImport.import(url, + (loadingMsg) => { + addTooltip(loadingMsg) + }, + (error, content, cleanUrl, type, url) => { + if (!error) { + if (self._deps.filesProviders[type]) { + self._deps.filesProviders[type].addReadOnly(cleanUrl, content, url) + } + cb(null, content) + } else { + cb(error) + } + }) + } + importFileCb (url, filecb) { + const self = this + if (url.indexOf('/remix_tests.sol') !== -1) { + return filecb(null, remixTests.assertLibCode) + } + var provider = self._deps.fileManager.fileProviderOf(url) + if (provider) { + if (provider.type === 'localhost' && !provider.isConnected()) { + return filecb(`file provider ${provider.type} not available while trying to resolve ${url}`) + } + provider.exists(url, (error, exist) => { + if (error) return filecb(error) + if (exist) { + return provider.get(url, filecb) + } else { + self.importExternal(url, filecb) + } + }) + } else if (self._components.compilerImport.isRelativeImport(url)) { + // try to resolve localhost modules (aka truffle imports) + var splitted = /([^/]+)\/(.*)$/g.exec(url) + async.tryEach([ + (cb) => { self.importFileCb('localhost/installed_contracts/' + url, cb) }, + (cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { self.importFileCb('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], cb) } }, + (cb) => { self.importFileCb('localhost/node_modules/' + url, cb) }, + (cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { self.importFileCb('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], cb) } }], + (error, result) => { filecb(error, result) } + ) + } else { + self.importExternal(url, filecb) + } + } } const css = csjs` diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 03e9f4f1c31..4067e69e632 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -71,7 +71,6 @@ function runTab (opts, localRegistry) { } // dependencies self._deps = { - compiler: self._components.registry.get('compiler').api, udapp: self._components.registry.get('udapp').api, udappUI: self._components.registry.get('udappUI').api, config: self._components.registry.get('config').api, @@ -310,21 +309,10 @@ function contractDropdown (events, self) { } self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { - // TODO check whether the tab is configured - let compiler = new CompilerAbstract(languageVersion, data) - self._deps.compilersArtefacts[languageVersion] = compiler - self._deps.compilersArtefacts['__last'] = compiler + let compiler = new CompilerAbstract(languageVersion, data, source) newlyCompiled(true, data, source, compiler, languageVersion) }) - self._deps.compiler.event.register('compilationFinished', (success, data, source) => { - var name = 'solidity' - let compiler = new CompilerAbstract(name, data) - self._deps.compilersArtefacts[name] = compiler - self._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(success, data, source, self._deps.compiler, name) - }) - var deployAction = (value) => { self._view.createPanel.style.display = value self._view.orLabel.style.display = value @@ -350,7 +338,7 @@ function contractDropdown (events, self) { function getSelectedContract () { var contract = selectContractNames.children[selectContractNames.selectedIndex] var contractName = contract.innerHTML - var compiler = self._deps.compilersArtefacts[contract.getAttribute('compiler')] + var compiler = self._deps.compilersArtefacts['__last'] if (!compiler) return null if (contractName) { diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js index cb00efc7976..b8f38ed79cc 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -18,11 +18,9 @@ module.exports = class SettingsTab { self._components.registry = localRegistry || globalRegistry // dependencies self._deps = { - compiler: self._components.registry.get('compiler').api, config: self._components.registry.get('config').api, editorPanel: self._components.registry.get('editorpanel').api, - editor: self._components.registry.get('editor').api, - righthandpanel: self._components.registry.get('righthandpanel').api + editor: self._components.registry.get('editor').api } self._view = { /* eslint-disable */ el: null, diff --git a/src/app/tabs/test-tab.js b/src/app/tabs/test-tab.js index 624857b3a2c..bb85ba84a2d 100644 --- a/src/app/tabs/test-tab.js +++ b/src/app/tabs/test-tab.js @@ -8,15 +8,16 @@ var css = require('./styles/test-tab-styles') var remixTests = require('remix-tests') module.exports = class TestTab { - constructor (localRegistry) { + constructor (localRegistry, compileTab) { + // TODO here is a direct reference to compile tab, should be removed const self = this + self.compileTab = compileTab self._view = { el: null } self._components = {} self._components.registry = localRegistry || globalRegistry // dependencies self._deps = { fileManager: self._components.registry.get('filemanager').api, - app: self._components.registry.get('app').api, filePanel: self._components.registry.get('filepanel').api } self.data = {} @@ -76,7 +77,7 @@ module.exports = class TestTab { remixTests.runTestSources(runningTest, testCallback, resultsCallback, (error, result) => { updateFinalResult(error, result, testFilePath) callback(error) - }, (url, cb) => { self._deps.app.importFileCb(url, cb) }) + }, (url, cb) => { self.compileTab.importFileCb(url, cb) }) } }) } diff --git a/src/lib/cmdInterpreterAPI.js b/src/lib/cmdInterpreterAPI.js index 0c9b7b6cacc..7f4e0716c9f 100644 --- a/src/lib/cmdInterpreterAPI.js +++ b/src/lib/cmdInterpreterAPI.js @@ -24,7 +24,7 @@ class CmdInterpreterAPI { app: self._components.registry.get('app').api, fileManager: self._components.registry.get('filemanager').api, editor: self._components.registry.get('editor').api, - compiler: self._components.registry.get('compiler').api, + compilersArtefacts: self._components.registry.get('compilersartefacts').api, offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api } self.commandHelp = { @@ -47,7 +47,9 @@ class CmdInterpreterAPI { self._components.sourceHighlighter.currentSourceLocation(null) return } - var lineColumnPos = self._deps.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, self._deps.compiler.lastCompilationResult.source.sources, self._deps.compiler.lastCompilationResult.data.sources) + var lineColumnPos = self._deps.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, + self._deps.compilersArtefacts['__last'].getSourceCode().sources, + self._deps.compilersArtefacts['__last'].getAsts()) self._components.sourceHighlighter.currentSourceLocation(lineColumnPos, rawLocation) } debug (hash, cb) { @@ -57,7 +59,7 @@ class CmdInterpreterAPI { if (error) return cb(error) var debugSession = new RemixDebug({ compilationResult: () => { - return self._deps.compiler.lastCompilationResult.data + return self._deps.compilersArtefacts['__last'].getData() } }) debugSession.addProvider('web3', executionContext.web3()) @@ -108,7 +110,9 @@ class CmdInterpreterAPI { self.d.goTo = (row) => { if (self._deps.editor.current()) { var breakPoint = new remixLib.code.BreakpointManager(self.d, (sourceLocation) => { - return self._deps.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, self._deps.compiler.lastCompilationResult.source.sources, self._deps.compiler.lastCompilationResult.data.sources) + return self._deps.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, + self._deps.compilersArtefacts['__last'].getSourceCode().sources, + self._deps.compilersArtefacts['__last'].getAsts()) }) breakPoint.event.register('breakpointHit', (sourceLocation, currentStep) => { self.log(null, 'step index ' + currentStep) diff --git a/src/lib/offsetToLineColumnConverter.js b/src/lib/offsetToLineColumnConverter.js index d85d443f89c..115cb59aa36 100644 --- a/src/lib/offsetToLineColumnConverter.js +++ b/src/lib/offsetToLineColumnConverter.js @@ -1,13 +1,10 @@ 'use strict' var SourceMappingDecoder = require('remix-lib').SourceMappingDecoder -function offsetToColumnConverter (compilerEvent) { +function offsetToColumnConverter () { this.lineBreakPositionsByContent = {} this.sourceMappingDecoder = new SourceMappingDecoder() - var self = this - compilerEvent.register('compilationFinished', function (success, data, source) { - self.clear() - }) + // we don't listen anymore on compilation result for clearing the cache } offsetToColumnConverter.prototype.offsetToLineColumn = function (rawLocation, file, sources, asts) { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 66c98fef197..7c77508a657 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -31,7 +31,7 @@ function UniversalDApp (opts, localRegistry) { self.removable_instances = opts.removable_instances self._deps = { config: self._components.registry.get('config').api, - compiler: self._components.registry.get('compiler').api, + compilersartefacts: self._components.registry.get('compilersartefacts').api, logCallback: self._components.registry.get('logCallback').api } executionContext.event.register('contextChanged', this, function (context) { @@ -47,10 +47,6 @@ function UniversalDApp (opts, localRegistry) { } } self.txRunner = new TxRunner({}, self._txRunnerAPI) - self.data.contractsDetails = {} - self._deps.compiler.event.register('compilationFinished', (success, data, source) => { - self.data.contractsDetails = success && data ? data.contracts : {} - }) self.accounts = {} self.resetEnvironment() } @@ -204,8 +200,7 @@ UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}` } } - // contractsDetails is used to resolve libraries - txFormat.buildData(args.contractName, args.contractAbi, self.data.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { + txFormat.buildData(args.contractName, args.contractAbi, self._deps.compilersartefacts['__last'].getData().contracts, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { if (!error) { if (isUserAction) { if (!args.funABI.constant) { diff --git a/test-browser/tests/ballot.js b/test-browser/tests/ballot.js index 3fb9573ea7c..d5b5d06c7be 100644 --- a/test-browser/tests/ballot.js +++ b/test-browser/tests/ballot.js @@ -48,7 +48,7 @@ function runTests (browser, testData) { .pause(2000) .perform(function (client, done) { console.log('goToVMtraceStep') - contractHelper.goToVMtraceStep(browser, 55, () => { + contractHelper.goToVMtraceStep(browser, 59, () => { done() }) }) diff --git a/test-browser/tests/sharedFolderExplorer.js b/test-browser/tests/sharedFolderExplorer.js index 0dd6028fb93..e4e8efbdb16 100644 --- a/test-browser/tests/sharedFolderExplorer.js +++ b/test-browser/tests/sharedFolderExplorer.js @@ -58,6 +58,11 @@ function runTests (browser, testData) { browser.end() return } + if (browserName === 'chrome') { + console.log('do not run remixd test for ' + browserName + ': TODO to reenable later') + browser.end() + return + } browser .waitForElementVisible('.newFile', 10000) .click('.websocketconn')