types introduced suggested changes and worker types more types more types and code style update fix to run compilation very first time after plugin activationpull/7/head
parent
1f74b65149
commit
355a02ea3b
@ -1,7 +0,0 @@ |
|||||||
const Compiler = require('./src/compiler/compiler') |
|
||||||
const CompilerInput = require('./src/compiler/compiler-input') |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
Compiler: Compiler, |
|
||||||
CompilerInput: CompilerInput |
|
||||||
} |
|
@ -0,0 +1,2 @@ |
|||||||
|
export { Compiler } from './src/compiler/compiler' |
||||||
|
export { default as CompilerInput} from './src/compiler/compiler-input' |
File diff suppressed because it is too large
Load Diff
@ -1,390 +0,0 @@ |
|||||||
'use strict' |
|
||||||
|
|
||||||
const solc = require('solc/wrapper') |
|
||||||
const solcABI = require('solc/abi') |
|
||||||
|
|
||||||
const webworkify = require('webworkify') |
|
||||||
|
|
||||||
const compilerInput = require('./compiler-input') |
|
||||||
|
|
||||||
const remixLib = require('remix-lib') |
|
||||||
const EventManager = remixLib.EventManager |
|
||||||
|
|
||||||
const txHelper = require('./txHelper') |
|
||||||
|
|
||||||
/* |
|
||||||
trigger compilationFinished, compilerLoaded, compilationStarted, compilationDuration |
|
||||||
*/ |
|
||||||
function Compiler (handleImportCall) { |
|
||||||
var self = this |
|
||||||
this.event = new EventManager() |
|
||||||
|
|
||||||
let compileJSON |
|
||||||
|
|
||||||
let worker = null |
|
||||||
|
|
||||||
let currentVersion |
|
||||||
|
|
||||||
let optimize = false |
|
||||||
|
|
||||||
let evmVersion = null |
|
||||||
|
|
||||||
let language = 'Solidity' |
|
||||||
|
|
||||||
this.setOptimize = function (_optimize) { |
|
||||||
optimize = _optimize |
|
||||||
} |
|
||||||
|
|
||||||
this.setEvmVersion = function (_evmVersion) { |
|
||||||
evmVersion = _evmVersion |
|
||||||
} |
|
||||||
|
|
||||||
this.setLanguage = function (_language) { |
|
||||||
language = _language |
|
||||||
} |
|
||||||
|
|
||||||
let compilationStartTime = null |
|
||||||
this.event.register('compilationFinished', (success, data, source) => { |
|
||||||
if (success && compilationStartTime) { |
|
||||||
this.event.trigger('compilationDuration', [(new Date().getTime()) - compilationStartTime]) |
|
||||||
} |
|
||||||
compilationStartTime = null |
|
||||||
}) |
|
||||||
|
|
||||||
this.event.register('compilationStarted', () => { |
|
||||||
compilationStartTime = new Date().getTime() |
|
||||||
}) |
|
||||||
|
|
||||||
const internalCompile = (files, target, missingInputs) => { |
|
||||||
gatherImports(files, target, missingInputs, (error, input) => { |
|
||||||
if (error) { |
|
||||||
this.lastCompilationResult = null |
|
||||||
this.event.trigger('compilationFinished', [false, {'error': { formattedMessage: error, severity: 'error' }}, files]) |
|
||||||
} else { |
|
||||||
compileJSON(input) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
const compile = function (files, target) { |
|
||||||
self.event.trigger('compilationStarted', []) |
|
||||||
internalCompile(files, target) |
|
||||||
} |
|
||||||
this.compile = compile |
|
||||||
|
|
||||||
function setCompileJSON (_compileJSON) { |
|
||||||
compileJSON = _compileJSON |
|
||||||
} |
|
||||||
this.setCompileJSON = setCompileJSON // this is exposed for testing
|
|
||||||
|
|
||||||
function onCompilerLoaded (version) { |
|
||||||
currentVersion = version |
|
||||||
self.event.trigger('compilerLoaded', [version]) |
|
||||||
} |
|
||||||
|
|
||||||
function onInternalCompilerLoaded () { |
|
||||||
if (worker === null) { |
|
||||||
let compiler |
|
||||||
if (typeof (window) === 'undefined') { |
|
||||||
compiler = require('solc') |
|
||||||
} else { |
|
||||||
compiler = solc(window.Module) |
|
||||||
} |
|
||||||
|
|
||||||
compileJSON = function (source) { |
|
||||||
const missingInputs = [] |
|
||||||
const missingInputsCallback = function (path) { |
|
||||||
missingInputs.push(path) |
|
||||||
return { error: 'Deferred import' } |
|
||||||
} |
|
||||||
|
|
||||||
let result |
|
||||||
try { |
|
||||||
const input = compilerInput(source.sources, {optimize: optimize, evmVersion: evmVersion, language: language, target: source.target}) |
|
||||||
result = compiler.compile(input, { import: missingInputsCallback }) |
|
||||||
result = JSON.parse(result) |
|
||||||
} catch (exception) { |
|
||||||
result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } } |
|
||||||
} |
|
||||||
|
|
||||||
compilationFinished(result, missingInputs, source) |
|
||||||
} |
|
||||||
onCompilerLoaded(compiler.version()) |
|
||||||
} |
|
||||||
} |
|
||||||
// exposed for use in node
|
|
||||||
this.onInternalCompilerLoaded = onInternalCompilerLoaded |
|
||||||
|
|
||||||
this.lastCompilationResult = { |
|
||||||
data: null, |
|
||||||
source: null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* return the contract obj of the given @arg name. Uses last compilation result. |
|
||||||
* return null if not found |
|
||||||
* @param {String} name - contract name |
|
||||||
* @returns contract obj and associated file: { contract, file } or null |
|
||||||
*/ |
|
||||||
this.getContract = (name) => { |
|
||||||
if (this.lastCompilationResult.data && this.lastCompilationResult.data.contracts) { |
|
||||||
return txHelper.getContract(name, this.lastCompilationResult.data.contracts) |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* call the given @arg cb (function) for all the contracts. Uses last compilation result |
|
||||||
* @param {Function} cb - callback |
|
||||||
*/ |
|
||||||
this.visitContracts = (cb) => { |
|
||||||
if (this.lastCompilationResult.data && this.lastCompilationResult.data.contracts) { |
|
||||||
return txHelper.visitContracts(this.lastCompilationResult.data.contracts, cb) |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* return the compiled contracts from the last compilation result |
|
||||||
* @return {Object} - contracts |
|
||||||
*/ |
|
||||||
this.getContracts = () => { |
|
||||||
if (this.lastCompilationResult.data && this.lastCompilationResult.data.contracts) { |
|
||||||
return this.lastCompilationResult.data.contracts |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* return the sources from the last compilation result |
|
||||||
* @param {Object} cb - map of sources |
|
||||||
*/ |
|
||||||
this.getSources = () => { |
|
||||||
if (this.lastCompilationResult.source) { |
|
||||||
return this.lastCompilationResult.source.sources |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* return the sources @arg fileName from the last compilation result |
|
||||||
* @param {Object} cb - map of sources |
|
||||||
*/ |
|
||||||
this.getSource = (fileName) => { |
|
||||||
if (this.lastCompilationResult.source) { |
|
||||||
return this.lastCompilationResult.source.sources[fileName] |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* return the source from the last compilation result that has the given index. null if source not found |
|
||||||
* @param {Int} index - index of the source |
|
||||||
*/ |
|
||||||
this.getSourceName = (index) => { |
|
||||||
if (this.lastCompilationResult.data && this.lastCompilationResult.data.sources) { |
|
||||||
return Object.keys(this.lastCompilationResult.data.sources)[index] |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
function compilationFinished (data, missingInputs, source) { |
|
||||||
let noFatalErrors = true // ie warnings are ok
|
|
||||||
|
|
||||||
function isValidError (error) { |
|
||||||
// The deferred import is not a real error
|
|
||||||
// FIXME: maybe have a better check?
|
|
||||||
if (/Deferred import/.exec(error.message)) { |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
return error.severity !== 'warning' |
|
||||||
} |
|
||||||
|
|
||||||
if (data['error'] !== undefined) { |
|
||||||
// Ignore warnings (and the 'Deferred import' error as those are generated by us as a workaround
|
|
||||||
if (isValidError(data['error'])) { |
|
||||||
noFatalErrors = false |
|
||||||
} |
|
||||||
} |
|
||||||
if (data['errors'] !== undefined) { |
|
||||||
data['errors'].forEach(function (err) { |
|
||||||
// Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround
|
|
||||||
if (isValidError(err)) { |
|
||||||
noFatalErrors = false |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
if (!noFatalErrors) { |
|
||||||
// There are fatal errors - abort here
|
|
||||||
self.lastCompilationResult = null |
|
||||||
self.event.trigger('compilationFinished', [false, data, source]) |
|
||||||
} else if (missingInputs !== undefined && missingInputs.length > 0) { |
|
||||||
// try compiling again with the new set of inputs
|
|
||||||
|
|
||||||
internalCompile(source.sources, source.target, missingInputs) |
|
||||||
} else { |
|
||||||
data = updateInterface(data) |
|
||||||
|
|
||||||
self.lastCompilationResult = { |
|
||||||
data: data, |
|
||||||
source: source |
|
||||||
} |
|
||||||
self.event.trigger('compilationFinished', [true, data, source]) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: needs to be changed to be more node friendly
|
|
||||||
this.loadVersion = (usingWorker, url) => { |
|
||||||
console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')) |
|
||||||
this.event.trigger('loadingCompiler', [url, usingWorker]) |
|
||||||
|
|
||||||
if (worker !== null) { |
|
||||||
worker.terminate() |
|
||||||
worker = null |
|
||||||
} |
|
||||||
if (usingWorker) { |
|
||||||
loadWorker(url) |
|
||||||
} else { |
|
||||||
loadInternal(url) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function loadInternal (url) { |
|
||||||
delete window.Module |
|
||||||
// NOTE: workaround some browsers?
|
|
||||||
window.Module = undefined |
|
||||||
|
|
||||||
// Set a safe fallback until the new one is loaded
|
|
||||||
setCompileJSON(function (source) { |
|
||||||
compilationFinished({ error: { formattedMessage: 'Compiler not yet loaded.' } }) |
|
||||||
}) |
|
||||||
|
|
||||||
const newScript = document.createElement('script') |
|
||||||
newScript.type = 'text/javascript' |
|
||||||
newScript.src = url |
|
||||||
document.getElementsByTagName('head')[0].appendChild(newScript) |
|
||||||
const check = window.setInterval(function () { |
|
||||||
if (!window.Module) { |
|
||||||
return |
|
||||||
} |
|
||||||
window.clearInterval(check) |
|
||||||
onInternalCompilerLoaded() |
|
||||||
}, 200) |
|
||||||
} |
|
||||||
|
|
||||||
function loadWorker (url) { |
|
||||||
worker = webworkify(require('./compiler-worker.js')) |
|
||||||
const jobs = [] |
|
||||||
worker.addEventListener('message', function (msg) { |
|
||||||
const data = msg.data |
|
||||||
switch (data.cmd) { |
|
||||||
case 'versionLoaded': |
|
||||||
onCompilerLoaded(data.data) |
|
||||||
break |
|
||||||
case 'compiled': |
|
||||||
let result |
|
||||||
try { |
|
||||||
result = JSON.parse(data.data) |
|
||||||
} catch (exception) { |
|
||||||
result = { 'error': 'Invalid JSON output from the compiler: ' + exception } |
|
||||||
} |
|
||||||
let sources = {} |
|
||||||
if (data.job in jobs !== undefined) { |
|
||||||
sources = jobs[data.job].sources |
|
||||||
delete jobs[data.job] |
|
||||||
} |
|
||||||
compilationFinished(result, data.missingInputs, sources) |
|
||||||
break |
|
||||||
} |
|
||||||
}) |
|
||||||
worker.addEventListener('error', function (msg) { |
|
||||||
compilationFinished({ error: 'Worker error: ' + msg.data }) |
|
||||||
}) |
|
||||||
compileJSON = function (source) { |
|
||||||
jobs.push({sources: source}) |
|
||||||
worker.postMessage({cmd: 'compile', job: jobs.length - 1, input: compilerInput(source.sources, |
|
||||||
{optimize: optimize, evmVersion: evmVersion, language: language, target: source.target})}) |
|
||||||
} |
|
||||||
worker.postMessage({cmd: 'loadVersion', data: url}) |
|
||||||
} |
|
||||||
|
|
||||||
function gatherImports (files, target, importHints, cb) { |
|
||||||
importHints = importHints || [] |
|
||||||
|
|
||||||
// FIXME: This will only match imports if the file begins with one.
|
|
||||||
// It should tokenize by lines and check each.
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
let importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g |
|
||||||
|
|
||||||
for (var fileName in files) { |
|
||||||
let match |
|
||||||
while ((match = importRegex.exec(files[fileName].content))) { |
|
||||||
let importFilePath = match[1] |
|
||||||
if (importFilePath.startsWith('./')) { |
|
||||||
const path = /(.*\/).*/.exec(fileName) |
|
||||||
if (path !== null) { |
|
||||||
importFilePath = importFilePath.replace('./', path[1]) |
|
||||||
} else { |
|
||||||
importFilePath = importFilePath.slice(2) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// FIXME: should be using includes or sets, but there's also browser compatibility..
|
|
||||||
if (importHints.indexOf(importFilePath) === -1) { |
|
||||||
importHints.push(importFilePath) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
while (importHints.length > 0) { |
|
||||||
let m = importHints.pop() |
|
||||||
if (m in files) { |
|
||||||
continue |
|
||||||
} |
|
||||||
|
|
||||||
if (handleImportCall) { |
|
||||||
handleImportCall(m, function (err, content) { |
|
||||||
if (err) { |
|
||||||
cb(err) |
|
||||||
} else { |
|
||||||
files[m] = { content } |
|
||||||
gatherImports(files, target, importHints, cb) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
cb(null, { 'sources': files, 'target': target }) |
|
||||||
} |
|
||||||
|
|
||||||
function truncateVersion (version) { |
|
||||||
const tmp = /^(\d+.\d+.\d+)/.exec(version) |
|
||||||
if (tmp) { |
|
||||||
return tmp[1] |
|
||||||
} |
|
||||||
return version |
|
||||||
} |
|
||||||
|
|
||||||
function updateInterface (data) { |
|
||||||
txHelper.visitContracts(data.contracts, (contract) => { |
|
||||||
if (!contract.object.abi) contract.object.abi = [] |
|
||||||
if (language === 'Yul' && contract.object.abi.length === 0) { |
|
||||||
// yul compiler does not return any abi,
|
|
||||||
// we default to accept the fallback function (which expect raw data as argument).
|
|
||||||
contract.object.abi.push({ |
|
||||||
'payable': true, |
|
||||||
'stateMutability': 'payable', |
|
||||||
'type': 'fallback' |
|
||||||
}) |
|
||||||
} |
|
||||||
data.contracts[contract.file][contract.name].abi = solcABI.update(truncateVersion(currentVersion), contract.object.abi) |
|
||||||
}) |
|
||||||
return data |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = Compiler |
|
@ -0,0 +1,411 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
import { update } from 'solc/abi' |
||||||
|
import webworkify from 'webworkify' |
||||||
|
import compilerInput from './compiler-input' |
||||||
|
import { EventManager } from 'remix-lib' |
||||||
|
import { default as txHelper } from './txHelper'; |
||||||
|
import { Source, SourceWithTarget, MessageFromWorker, CompilerState, CompilationResult,
|
||||||
|
visitContractsCallbackParam, visitContractsCallbackInterface, CompilationError,
|
||||||
|
gatherImportsCallbackInterface } from './types' |
||||||
|
|
||||||
|
/* |
||||||
|
trigger compilationFinished, compilerLoaded, compilationStarted, compilationDuration |
||||||
|
*/ |
||||||
|
export class Compiler { |
||||||
|
event: EventManager |
||||||
|
state: CompilerState |
||||||
|
|
||||||
|
constructor (public handleImportCall: (fileurl: string, cb: Function) => void) { |
||||||
|
this.event = new EventManager() |
||||||
|
this.state = { |
||||||
|
compileJSON: null, |
||||||
|
worker: null, |
||||||
|
currentVersion: null, |
||||||
|
optimize: false, |
||||||
|
evmVersion: null, |
||||||
|
language: 'Solidity', |
||||||
|
compilationStartTime: null, |
||||||
|
lastCompilationResult: { |
||||||
|
data: null, |
||||||
|
source: null |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.event.register('compilationFinished', (success: boolean, data: CompilationResult, source: SourceWithTarget) => { |
||||||
|
if (success && this.state.compilationStartTime) { |
||||||
|
this.event.trigger('compilationDuration', [(new Date().getTime()) - this.state.compilationStartTime]) |
||||||
|
} |
||||||
|
this.state.compilationStartTime = null |
||||||
|
}) |
||||||
|
|
||||||
|
this.event.register('compilationStarted', () => { |
||||||
|
this.state.compilationStartTime = new Date().getTime() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Setter function for CompilerState's properties (used by IDE) |
||||||
|
* @param key key |
||||||
|
* @param value value of key in CompilerState |
||||||
|
*/ |
||||||
|
|
||||||
|
set <K extends keyof CompilerState>(key: K, value: CompilerState[K]) { |
||||||
|
this.state[key] = value |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Internal function to compile the contract after gathering imports |
||||||
|
* @param files source file |
||||||
|
* @param missingInputs missing import file path list |
||||||
|
*/ |
||||||
|
|
||||||
|
internalCompile (files: Source, missingInputs?: string[]): void { |
||||||
|
this.gatherImports(files, missingInputs, (error, input) => { |
||||||
|
if (error) { |
||||||
|
this.state.lastCompilationResult = null |
||||||
|
this.event.trigger('compilationFinished', [false, {'error': { formattedMessage: error, severity: 'error' }}, files]) |
||||||
|
} else if(this.state.compileJSON && input) |
||||||
|
this.state.compileJSON(input) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Compile source files (used by IDE) |
||||||
|
* @param files source files |
||||||
|
* @param target target file name (This is passed as it is to IDE) |
||||||
|
*/ |
||||||
|
|
||||||
|
compile (files: Source, target: string): void { |
||||||
|
this.state.target = target |
||||||
|
this.event.trigger('compilationStarted', []) |
||||||
|
this.internalCompile(files, target) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Called when compiler is loaded, set current compiler version |
||||||
|
* @param version compiler version |
||||||
|
*/ |
||||||
|
|
||||||
|
onCompilerLoaded (version: string): void { |
||||||
|
this.state.currentVersion = version |
||||||
|
this.event.trigger('compilerLoaded', [version]) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Called when compiler is loaded internally (without worker) |
||||||
|
*/ |
||||||
|
|
||||||
|
onInternalCompilerLoaded (): void { |
||||||
|
if (this.state.worker === null) { |
||||||
|
const compiler: any = typeof (window) === 'undefined' ? require('solc') : require('solc/wrapper')(window['Module']) |
||||||
|
this.state.compileJSON = (source: SourceWithTarget) => { |
||||||
|
let missingInputs: string[] = [] |
||||||
|
const missingInputsCallback = (path: string) => { |
||||||
|
missingInputs.push(path) |
||||||
|
return { error: 'Deferred import' } |
||||||
|
} |
||||||
|
let result: CompilationResult = {} |
||||||
|
try { |
||||||
|
if(source && source.sources) { |
||||||
|
const input = compilerInput(source.sources, {optimize: this.state.optimize, evmVersion: this.state.evmVersion, language: this.state.language}) |
||||||
|
result = JSON.parse(compiler.compile(input, { import: missingInputsCallback })) |
||||||
|
} |
||||||
|
} catch (exception) { |
||||||
|
result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } } |
||||||
|
} |
||||||
|
this.onCompilationFinished(result, missingInputs, source) |
||||||
|
} |
||||||
|
this.onCompilerLoaded(compiler.version()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Called when compilation is finished |
||||||
|
* @param data compilation result data |
||||||
|
* @param missingInputs missing imports |
||||||
|
* @param source Source |
||||||
|
*/ |
||||||
|
|
||||||
|
onCompilationFinished (data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget): void { |
||||||
|
let noFatalErrors: boolean = true // ie warnings are ok
|
||||||
|
|
||||||
|
const checkIfFatalError = (error: CompilationError) => { |
||||||
|
// Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround
|
||||||
|
const isValidError = (error.message && error.message === 'Deferred import') ? false : error.severity !== 'warning' |
||||||
|
if(isValidError) noFatalErrors = false |
||||||
|
} |
||||||
|
if (data.error) checkIfFatalError(data.error) |
||||||
|
if (data.errors) data.errors.forEach((err) => checkIfFatalError(err)) |
||||||
|
if (!noFatalErrors) { |
||||||
|
// There are fatal errors, abort here
|
||||||
|
this.state.lastCompilationResult = null |
||||||
|
this.event.trigger('compilationFinished', [false, data, source]) |
||||||
|
} else if (missingInputs !== undefined && missingInputs.length > 0) { |
||||||
|
// try compiling again with the new set of inputs
|
||||||
|
this.internalCompile(source.sources, missingInputs) |
||||||
|
} else { |
||||||
|
data = this.updateInterface(data) |
||||||
|
if(source) |
||||||
|
{ |
||||||
|
source.target = this.state.target; |
||||||
|
this.state.lastCompilationResult = { |
||||||
|
data: data, |
||||||
|
source: source |
||||||
|
} |
||||||
|
} |
||||||
|
this.event.trigger('compilationFinished', [true, data, source]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Load compiler using given URL (used by IDE) |
||||||
|
* @param usingWorker if true, load compiler using worker |
||||||
|
* @param url URL to load compiler from |
||||||
|
*/ |
||||||
|
|
||||||
|
loadVersion (usingWorker: boolean, url: string): void { |
||||||
|
console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')) |
||||||
|
this.event.trigger('loadingCompiler', [url, usingWorker]) |
||||||
|
if (this.state.worker) { |
||||||
|
this.state.worker.terminate() |
||||||
|
this.state.worker = null |
||||||
|
} |
||||||
|
if (usingWorker) { |
||||||
|
this.loadWorker(url) |
||||||
|
} else { |
||||||
|
this.loadInternal(url) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Load compiler using 'script' element (without worker) |
||||||
|
* @param url URL to load compiler from |
||||||
|
*/ |
||||||
|
|
||||||
|
loadInternal (url: string): void { |
||||||
|
delete window['Module'] |
||||||
|
// NOTE: workaround some browsers?
|
||||||
|
window['Module'] = undefined |
||||||
|
// Set a safe fallback until the new one is loaded
|
||||||
|
this.state.compileJSON = (source: SourceWithTarget) => { |
||||||
|
this.onCompilationFinished({ error: { formattedMessage: 'Compiler not yet loaded.' } }) |
||||||
|
} |
||||||
|
let newScript: HTMLScriptElement = document.createElement('script') |
||||||
|
newScript.type = 'text/javascript' |
||||||
|
newScript.src = url |
||||||
|
document.getElementsByTagName('head')[0].appendChild(newScript) |
||||||
|
const check: number = window.setInterval(() => { |
||||||
|
if (!window['Module']) { |
||||||
|
return |
||||||
|
} |
||||||
|
window.clearInterval(check) |
||||||
|
this.onInternalCompilerLoaded() |
||||||
|
}, 200) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Load compiler using web worker |
||||||
|
* @param url URL to load compiler from |
||||||
|
*/ |
||||||
|
|
||||||
|
loadWorker (url: string): void { |
||||||
|
this.state.worker = webworkify(require('./compiler-worker.js').default) |
||||||
|
let jobs: Record<'sources', SourceWithTarget> [] = [] |
||||||
|
|
||||||
|
this.state.worker.addEventListener('message', (msg: Record <'data', MessageFromWorker>) => { |
||||||
|
const data: MessageFromWorker = msg.data |
||||||
|
switch (data.cmd) { |
||||||
|
case 'versionLoaded': |
||||||
|
if(data.data) this.onCompilerLoaded(data.data) |
||||||
|
break |
||||||
|
case 'compiled': |
||||||
|
let result: CompilationResult |
||||||
|
if(data.data && data.job !== undefined && data.job >= 0) { |
||||||
|
try { |
||||||
|
result = JSON.parse(data.data) |
||||||
|
} catch (exception) { |
||||||
|
result = { error : { formattedMessage: 'Invalid JSON output from the compiler: ' + exception }} |
||||||
|
} |
||||||
|
let sources: SourceWithTarget = {} |
||||||
|
if (data.job in jobs !== undefined) { |
||||||
|
sources = jobs[data.job].sources |
||||||
|
delete jobs[data.job] |
||||||
|
} |
||||||
|
this.onCompilationFinished(result, data.missingInputs, sources) |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
this.state.worker.addEventListener('error', (msg: Record <'data', MessageFromWorker>) => { |
||||||
|
this.onCompilationFinished({ error: { formattedMessage: 'Worker error: ' + msg.data }}) |
||||||
|
}) |
||||||
|
|
||||||
|
this.state.compileJSON = (source: SourceWithTarget) => { |
||||||
|
if(source && source.sources) { |
||||||
|
jobs.push({sources: source}) |
||||||
|
this.state.worker.postMessage({ |
||||||
|
cmd: 'compile',
|
||||||
|
job: jobs.length - 1,
|
||||||
|
input: compilerInput(source.sources, { |
||||||
|
optimize: this.state.optimize,
|
||||||
|
evmVersion: this.state.evmVersion,
|
||||||
|
language: this.state.language |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.state.worker.postMessage({ |
||||||
|
cmd: 'loadVersion',
|
||||||
|
data: url |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Gather imports for compilation |
||||||
|
* @param files file sources |
||||||
|
* @param importHints import file list |
||||||
|
* @param cb callback |
||||||
|
*/ |
||||||
|
|
||||||
|
gatherImports (files: Source, importHints?: string[], cb?: gatherImportsCallbackInterface): void { |
||||||
|
importHints = importHints || [] |
||||||
|
// FIXME: This will only match imports if the file begins with one '.'
|
||||||
|
// It should tokenize by lines and check each.
|
||||||
|
const importRegex: RegExp = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g |
||||||
|
for (const fileName in files) { |
||||||
|
let match: RegExpExecArray | null |
||||||
|
while ((match = importRegex.exec(files[fileName].content))) { |
||||||
|
let importFilePath = match[1] |
||||||
|
if (importFilePath.startsWith('./')) { |
||||||
|
const path: RegExpExecArray | null = /(.*\/).*/.exec(fileName) |
||||||
|
importFilePath = path ? importFilePath.replace('./', path[1]) : importFilePath.slice(2) |
||||||
|
} |
||||||
|
if (!importHints.includes(importFilePath)) importHints.push(importFilePath) |
||||||
|
} |
||||||
|
} |
||||||
|
while (importHints.length > 0) { |
||||||
|
const m: string = importHints.pop() as string |
||||||
|
if (m && m in files) continue |
||||||
|
|
||||||
|
if (this.handleImportCall) { |
||||||
|
this.handleImportCall(m, (err, content: string) => { |
||||||
|
if (err && cb) cb(err) |
||||||
|
else { |
||||||
|
files[m] = { content } |
||||||
|
this.gatherImports(files, importHints, cb) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
if(cb) |
||||||
|
cb(null, { 'sources': files }) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Truncate version string |
||||||
|
* @param version version |
||||||
|
*/ |
||||||
|
|
||||||
|
truncateVersion (version: string): string { |
||||||
|
const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version) |
||||||
|
return tmp ? tmp[1] : version |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Update ABI according to current compiler version |
||||||
|
* @param data Compilation result |
||||||
|
*/ |
||||||
|
|
||||||
|
updateInterface (data: CompilationResult) : CompilationResult { |
||||||
|
txHelper.visitContracts(data.contracts, (contract : visitContractsCallbackParam) => { |
||||||
|
if (!contract.object.abi) contract.object.abi = [] |
||||||
|
if (this.state.language === 'Yul' && contract.object.abi.length === 0) { |
||||||
|
// yul compiler does not return any abi,
|
||||||
|
// we default to accept the fallback function (which expect raw data as argument).
|
||||||
|
contract.object.abi.push({ |
||||||
|
'payable': true, |
||||||
|
'stateMutability': 'payable', |
||||||
|
'type': 'fallback' |
||||||
|
}) |
||||||
|
} |
||||||
|
if(data && data.contracts && this.state.currentVersion) |
||||||
|
data.contracts[contract.file][contract.name].abi = update(this.truncateVersion(this.state.currentVersion), contract.object.abi) |
||||||
|
}) |
||||||
|
return data |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Get contract obj of the given contract name from last compilation result. |
||||||
|
* @param name contract name |
||||||
|
*/ |
||||||
|
|
||||||
|
getContract (name: string): Record<string, any> | null { |
||||||
|
if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { |
||||||
|
return txHelper.getContract(name, this.state.lastCompilationResult.data.contracts) |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Call the given callback for all the contracts from last compilation result |
||||||
|
* @param cb callback |
||||||
|
*/ |
||||||
|
|
||||||
|
visitContracts (cb: visitContractsCallbackInterface) : void | null { |
||||||
|
if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { |
||||||
|
return txHelper.visitContracts(this.state.lastCompilationResult.data.contracts, cb) |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Get the compiled contracts data from last compilation result |
||||||
|
*/ |
||||||
|
|
||||||
|
getContracts () : CompilationResult['contracts'] | null { |
||||||
|
if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { |
||||||
|
return this.state.lastCompilationResult.data.contracts |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Get sources from last compilation result |
||||||
|
*/ |
||||||
|
|
||||||
|
getSources () : Source | null | undefined { |
||||||
|
if (this.state.lastCompilationResult && this.state.lastCompilationResult.source) { |
||||||
|
return this.state.lastCompilationResult.source.sources |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Get sources of passed file name from last compilation result |
||||||
|
* @param fileName file name |
||||||
|
*/ |
||||||
|
|
||||||
|
getSource (fileName: string) : Source['filename'] | null { |
||||||
|
if (this.state.lastCompilationResult && this.state.lastCompilationResult.source && this.state.lastCompilationResult.source.sources) { |
||||||
|
return this.state.lastCompilationResult.source.sources[fileName] |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Get source name at passed index from last compilation result |
||||||
|
* @param index - index of the source |
||||||
|
*/ |
||||||
|
|
||||||
|
getSourceName (index: number): string | null { |
||||||
|
if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.sources) { |
||||||
|
return Object.keys(this.state.lastCompilationResult.data.sources)[index] |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -1,33 +0,0 @@ |
|||||||
'use strict' |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
|
|
||||||
/** |
|
||||||
* return the contract obj of the given @arg name. Uses last compilation result. |
|
||||||
* return null if not found |
|
||||||
* @param {String} name - contract name |
|
||||||
* @returns contract obj and associated file: { contract, file } or null |
|
||||||
*/ |
|
||||||
getContract: (contractName, contracts) => { |
|
||||||
for (let file in contracts) { |
|
||||||
if (contracts[file][contractName]) { |
|
||||||
return { object: contracts[file][contractName], file: file } |
|
||||||
} |
|
||||||
} |
|
||||||
return null |
|
||||||
}, |
|
||||||
|
|
||||||
/** |
|
||||||
* call the given @arg cb (function) for all the contracts. Uses last compilation result |
|
||||||
* stop visiting when cb return true |
|
||||||
* @param {Function} cb - callback |
|
||||||
*/ |
|
||||||
visitContracts: (contracts, cb) => { |
|
||||||
for (let file in contracts) { |
|
||||||
for (let name in contracts[file]) { |
|
||||||
if (cb({ name: name, object: contracts[file][name], file: file })) return |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,40 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
import { CompilationResult, visitContractsCallbackParam, visitContractsCallbackInterface } from './types' |
||||||
|
export default { |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Get contract obj of given contract name from last compilation result. |
||||||
|
* @param name contract name |
||||||
|
* @param contracts 'contracts' object from last compilation result |
||||||
|
*/ |
||||||
|
|
||||||
|
getContract: (contractName: string, contracts: CompilationResult["contracts"]) : Record<string, any> | null => { |
||||||
|
for (const file in contracts) { |
||||||
|
if (contracts[file][contractName]) { |
||||||
|
return { object: contracts[file][contractName], file: file } |
||||||
|
} |
||||||
|
} |
||||||
|
return null |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev call the given callback for all contracts from last compilation result, stop visiting when cb return true |
||||||
|
* @param contracts - 'contracts' object from last compilation result |
||||||
|
* @param cb - callback |
||||||
|
*/ |
||||||
|
|
||||||
|
visitContracts: (contracts: CompilationResult["contracts"], cb: visitContractsCallbackInterface) : void => { |
||||||
|
for (const file in contracts) { |
||||||
|
for (const name in contracts[file]) { |
||||||
|
const param: visitContractsCallbackParam = {
|
||||||
|
name: name,
|
||||||
|
object: contracts[file][name],
|
||||||
|
file: file
|
||||||
|
} |
||||||
|
if (cb(param)) return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,481 @@ |
|||||||
|
export interface CompilerInput { |
||||||
|
// Required: Source code language. Currently supported are "Solidity" and "Yul".
|
||||||
|
language: Language, |
||||||
|
// Required
|
||||||
|
sources: Source, |
||||||
|
// Optional
|
||||||
|
settings: |
||||||
|
{ |
||||||
|
// Optional: Sorted list of remappings
|
||||||
|
remappings?: string[], |
||||||
|
// Optional: Optimizer settings
|
||||||
|
optimizer: { |
||||||
|
// disabled by default
|
||||||
|
enabled: boolean, |
||||||
|
// Optimize for how many times you intend to run the code.
|
||||||
|
// Lower values will optimize more for initial deployment cost, higher
|
||||||
|
// values will optimize more for high-frequency usage.
|
||||||
|
runs: number, |
||||||
|
// Switch optimizer components on or off in detail.
|
||||||
|
// The "enabled" switch above provides two defaults which can be
|
||||||
|
// tweaked here. If "details" is given, "enabled" can be omitted.
|
||||||
|
details?: { |
||||||
|
// The peephole optimizer is always on if no details are given,
|
||||||
|
// use details to switch it off.
|
||||||
|
peephole?: boolean, |
||||||
|
// The unused jumpdest remover is always on if no details are given,
|
||||||
|
// use details to switch it off.
|
||||||
|
jumpdestRemover?: boolean, |
||||||
|
// Sometimes re-orders literals in commutative operations.
|
||||||
|
orderLiterals?: boolean, |
||||||
|
// Removes duplicate code blocks
|
||||||
|
deduplicate?: boolean, |
||||||
|
// Common subexpression elimination, this is the most complicated step but
|
||||||
|
// can also provide the largest gain.
|
||||||
|
cse?: boolean, |
||||||
|
// Optimize representation of literal numbers and strings in code.
|
||||||
|
constantOptimizer?: boolean, |
||||||
|
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2.
|
||||||
|
// It can only be activated through the details here.
|
||||||
|
yul?: boolean, |
||||||
|
// Tuning options for the Yul optimizer.
|
||||||
|
yulDetails?: { |
||||||
|
// Improve allocation of stack slots for variables, can free up stack slots early.
|
||||||
|
// Activated by default if the Yul optimizer is activated.
|
||||||
|
stackAllocation: boolean |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
// Version of the EVM to compile for.
|
||||||
|
// Affects type checking and code generation.
|
||||||
|
evmVersion?: EVMVersion, |
||||||
|
// Optional: Debugging settings
|
||||||
|
debug?: { |
||||||
|
// How to treat revert (and require) reason strings. Settings are
|
||||||
|
// "default", "strip", "debug" and "verboseDebug".
|
||||||
|
// "default" does not inject compiler-generated revert strings and keeps user-supplied ones.
|
||||||
|
// "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects
|
||||||
|
// "debug" injects strings for compiler-generated internal reverts (not yet implemented)
|
||||||
|
// "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
|
||||||
|
revertStrings: 'default' | 'strip' | 'debug' | 'verboseDebug' |
||||||
|
} |
||||||
|
// Metadata settings (optional)
|
||||||
|
metadata?: { |
||||||
|
// Use only literal content and not URLs (false by default)
|
||||||
|
useLiteralContent: boolean, |
||||||
|
// Use the given hash method for the metadata hash that is appended to the bytecode.
|
||||||
|
// The metadata hash can be removed from the bytecode via option "none".
|
||||||
|
// The other options are "ipfs" and "bzzr1".
|
||||||
|
// If the option is omitted, "ipfs" is used by default.
|
||||||
|
bytecodeHash: 'ipfs' | 'bzzr1' | 'none' |
||||||
|
}, |
||||||
|
// Addresses of the libraries. If not all libraries are given here,
|
||||||
|
// it can result in unlinked objects whose output data is different.
|
||||||
|
libraries?: { |
||||||
|
// The top level key is the the name of the source file where the library is used.
|
||||||
|
// If remappings are used, this source file should match the global path
|
||||||
|
// after remappings were applied.
|
||||||
|
// If this key is an empty string, that refers to a global level.
|
||||||
|
[fileName: string]: Record<string, string> |
||||||
|
} |
||||||
|
// The following can be used to select desired outputs based
|
||||||
|
// on file and contract names.
|
||||||
|
// If this field is omitted, then the compiler loads and does type checking,
|
||||||
|
// but will not generate any outputs apart from errors.
|
||||||
|
// The first level key is the file name and the second level key is the contract name.
|
||||||
|
// An empty contract name is used for outputs that are not tied to a contract
|
||||||
|
// but to the whole source file like the AST.
|
||||||
|
// A star as contract name refers to all contracts in the file.
|
||||||
|
// Similarly, a star as a file name matches all files.
|
||||||
|
// To select all outputs the compiler can possibly generate, use
|
||||||
|
// "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }"
|
||||||
|
// but note that this might slow down the compilation process needlessly.
|
||||||
|
//
|
||||||
|
// The available output types are as follows:
|
||||||
|
//
|
||||||
|
// File level (needs empty string as contract name):
|
||||||
|
// ast - AST of all source files
|
||||||
|
// legacyAST - legacy AST of all source files
|
||||||
|
//
|
||||||
|
// Contract level (needs the contract name or "*"):
|
||||||
|
// abi - ABI
|
||||||
|
// devdoc - Developer documentation (natspec)
|
||||||
|
// userdoc - User documentation (natspec)
|
||||||
|
// metadata - Metadata
|
||||||
|
// ir - Yul intermediate representation of the code before optimization
|
||||||
|
// irOptimized - Intermediate representation after optimization
|
||||||
|
// storageLayout - Slots, offsets and types of the contract's state variables.
|
||||||
|
// evm.assembly - New assembly format
|
||||||
|
// evm.legacyAssembly - Old-style assembly format in JSON
|
||||||
|
// evm.bytecode.object - Bytecode object
|
||||||
|
// evm.bytecode.opcodes - Opcodes list
|
||||||
|
// evm.bytecode.sourceMap - Source mapping (useful for debugging)
|
||||||
|
// evm.bytecode.linkReferences - Link references (if unlinked object)
|
||||||
|
// evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode)
|
||||||
|
// evm.methodIdentifiers - The list of function hashes
|
||||||
|
// evm.gasEstimates - Function gas estimates
|
||||||
|
// ewasm.wast - eWASM S-expressions format (not supported at the moment)
|
||||||
|
// ewasm.wasm - eWASM binary format (not supported at the moment)
|
||||||
|
//
|
||||||
|
// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every
|
||||||
|
// target part of that output. Additionally, `*` can be used as a wildcard to request everything.
|
||||||
|
//
|
||||||
|
outputSelection?: { |
||||||
|
'*': { |
||||||
|
'': [ 'legacyAST', 'ast' ], |
||||||
|
'*': [ 'abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export interface Source { |
||||||
|
[fileName: string]: |
||||||
|
{ |
||||||
|
// Optional: keccak256 hash of the source file
|
||||||
|
keccak256?: string, |
||||||
|
// Required (unless "urls" is used): literal contents of the source file
|
||||||
|
content: string, |
||||||
|
urls?: string[] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export interface CompilerInputOptions { |
||||||
|
optimize: boolean | number, |
||||||
|
libraries?: { |
||||||
|
[fileName: string]: Record<string, string> |
||||||
|
}, |
||||||
|
evmVersion?: EVMVersion, |
||||||
|
language?: Language |
||||||
|
} |
||||||
|
|
||||||
|
export type EVMVersion = 'homestead' | 'tangerineWhistle' | 'spuriousDragon' | 'byzantium' | 'constantinople' | 'petersburg' | 'istanbul' | 'berlin' | null |
||||||
|
|
||||||
|
export type Language = 'Solidity' | 'Yul' |
||||||
|
|
||||||
|
export interface CompilerState { |
||||||
|
compileJSON: ((input: SourceWithTarget) => void) | null, |
||||||
|
worker: any, |
||||||
|
currentVersion: string| null| undefined, |
||||||
|
optimize: boolean, |
||||||
|
evmVersion: EVMVersion| null, |
||||||
|
language: Language, |
||||||
|
compilationStartTime: number| null, |
||||||
|
target: string | null, |
||||||
|
lastCompilationResult: { |
||||||
|
data: CompilationResult | null, |
||||||
|
source: SourceWithTarget | null | undefined |
||||||
|
} | null |
||||||
|
} |
||||||
|
|
||||||
|
export interface SourceWithTarget { |
||||||
|
sources?: Source, |
||||||
|
target?: string | null | undefined |
||||||
|
} |
||||||
|
|
||||||
|
export interface MessageToWorker { |
||||||
|
cmd: string, |
||||||
|
job?: number, |
||||||
|
input?: CompilerInput, |
||||||
|
data?: string |
||||||
|
} |
||||||
|
|
||||||
|
export interface MessageFromWorker { |
||||||
|
cmd: string, |
||||||
|
job?: number, |
||||||
|
missingInputs?: string[], |
||||||
|
data?: string |
||||||
|
} |
||||||
|
|
||||||
|
export interface visitContractsCallbackParam { |
||||||
|
name: string,
|
||||||
|
object: CompiledContract, |
||||||
|
file: string |
||||||
|
} |
||||||
|
|
||||||
|
export interface visitContractsCallbackInterface { |
||||||
|
(param: visitContractsCallbackParam): boolean | void |
||||||
|
} |
||||||
|
|
||||||
|
export interface gatherImportsCallbackInterface { |
||||||
|
(err?: Error | null, result?: SourceWithTarget) : any |
||||||
|
} |
||||||
|
|
||||||
|
export interface CompilationResult { |
||||||
|
error?: CompilationError, |
||||||
|
/** not present if no errors/warnings were encountered */ |
||||||
|
errors?: CompilationError[] |
||||||
|
/** This contains the file-level outputs. In can be limited/filtered by the outputSelection settings */ |
||||||
|
sources?: { |
||||||
|
[contractName: string]: CompilationSource |
||||||
|
} |
||||||
|
/** This contains the contract-level outputs. It can be limited/filtered by the outputSelection settings */ |
||||||
|
contracts?: { |
||||||
|
/** If the language used has no contract names, this field should equal to an empty string. */ |
||||||
|
[fileName: string]: { |
||||||
|
[contract: string]: CompiledContract |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
///////////
|
||||||
|
// ERROR //
|
||||||
|
///////////
|
||||||
|
|
||||||
|
export interface CompilationError { |
||||||
|
/** Location within the source file */ |
||||||
|
sourceLocation?: { |
||||||
|
file: string |
||||||
|
start: number |
||||||
|
end: number |
||||||
|
} |
||||||
|
/** Error type */ |
||||||
|
type?: CompilationErrorType |
||||||
|
/** Component where the error originated, such as "general", "ewasm", etc. */ |
||||||
|
component?: 'general' | 'ewasm' | string |
||||||
|
severity?: 'error' | 'warning' |
||||||
|
message?: string |
||||||
|
mode?: 'panic' |
||||||
|
/** the message formatted with source location */ |
||||||
|
formattedMessage?: string |
||||||
|
} |
||||||
|
|
||||||
|
type CompilationErrorType = |
||||||
|
| 'JSONError' |
||||||
|
| 'IOError' |
||||||
|
| 'ParserError' |
||||||
|
| 'DocstringParsingError' |
||||||
|
| 'SyntaxError' |
||||||
|
| 'DeclarationError' |
||||||
|
| 'TypeError' |
||||||
|
| 'UnimplementedFeatureError' |
||||||
|
| 'InternalCompilerError' |
||||||
|
| 'Exception' |
||||||
|
| 'CompilerError' |
||||||
|
| 'FatalError' |
||||||
|
| 'Warning' |
||||||
|
|
||||||
|
////////////
|
||||||
|
// SOURCE //
|
||||||
|
////////////
|
||||||
|
export interface CompilationSource { |
||||||
|
/** Identifier of the source (used in source maps) */ |
||||||
|
id: number |
||||||
|
/** The AST object */ |
||||||
|
ast: AstNode |
||||||
|
/** The legacy AST object */ |
||||||
|
legacyAST: AstNodeLegacy |
||||||
|
} |
||||||
|
|
||||||
|
/////////
|
||||||
|
// AST //
|
||||||
|
/////////
|
||||||
|
export interface AstNode { |
||||||
|
absolutePath?: string |
||||||
|
exportedSymbols?: Object |
||||||
|
id: number |
||||||
|
nodeType: string |
||||||
|
nodes?: Array<AstNode> |
||||||
|
src: string |
||||||
|
literals?: Array<string> |
||||||
|
file?: string |
||||||
|
scope?: number |
||||||
|
sourceUnit?: number |
||||||
|
symbolAliases?: Array<string> |
||||||
|
[x: string]: any |
||||||
|
} |
||||||
|
|
||||||
|
export interface AstNodeLegacy { |
||||||
|
id: number |
||||||
|
name: string |
||||||
|
src: string |
||||||
|
children?: Array<AstNodeLegacy> |
||||||
|
attributes?: AstNodeAtt |
||||||
|
} |
||||||
|
|
||||||
|
export interface AstNodeAtt { |
||||||
|
operator?: string |
||||||
|
string?: null |
||||||
|
type?: string |
||||||
|
value?: string |
||||||
|
constant?: boolean |
||||||
|
name?: string |
||||||
|
public?: boolean |
||||||
|
exportedSymbols?: Object |
||||||
|
argumentTypes?: null |
||||||
|
absolutePath?: string |
||||||
|
[x: string]: any |
||||||
|
} |
||||||
|
|
||||||
|
//////////////
|
||||||
|
// CONTRACT //
|
||||||
|
//////////////
|
||||||
|
export interface CompiledContract { |
||||||
|
/** The Ethereum Contract ABI. If empty, it is represented as an empty array. */ |
||||||
|
abi: ABIDescription[] |
||||||
|
// See the Metadata Output documentation (serialised JSON string)
|
||||||
|
metadata: string |
||||||
|
/** User documentation (natural specification) */ |
||||||
|
userdoc: UserDocumentation |
||||||
|
/** Developer documentation (natural specification) */ |
||||||
|
devdoc: DeveloperDocumentation |
||||||
|
/** Intermediate representation (string) */ |
||||||
|
ir: string |
||||||
|
/** EVM-related outputs */ |
||||||
|
evm: { |
||||||
|
assembly: string |
||||||
|
legacyAssembly: {} |
||||||
|
/** Bytecode and related details. */ |
||||||
|
bytecode: BytecodeObject |
||||||
|
deployedBytecode: BytecodeObject |
||||||
|
/** The list of function hashes */ |
||||||
|
methodIdentifiers: { |
||||||
|
[functionIdentifier: string]: string |
||||||
|
} |
||||||
|
// Function gas estimates
|
||||||
|
gasEstimates: { |
||||||
|
creation: { |
||||||
|
codeDepositCost: string |
||||||
|
executionCost: 'infinite' | string |
||||||
|
totalCost: 'infinite' | string |
||||||
|
} |
||||||
|
external: { |
||||||
|
[functionIdentifier: string]: string |
||||||
|
} |
||||||
|
internal: { |
||||||
|
[functionIdentifier: string]: 'infinite' | string |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
/** eWASM related outputs */ |
||||||
|
ewasm: { |
||||||
|
/** S-expressions format */ |
||||||
|
wast: string |
||||||
|
/** Binary format (hex string) */ |
||||||
|
wasm: string |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/////////
|
||||||
|
// ABI //
|
||||||
|
/////////
|
||||||
|
export type ABIDescription = FunctionDescription | EventDescription |
||||||
|
|
||||||
|
export interface FunctionDescription { |
||||||
|
/** Type of the method. default is 'function' */ |
||||||
|
type?: 'function' | 'constructor' | 'fallback' | 'receive' |
||||||
|
/** The name of the function. Constructor and fallback function never have name */ |
||||||
|
name?: string |
||||||
|
/** List of parameters of the method. Fallback function doesn’t have inputs. */ |
||||||
|
inputs?: ABIParameter[] |
||||||
|
/** List of the outputs parameters for the method, if any */ |
||||||
|
outputs?: ABIParameter[] |
||||||
|
/** State mutability of the method */ |
||||||
|
stateMutability: 'pure' | 'view' | 'nonpayable' | 'payable' |
||||||
|
/** true if function accepts Ether, false otherwise. Default is false */ |
||||||
|
payable?: boolean |
||||||
|
/** true if function is either pure or view, false otherwise. Default is false */ |
||||||
|
constant?: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export interface EventDescription { |
||||||
|
type: 'event' |
||||||
|
name: string |
||||||
|
inputs: ABIParameter & |
||||||
|
{ |
||||||
|
/** true if the field is part of the log’s topics, false if it one of the log’s data segment. */ |
||||||
|
indexed: boolean |
||||||
|
}[] |
||||||
|
/** true if the event was declared as anonymous. */ |
||||||
|
anonymous: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export interface ABIParameter { |
||||||
|
/** The name of the parameter */ |
||||||
|
name: string |
||||||
|
/** The canonical type of the parameter */ |
||||||
|
type: ABITypeParameter |
||||||
|
/** Used for tuple types */ |
||||||
|
components?: ABIParameter[] |
||||||
|
} |
||||||
|
|
||||||
|
export type ABITypeParameter = |
||||||
|
| 'uint' |
||||||
|
| 'uint[]' // TODO : add <M>
|
||||||
|
| 'int' |
||||||
|
| 'int[]' // TODO : add <M>
|
||||||
|
| 'address' |
||||||
|
| 'address[]' |
||||||
|
| 'bool' |
||||||
|
| 'bool[]' |
||||||
|
| 'fixed' |
||||||
|
| 'fixed[]' // TODO : add <M>
|
||||||
|
| 'ufixed' |
||||||
|
| 'ufixed[]' // TODO : add <M>
|
||||||
|
| 'bytes' |
||||||
|
| 'bytes[]' // TODO : add <M>
|
||||||
|
| 'function' |
||||||
|
| 'function[]' |
||||||
|
| 'tuple' |
||||||
|
| 'tuple[]' |
||||||
|
| string // Fallback
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// NATURAL SPECIFICATION //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
|
// Userdoc
|
||||||
|
export interface UserDocumentation { |
||||||
|
methods: UserMethodList |
||||||
|
notice: string |
||||||
|
} |
||||||
|
|
||||||
|
export type UserMethodList = { |
||||||
|
[functionIdentifier: string]: UserMethodDoc |
||||||
|
} & { |
||||||
|
'constructor'?: string |
||||||
|
} |
||||||
|
export interface UserMethodDoc { |
||||||
|
notice: string |
||||||
|
} |
||||||
|
|
||||||
|
// Devdoc
|
||||||
|
export interface DeveloperDocumentation { |
||||||
|
author: string |
||||||
|
title: string |
||||||
|
details: string |
||||||
|
methods: DevMethodList |
||||||
|
} |
||||||
|
|
||||||
|
export interface DevMethodList { |
||||||
|
[functionIdentifier: string]: DevMethodDoc |
||||||
|
} |
||||||
|
|
||||||
|
export interface DevMethodDoc { |
||||||
|
author: string |
||||||
|
details: string |
||||||
|
return: string |
||||||
|
params: { |
||||||
|
[param: string]: string |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//////////////
|
||||||
|
// BYTECODE //
|
||||||
|
//////////////
|
||||||
|
export interface BytecodeObject { |
||||||
|
/** The bytecode as a hex string. */ |
||||||
|
object: string |
||||||
|
/** Opcodes list */ |
||||||
|
opcodes: string |
||||||
|
/** The source mapping as a string. See the source mapping definition. */ |
||||||
|
sourceMap: string |
||||||
|
/** If given, this is an unlinked object. */ |
||||||
|
linkReferences?: { |
||||||
|
[contractName: string]: { |
||||||
|
/** Byte offsets into the bytecode. */ |
||||||
|
[library: string]: { start: number; length: number }[] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
{ |
||||||
|
"include": ["src", "index.ts"], |
||||||
|
"compilerOptions": { |
||||||
|
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ |
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ |
||||||
|
"lib": ["dom", "es2018"], /* Specify library files to be included in the compilation. */ |
||||||
|
"declaration": true, /* Generates corresponding '.d.ts' file. */ |
||||||
|
"sourceMap": true, /* Generates corresponding '.map' file. */ |
||||||
|
"outDir": "./dist", /* Redirect output structure to the directory. */ |
||||||
|
/* Strict Type-Checking Options */ |
||||||
|
"strict": true, /* Enable all strict type-checking options. */ |
||||||
|
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ |
||||||
|
/* Module Resolution Options */ |
||||||
|
"baseUrl": "./src", /* Base directory to resolve non-absolute module names. */ |
||||||
|
"paths": { "remix-solidity": ["./"] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ |
||||||
|
"typeRoots": [ |
||||||
|
"./@types", |
||||||
|
"./node_modules/@types" |
||||||
|
], |
||||||
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ |
||||||
|
/* Experimental Options */ |
||||||
|
"experimentalDecorators": false, /* Enables experimental support for ES7 decorators. */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue