@ -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 . _dep s . compiler . setOptimize ( self . data . optimize )
self . _component s . 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 . _dep s . compiler . event . register ( 'compilationDuration' , function tabHighlighting ( speed ) {
self . _component s . 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 . _dep s . compiler . event . register ( 'loadingCompiler' , function start ( ) {
self . _component s . 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 . _dep s . compiler . event . register ( 'compilationStarted' , function start ( ) {
self . _component s . 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 . _dep s . compiler . event . register ( 'compilerLoaded' , function loaded ( ) {
self . _component s . compiler . event . register ( 'compilerLoaded' , function loaded ( ) {
if ( ! self . _view . compileIcon ) return
self . _view . compileIcon . classList . remove ( ` ${ css . spinningIcon } ` )
self . _view . compileIcon . setAttribute ( 'title' , '' )
} )
self . _dep s . compiler . event . register ( 'compilationFinished' , function finish ( success , data , source ) {
self . _component s . 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 . _dep s . compiler . getSource ( contract . file ) )
self . _component s . compiler . visitContracts ( contract => {
self . data . contractsDetails [ contract . name ] = parseContracts ( contract . name , contract . object , self . _component s . compiler . getSource ( contract . file ) )
var contractName = yo ` <option> ${ contract . name } </option> `
self . _view . contractNames . appendChild ( contractName )
} )
@ -160,11 +170,20 @@ module.exports = class CompileTab {
} )
}
if ( ! error && data . contracts ) {
self . _dep s . compiler . visitContracts ( ( contract ) => {
self . _component s . 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 . _dep s . compiler . setOptimize ( self . data . optimize )
self . _deps . app . runCompiler ( )
self . _component s . compiler . setOptimize ( self . data . optimize )
self . runCompiler ( )
}
self . _dep s . compiler . event . register ( 'compilerLoaded' , ( version ) => self . setVersionText ( version ) )
self . _component s . 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 {
< select onchange = $ { onchangeLoadVersion } class = "${css.select}" id = "versionSelector" disabled >
< option disabled selected > Select new compiler version < / o p t i o n >
< / s e l e c t > `
if ( self . data . allversions && self . data . selectedVersion ) self . _updateVersionSelector ( )
self . _view . version = yo ` <span id="version"></span> `
self . _view . warnCompilationSlow = yo ` <i title="Compilation Slow" style="visibility:hidden" class=" ${ css . warnCompilationSlow } fa fa-exclamation-triangle" aria-hidden="true"></i> `
@ -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 . _dep s . compiler . loadVersion ( true , url )
self . _component s . compiler . loadVersion ( true , url )
self . setVersionText ( '(loading using worker)' )
} else {
self . _dep s . compiler . loadVersion ( false , url )
self . _component s . 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 ) {
t ooltip( '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.' )
addT ooltip( '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 `