Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load wasm builds if available #2828

Merged
merged 14 commits into from
Jun 4, 2020
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
"nightwatch_local_firefox": "nightwatch --config nightwatch.js --env firefox",
"nightwatch_local_chrome": "nightwatch --config nightwatch.js --env chrome",
"nightwatch_local_ballot": "nightwatch ./test-browser/tests/ballot.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_usingWorker": "nightwatch ./test-browser/tests/usingWebWorker.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_libraryDeployment": "nightwatch ./test-browser/tests/libraryDeployment.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_solidityImport": "nightwatch ./test-browser/tests/solidityImport.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_recorder": "nightwatch ./test-browser/tests/recorder.test.js --config nightwatch.js --env chrome ",
Expand Down
4 changes: 2 additions & 2 deletions src/app/compiler/compiler-helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use strict'
import { canUseWorker } from './compiler-utils'
import { canUseWorker, urlFromVersion } from './compiler-utils'
import { Compiler } from 'remix-solidity'
import CompilerAbstract from './compiler-abstract'

Expand All @@ -9,7 +9,7 @@ export const compile = async (compilationTargets, settings) => {
const compiler = new Compiler(() => {})
compiler.set('evmVersion', settings.evmVersion)
compiler.set('optimize', settings.optimize)
compiler.loadVersion(canUseWorker(settings.version), settings.compilerUrl)
compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version))
compiler.event.register('compilationFinished', (success, compilationData, source) => {
if (!success) return reject(compilationData)
resolve(new CompilerAbstract(settings.version, compilationData, source))
Expand Down
4 changes: 1 addition & 3 deletions src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const ethutil = require('ethereumjs-util')
import * as packageJson from '../../../package.json'
import { Plugin } from '@remixproject/engine'
import { urlFromVersion } from './compiler-utils'
import { compile } from './compiler-helpers'
import globalRegistry from '../../global/registry'

Expand Down Expand Up @@ -108,8 +107,7 @@ export default class FetchAndCompile extends Plugin {
version: data.metadata.compiler.version,
languageName: data.metadata.language,
evmVersion: data.metadata.settings.evmVersion,
optimize: data.metadata.settings.optimizer.enabled,
compilerUrl: urlFromVersion(data.metadata.compiler.version)
optimize: data.metadata.settings.optimizer.enabled
}
try {
setTimeout(_ => this.emit('compiling', settings), 0)
Expand Down
54 changes: 25 additions & 29 deletions src/app/compiler/compiler-utils.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,44 @@
const semver = require('semver')
const minixhr = require('minixhr')
/* global Worker */

export const baseUrl = 'https://solc-bin.ethereum.org/bin'
export const baseURLBin = 'https://solc-bin.ethereum.org/bin'
export const baseURLWasm = 'https://solc-bin.ethereum.org/wasm'

export const pathToURL = {}

/**
* Retrieves the URL of the given compiler version
* @param version is the version of compiler with or without 'soljson-v' prefix and .js postfix
*/
export function urlFromVersion (version) {
return `${baseUrl}/soljson-v${version}.js`
if (!version.startsWith('soljson-v')) version = 'soljson-v' + version
if (!version.endsWith('.js')) version = version + '.js'
return `${pathToURL[version]}/${version}`
}

/**
* Checks if the worker can be used to load a compiler.
* checks a compiler whitelist, browser support and OS.
*/
export function canUseWorker (selectedVersion) {
// Following restrictions should be deleted when Solidity will release fixed versions of compilers.
// See https://github.com/ethereum/remix-ide/issues/2461
const isChrome = !!window.chrome
const os = retrieveOS()
// define a whitelist for Linux
const linuxWL = ['0.4.26', '0.5.3', '0.5.4', '0.5.5']
const version = semver.coerce(selectedVersion)
// defining whitelist for chrome
let isFromWhiteList = false
switch (os) {
case 'Windows':
isFromWhiteList = semver.gt(version, '0.5.2') || version === '0.4.26'
break
case 'Linux':
isFromWhiteList = semver.gt(version, '0.5.13') || linuxWL.includes(version)
break
default :
isFromWhiteList = true
}
return browserSupportWorker() && (!isChrome || (isChrome && isFromWhiteList))
const isNightly = selectedVersion.includes('nightly')
return browserSupportWorker() && (
semver.gt(version, '0.6.3') ||
semver.gt(version, '0.3.6') && !isNightly
)
}

function browserSupportWorker () {
return document.location.protocol !== 'file:' && Worker !== undefined
}

function retrieveOS () {
let osName = 'Unknown OS'
if (navigator.platform.indexOf('Win') !== -1) {
osName = 'Windows'
} else if (navigator.platform.indexOf('Linux') !== -1) {
osName = 'Linux'
}
return osName
// returns a promise for minixhr
export function promisedMiniXhr (url) {
return new Promise((resolve, reject) => {
minixhr(url, (json, event) => {
resolve({ json, event })
})
})
}
13 changes: 12 additions & 1 deletion src/app/editor/example-contracts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
'use strict'

const basic = `pragma solidity >=0.2.0 <0.7.0;

/**
* @title Basic contract
*/
contract Basic {
uint someVar;
constructor() public {}
}`

const storage = `pragma solidity >=0.4.22 <0.7.0;

/**
Expand Down Expand Up @@ -245,5 +255,6 @@ module.exports = {
storage: { name: '1_Storage.sol', content: storage },
owner: { name: '2_Owner.sol', content: owner },
ballot: { name: '3_Ballot.sol', content: ballot },
ballot_test: { name: '4_Ballot_test.sol', content: ballotTest }
ballot_test: { name: '4_Ballot_test.sol', content: ballotTest },
basic: { name: 'basic.sol', content: basic }
}
5 changes: 4 additions & 1 deletion src/app/files/fileProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ class FileProvider {
cb = cb || function () {}
var unprefixedpath = this.removePrefix(path)
var exists = window.remixFileSystem.existsSync(unprefixedpath)
if (exists && window.remixFileSystem.readFileSync(unprefixedpath, 'utf8') === content) return true
if (exists && window.remixFileSystem.readFileSync(unprefixedpath, 'utf8') === content) {
cb()
return true
}
if (!exists && unprefixedpath.indexOf('/') !== -1) {
this.createDir(path)
}
Expand Down
3 changes: 2 additions & 1 deletion src/app/tabs/compile-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class CompileTab extends ViewPlugin {
this.fileManager.events.on('noFileSelected', this.data.eventHandlers.onNoFileSelected)

this.data.eventHandlers.onCompilationFinished = (success, data, source) => {
this._view.errorContainer.appendChild(yo`<span data-id="compilationFinishedWith_${this.getCurrentVersion()}"></span>`)
if (success) {
// forwarding the event to the appManager infra
this.emit('compilationFinished', source.target, source, 'soljson', data)
Expand Down Expand Up @@ -411,7 +412,7 @@ class CompileTab extends ViewPlugin {
render () {
if (this._view.el) return this._view.el
this.onActivationInternal()
this._view.errorContainer = yo`<div class="${css.errorBlobs} p-4"></div>`
this._view.errorContainer = yo`<div class="${css.errorBlobs} p-4" data-id="compiledErrors" ></div>`
this._view.contractSelection = this.contractSelection()
this._view.compilerContainer = this.compilerContainer.render()
this.compilerContainer.activate()
Expand Down
63 changes: 38 additions & 25 deletions src/app/tabs/compileTab/compilerContainer.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@

const yo = require('yo-yo')
const minixhr = require('minixhr')
const helper = require('../../../lib/helper')
const addTooltip = require('../../ui/tooltip')
const semver = require('semver')
const modalDialogCustom = require('../../ui/modal-dialog-custom')
const css = require('../styles/compile-tab-styles')
import { canUseWorker } from '../../compiler/compiler-utils'
import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '../../compiler/compiler-utils'

class CompilerContainer {

Expand All @@ -24,8 +23,7 @@ class CompilerContainer {
timeout: 300,
allversions: null,
selectedVersion: null,
defaultVersion: 'soljson-v0.6.6+commit.6c089d02.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'
defaultVersion: 'soljson-v0.6.6+commit.6c089d02.js' // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler
}
}

Expand Down Expand Up @@ -383,13 +381,13 @@ class CompilerContainer {
if (this.data.selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(this.data.selectedVersion)) {
return console.log('loading ' + this.data.selectedVersion + ' not allowed')
}
url = `${this.data.baseurl}/${this.data.selectedVersion}`
url = `${urlFromVersion(this.data.selectedVersion)}`
}

// 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.
if (canUseWorker(this.data.selectedVersion)) {
if (this.data.selectedVersion !== 'builtin' && canUseWorker(this.data.selectedVersion)) {
this.compileTabLogic.compiler.loadVersion(true, url)
this.setVersionText('(loading using worker)')
} else {
Expand All @@ -413,27 +411,42 @@ class CompilerContainer {
if (this._view.version) this._view.version.innerText = text
}

fetchAllVersion (callback) {
minixhr(`${this.data.baseurl}/list.json`, (json, event) => {
// @TODO: optimise and cache results to improve app loading times #2461
var allversions, selectedVersion
if (event.type !== 'error') {
try {
const data = JSON.parse(json)
allversions = data.builds.slice().reverse()
selectedVersion = this.data.defaultVersion
if (this.queryParams.get().version) selectedVersion = this.queryParams.get().version
} catch (e) {
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' }]
selectedVersion = 'builtin'
// fetching both normal and wasm builds and creating a [version, baseUrl] map
async fetchAllVersion (callback) {
let allVersions, selectedVersion, allVersionsWasm
// fetch normal builds
const binRes = await promisedMiniXhr(`${baseURLBin}/list.json`)
// fetch wasm builds
const wasmRes = await promisedMiniXhr(`${baseURLWasm}/list.json`)
if (binRes.event.type === 'error' && wasmRes.event.type === 'error') {
allVersions = [{ path: 'builtin', longVersion: 'latest local version' }]
selectedVersion = 'builtin'
callback(allVersions, selectedVersion)
}
try {
allVersions = JSON.parse(binRes.json).builds.slice().reverse()
selectedVersion = this.data.defaultVersion
if (this.queryParams.get().version) selectedVersion = this.queryParams.get().version
if (wasmRes.event.type !== 'error') {
allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse()
}
callback(allversions, selectedVersion)
})
} catch (e) {
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. Error: ' + e)
}
// replace in allVersions those compiler builds which exist in allVersionsWasm with new once
if (allVersionsWasm && allVersions) {
allVersions.forEach((compiler, index) => {
const wasmIndex = allVersionsWasm.findIndex(wasmCompiler => { return wasmCompiler.longVersion === compiler.longVersion })
if (wasmIndex !== -1) {
allVersions[index] = allVersionsWasm[wasmIndex]
pathToURL[compiler.path] = baseURLWasm
} else {
pathToURL[compiler.path] = baseURLBin
}
})
}
callback(allVersions, selectedVersion)
}

scheduleCompilation () {
if (!this.config.get('autoCompile')) return
if (this.data.compileTimeout) window.clearTimeout(this.data.compileTimeout)
Expand Down
6 changes: 3 additions & 3 deletions src/app/tabs/test-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var tooltip = require('../ui/tooltip')
var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests')
import { ViewPlugin } from '@remixproject/engine'
import { canUseWorker, baseUrl } from '../compiler/compiler-utils'
import { canUseWorker, urlFromVersion } from '../compiler/compiler-utils'

const TestTabLogic = require('./testTab/testTab')

Expand Down Expand Up @@ -301,7 +301,7 @@ module.exports = class TestTab extends ViewPlugin {
let runningTest = {}
runningTest[path] = { content }
const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = baseUrl + '/' + currentVersion
const currentCompilerUrl = urlFromVersion(currentVersion)
const compilerConfig = {
currentCompilerUrl,
evmVersion,
Expand All @@ -327,7 +327,7 @@ module.exports = class TestTab extends ViewPlugin {
const runningTest = {}
runningTest[testFilePath] = { content }
const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = baseUrl + '/' + currentVersion
const currentCompilerUrl = urlFromVersion(currentVersion)
const compilerConfig = {
currentCompilerUrl,
evmVersion,
Expand Down
1 change: 0 additions & 1 deletion src/lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,3 @@ function find (args, query) {
})
return isMatch
}

28 changes: 28 additions & 0 deletions test-browser/commands/noWorkerErrorFor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const EventEmitter = require('events')

class NoWorkerErrorFor extends EventEmitter {
command (version) {
this.api.perform((done) => {
noWorkerErrorFor(this.api, version, () => {
done()
this.emit('complete')
})
})
return this
}
}

function noWorkerErrorFor (browser, version, callback) {
browser
.setSolidityCompilerVersion(version)
.click('*[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('*[data-id="compilationFinishedWith_' + version + '"]', 10000)
.notContainsText('*[data-id="compiledErrors"]', 'worker error:undefined')
.notContainsText('*[data-id="compiledErrors"]', 'Uncaught RangeError: Maximum call stack size exceeded')
.notContainsText('*[data-id="compiledErrors"]', 'RangeError: Maximum call stack size exceeded')
.perform(() => {
callback()
})
}

module.exports = NoWorkerErrorFor
7 changes: 3 additions & 4 deletions test-browser/tests/runAndDeploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ module.exports = {
.assert.elementNotPresent('*[data-id="settingsRemixRunSignMsgHash"]')
.assert.elementNotPresent('*[data-id="settingsRemixRunSignMsgSignature"]')
.modalFooterOKClick()
.pause(2000)
.waitForElementPresent('*[data-id="modalDialogContainer"]')
.waitForElementPresent('*[data-id="modalDialogContainer"]', 12000)
.assert.elementPresent('*[data-id="settingsRemixRunSignMsgHash"]')
.assert.elementPresent('*[data-id="settingsRemixRunSignMsgSignature"]')
.modalFooterOKClick()
Expand Down Expand Up @@ -145,8 +144,8 @@ module.exports = {
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.pause(2000)
.waitForElementPresent('*[data-id="modalDialogContainer"]')
.waitForElementPresent('*[data-id="modalDialogContainer"]', 15000)
.pause(10000)
.assert.containsText('*[data-id="modalDialogModalBody"]', 'You are creating a transaction on the main network. Click confirm if you are sure to continue.')
.modalFooterCancelClick()
},
Expand Down
2 changes: 1 addition & 1 deletion test-browser/tests/solidityUnittests.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ module.exports = {
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.pause(5000)
.click('*[data-id="testTabRunTestsTabStopAction"]')
.pause(2000)
.pause(1000)
.assert.containsText('*[data-id="testTabRunTestsTabStopAction"]', 'Stopping')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'browser/ks2b_test.sol')
Expand Down
37 changes: 37 additions & 0 deletions test-browser/tests/usingWebWorker.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict'
var examples = require('../../src/app/editor/example-contracts')
var init = require('../helpers/init')
var sauce = require('./sauce')

var sources = [
{'browser/basic.sol': {content: examples.basic.content}}
]

module.exports = {
before: function (browser, done) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Using Web Worker': function (browser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('fileExplorers')
.openFile('browser/basic.sol')
.clickLaunchIcon('solidity')
.execute(() => {
document.getElementById('nightlies').checked = true
})
.noWorkerErrorFor('soljson-v0.3.4+commit.7dab890.js')
.noWorkerErrorFor('soljson-v0.6.5+commit.f956cc89.js')
.noWorkerErrorFor('soljson-v0.6.8-nightly.2020.5.14+commit.a6d0067b.js')
.noWorkerErrorFor('soljson-v0.6.0-nightly.2019.12.17+commit.d13438ee.js')
.noWorkerErrorFor('soljson-v0.4.26+commit.4563c3fc.js')
.execute(() => {
document.getElementById('nightlies').checked = false
})
},

tearDown: sauce
}