From 929cbdbfd5071ae63c27edf11c21b0cefb591800 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Oct 2015 20:14:42 +0200 Subject: [PATCH 1/4] Handle multiple files. --- index.html | 73 +++++++++++++++++++++++++++++++++++++++--------------- worker.js | 14 +++++++---- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/index.html b/index.html index e4596afd1c..af7f40ba15 100644 --- a/index.html +++ b/index.html @@ -475,6 +475,7 @@ // ----------------- compiler ---------------------- var compileJSON; + var compilerAcceptsMultipleFiles; var previousInput = ''; var sourceAnnotations = []; @@ -487,11 +488,13 @@ var input = editor.getValue(); window.localStorage.setItem(SOL_CACHE_FILE, input); - var inputIncludingImports = includeLocalAndRemoteImports(input, compile); - if (!inputIncludingImports) return; + var files = {}; + files[fileNameFromKey(SOL_CACHE_FILE)] = input; + var input = gatherImports(files, compile); + if (!input) return; var optimize = document.querySelector('#optimize').checked; - compileJSON(inputIncludingImports, optimize ? 1 : 0); + compileJSON(input, optimize ? 1 : 0); }; var compilationFinished = function(result) { var data = $.parseJSON(result); @@ -527,7 +530,14 @@ var onCompilerLoaded = function() { if (worker === null) { - var compile = Module.cwrap("compileJSON", "string", ["string", "number"]); + var compile; + if ('_compileJSONMulti' in Module) { + compilerAcceptsMultipleFiles = true; + compile = Module.cwrap("compileJSONMulti", "string", ["string", "number"]); + } else { + compilerAcceptsMultipleFiles = false; + compile = Module.cwrap("compileJSON", "string", ["string", "number"]); + } compileJSON = function(source, optimize, cb) { try { var result = compile(source, optimize); @@ -543,24 +553,46 @@ }; var cachedRemoteFiles = {}; - function includeLocalAndRemoteImports(input, asyncCallback, needAsync) { + function gatherImports(files, asyncCallback, needAsync) { + if (!compilerAcceptsMultipleFiles) + return files[fileNameFromKey(SOL_CACHE_FILE)]; var importRegex = /import\s[\'\"]([^\'\"]+)[\'\"];/g; - var match; - for (var runs = 0; (match = importRegex.exec(input)) !== null && runs < 100; runs++) { - var githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(match[1]); - if (getFiles().indexOf(fileKey(match[1])) !== -1) - input = input.replace(match[0], window.localStorage.getItem( fileKey(match[1]) )); - else if (match[1] in cachedRemoteFiles) - input = input.replace(match[0], cachedRemoteFiles[match[1]]); - else if (githubMatch) { - $.getJSON('https://api.github.com/repos/' + githubMatch[3] + '/contents/' + githubMatch[4], function(result) { - var content = Base64.decode(result.content); - cachedRemoteFiles[match[1]] = content; - includeLocalAndRemoteImports(input.replace(match[0], content), asyncCallback, true); - }); - return null; + var reloop = false; + do { + reloop = false; + for (var fileName in files) { + var match; + while (match = importRegex.exec(files[fileName])) { + var m = match[1]; + if (m in files) continue; + if (getFiles().indexOf(fileKey(m)) !== -1) { + files[m] = window.localStorage[fileKey(match[1])]; + reloop = true; + } else if (m in cachedRemoteFiles) { + files[m] = cachedRemoteFiles[m]; + reloop = true; + } else if (githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m)) { + $.getJSON('https://api.github.com/repos/' + githubMatch[3] + '/contents/' + githubMatch[4], function(result) { + var content; + if ('content' in result) + content = Base64.decode(result.content); + else + content = "\"" + m + "\" NOT FOUND"; //@TODO handle this better + cachedRemoteFiles[m] = content; + files[m] = content; + gatherImports(files, asyncCallback, true); + }).fail(function(){ + var content = "\"" + m + "\" NOT FOUND"; //@TODO handle this better + cachedRemoteFiles[m] = content; + files[m] = content; + gatherImports(files, asyncCallback, true); + }); + return null; + } + } } - } + } while (reloop); + var input = JSON.stringify({'sources':files}); if (needAsync) asyncCallback(input); return input; @@ -575,6 +607,7 @@ switch (data.cmd) { case 'versionLoaded': $('#version').text(data.data); + compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles; onCompilerLoaded(); break; case 'compiled': diff --git a/worker.js b/worker.js index 60cd302b2c..2edea48ff7 100644 --- a/worker.js +++ b/worker.js @@ -10,11 +10,15 @@ addEventListener('message', function(e) { importScripts(data.data); version = Module.cwrap("version", "string", []); - compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]); - postMessage({cmd: 'versionLoaded', data: version()}); - break; - case 'version': - postMessage({cmd: 'versionLoaded', data: version()}); + if ('_compileJSONMulti' in Module) + compileJSON = Module.cwrap("compileJSONMulti", "string", ["string", "number"]); + else + compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]); + postMessage({ + cmd: 'versionLoaded', + data: version(), + acceptsMultipleFiles: ('_compileJSONMulti' in Module) + }); break; case 'compile': postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize)}); From ef674daa2815b719c732bb3a334814d374717b66 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Oct 2015 20:31:14 +0200 Subject: [PATCH 2/4] Render errors from other files correctly. --- index.html | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/index.html b/index.html index af7f40ba15..11ef8dfe61 100644 --- a/index.html +++ b/index.html @@ -655,7 +655,7 @@ var detailsOpen = {}; function errortype(message) { - return message.match(/^[0-9:]* Warning: /) ? 'warning' : 'error'; + return message.match(/^.*:[0-9]*:[0-9]* Warning: /) ? 'warning' : 'error'; } var renderError = function(message) { @@ -663,18 +663,27 @@ var $pre = $("
").text(message);
 				var $error = $('
').prepend($pre); $('#output').append( $error ); - var err = message.match(/^:([0-9]*):([0-9]*)/); - if (err && err.length) { - var errLine = parseInt(err[1], 10) - 1; - var errCol = err[2] ? parseInt(err[2], 10) : 0; - sourceAnnotations[sourceAnnotations.length] = { - row: errLine, - column: errCol, - text: message, - type: type - }; - editor.getSession().setAnnotations(sourceAnnotations); + var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /); + if (err) { + var errFile = err[1]; + var errLine = parseInt(err[2], 10) - 1; + var errCol = err[4] ? parseInt(err[4], 10) : 0; + if (errFile == '' || errFile == fileNameFromKey(SOL_CACHE_FILE)) { + sourceAnnotations[sourceAnnotations.length] = { + row: errLine, + column: errCol, + text: message, + type: type + }; + editor.getSession().setAnnotations(sourceAnnotations); + } $error.click(function(ev){ + if (errFile != '' && errFile != fileNameFromKey(SOL_CACHE_FILE) && getFiles().indexOf(fileKey(errFile)) !== -1) { + // Switch to file + SOL_CACHE_FILE = fileKey(errFile); + updateFiles(); + //@TODO could show some error icon in files with errors + } editor.focus(); editor.gotoLine(errLine + 1, errCol - 1, true); }); From 8b9a212d566451db8d4a99e3675a6f44f823339b Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Oct 2015 09:57:17 +0200 Subject: [PATCH 3/4] Updated npm package. --- README.md | 15 ++++++++++++++- index.js | 22 +++++++++++++++++----- package.json | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3018b68e8b..d31c6083c8 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,17 @@ And then use it like so: var input = "contract x { function g() {} }"; var output = solc.compile(input, 1); // 1 activates the optimiser for (var contractName in output.contracts) - console.log(contractName + ': ' + output.contracts[contractName].bytecode); \ No newline at end of file + console.log(contractName + ': ' + output.contracts[contractName].bytecode); + +Starting from version 0.1.6, multiple files are supported with automatic import resolution by the compiler as follows: + + var solc = require('solc'); + var input = { + 'lib.sol': 'library L { function f() returns (uint) { return 7; } }', + 'cont.sol': 'import "lib.sol"; contract x { function g() { L.f(); } }' + }; + var output = solc.compile({sources: input}, 1); + for (var contractName in output.contracts) + console.log(contractName + ': ' + output.contracts[contractName].bytecode); + +Note that all input files that are imported have to be supplied, the compiler will not load any additional files on its own. diff --git a/index.js b/index.js index 6589bfd43f..2e3e2552c7 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,23 @@ var soljson = require('./bin/soljson-latest.js'); -compileJSON = soljson.cwrap("compileJSON", "string", ["string", "number"]); +var compileJSON = soljson.cwrap("compileJSON", "string", ["string", "number"]); +var compileJSONMulti = + '_compileJSONMulti' in soljson ? + soljson.cwrap("compileJSONMulti", "string", ["string", "number"]) : + null; + +var compile = function(input, optimise) { + var result = ''; + if (typeof(input) != typeof('') && compileJSONMulti !== null) + result = compileJSONMulti(JSON.stringify(input), optimise); + else + result = compileJSON(input, optimise); + return JSON.parse(result); +} + module.exports = { - compile: function(input, optimise){ - return JSON.parse( compileJSON(input, optimise) ); - }, + compile: compile, version: soljson.cwrap("version", "string", []) -} \ No newline at end of file +} diff --git a/package.json b/package.json index 29bc351e95..b05b6cd2fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.1.5", + "version": "0.1.5-multi", "description": "Solidity compiler", "main": "index.js", "scripts": { From 1e325dd01698e9aea729acf32b942229029d384f Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Oct 2015 12:28:00 +0200 Subject: [PATCH 4/4] Removed space. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d31c6083c8..921f73d5bc 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Starting from version 0.1.6, multiple files are supported with automatic import var solc = require('solc'); var input = { 'lib.sol': 'library L { function f() returns (uint) { return 7; } }', - 'cont.sol': 'import "lib.sol"; contract x { function g() { L.f(); } }' + 'cont.sol': 'import "lib.sol"; contract x { function g() { L.f(); } }' }; var output = solc.compile({sources: input}, 1); for (var contractName in output.contracts)