From 959b5c06bd5bc8be8926091cab6d51a86f13440c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 9 Nov 2019 17:14:39 +0000 Subject: [PATCH 001/153] Drop backwards compatibility aliases compileStandard/compilerStandardWrapper --- README.md | 2 -- test/compiler.js | 8 -------- wrapper.js | 4 ---- 3 files changed, 14 deletions(-) diff --git a/README.md b/README.md index eddc2c2a..0a91baa1 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,6 @@ of them are resolved. Starting 0.5.12 it also accepts an object in place of the callback to supply different kind of callbacks, however only file imports are supported. -_Note_: as an intermittent backwards compatibility feature, between versions 0.5.0 and 0.5.2, `compileStandard` and `compileStandardWrapper` also exists and behave like `compile` does. - #### Example usage without the import callback Example: diff --git a/test/compiler.js b/test/compiler.js index 5acb2c3e..a3c5839b 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -626,14 +626,6 @@ function runTests (solc, versionText) { }); }); }); - - tape('API backwards compatibility', function (t) { - t.test('compileStandard and compileStandardWrapper exists', function (st) { - st.equal(solc.compile, solc.compileStandard); - st.equal(solc.compile, solc.compileStandardWrapper); - st.end(); - }); - }); } } diff --git a/wrapper.js b/wrapper.js index b43c0030..a444ac41 100644 --- a/wrapper.js +++ b/wrapper.js @@ -239,10 +239,6 @@ function setupMethods (soljson) { nativeStandardJSON: compileStandard !== null }, compile: compileStandardWrapper, - // Temporary wrappers to minimise breaking with other projects. - // NOTE: to be removed in 0.5.2 - compileStandard: compileStandardWrapper, - compileStandardWrapper: compileStandardWrapper, // Loads the compiler of the given version from the github repository // instead of from the local filesystem. loadRemoteVersion: function (versionString, cb) { From 12bf856cb647c5c1eb721a9494a8ecc9ca25e96b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 9 Nov 2019 17:29:29 +0000 Subject: [PATCH 002/153] Change lowlevel API to also expect a callback object --- test/compiler.js | 15 ++++++--------- wrapper.js | 13 ++----------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index a3c5839b..09b7abbd 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -169,7 +169,7 @@ function runTests (solc, versionText) { return { error: 'File not found' }; } } - var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports)); + var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports })); var x = getBytecode(output, 'cont.sol', 'x'); var L = getBytecode(output, 'lib.sol', 'L'); st.ok(typeof x === 'string'); @@ -193,7 +193,7 @@ function runTests (solc, versionText) { function findImports (path) { return { error: 'File not found' }; } - var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports)); + var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports })); st.plan(3); st.ok('errors' in output); // Check if the ParserError exists, but allow others too @@ -224,7 +224,7 @@ function runTests (solc, versionText) { throw new Error('Could not implement this interface properly...'); } st.throws(function () { - solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports); + solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports }); }, /^Error: Could not implement this interface properly.../); st.end(); }); @@ -242,7 +242,7 @@ function runTests (solc, versionText) { }; st.throws(function () { solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, "this isn't a callback"); - }, /Invalid callback specified./); + }, /Invalid callback object specified./); st.end(); }); @@ -387,7 +387,7 @@ function runTests (solc, versionText) { } } - var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), findImports)); + var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), { import: findImports })); st.ok(bytecodeExists(output, 'cont.sol', 'x')); st.ok(bytecodeExists(output, 'lib.sol', 'L')); st.end(); @@ -462,16 +462,13 @@ function runTests (solc, versionText) { } } - var output = JSON.parse(solc.compile(JSON.stringify(input), findImports)); + var output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); var x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); var L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); - - var outputNewApi = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); - st.deepEqual(output, outputNewApi); st.end(); }); diff --git a/wrapper.js b/wrapper.js index a444ac41..ce9942c5 100644 --- a/wrapper.js +++ b/wrapper.js @@ -50,8 +50,8 @@ function setupMethods (soljson) { // This calls compile() with args || cb var runWithReadCallback = function (readCallback, compile, args) { - // Forward compatibility with 0.6.x - if (typeof readCallback === 'object') { + if (readCallback) { + assert(typeof readCallback === 'object', 'Invalid callback object specified.'); readCallback = readCallback.import; } @@ -132,15 +132,6 @@ function setupMethods (soljson) { }); } - // Forward compatibility with 0.6.x - if (typeof readCallback === 'object') { - readCallback = readCallback.import; - } - - if (readCallback !== undefined) { - assert(typeof readCallback === 'function', 'Invalid callback specified.'); - } - try { input = JSON.parse(input); } catch (e) { From 9ba7038cbf67247c71c9e5701c3bfd276b132925 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 17 Sep 2019 17:35:16 +0200 Subject: [PATCH 003/153] Add SMT solver callback --- smtchecker.js | 14 ++- smtsolver.js | 5 +- test/index.js | 1 + test/smtCheckerTests/smoke.sol | 5 + test/smtcallback.js | 165 +++++++++++++++++++++++++++++++++ wrapper.js | 68 +++++++++++--- 6 files changed, 243 insertions(+), 15 deletions(-) create mode 100644 test/smtCheckerTests/smoke.sol create mode 100644 test/smtcallback.js diff --git a/smtchecker.js b/smtchecker.js index 2e279f6d..5a1b85b3 100644 --- a/smtchecker.js +++ b/smtchecker.js @@ -25,6 +25,18 @@ function handleSMTQueries (inputJSON, outputJSON, solver) { return inputJSON; } +function smtCallback (solver) { + return function (query) { + try { + var result = solver(query); + return { contents: result }; + } catch (err) { + return { error: err }; + } + }; +} + module.exports = { - handleSMTQueries: handleSMTQueries + handleSMTQueries: handleSMTQueries, + smtCallback: smtCallback }; diff --git a/smtsolver.js b/smtsolver.js index a544dd0a..be9862f8 100644 --- a/smtsolver.js +++ b/smtsolver.js @@ -46,7 +46,7 @@ function solve (query) { !solverOutput.startsWith('unsat') && !solverOutput.startsWith('unknown') ) { - throw new Error('Failed solve SMT query. ' + e.toString()); + throw new Error('Failed to solve SMT query. ' + e.toString()); } } // Trigger early manual cleanup @@ -55,5 +55,6 @@ function solve (query) { } module.exports = { - smtSolver: solve + smtSolver: solve, + availableSolvers: solvers.length }; diff --git a/test/index.js b/test/index.js index 660cc7ec..10e97cc6 100644 --- a/test/index.js +++ b/test/index.js @@ -3,6 +3,7 @@ const semver = require('semver'); require('./linker.js'); require('./translate.js'); require('./compiler.js'); +require('./smtcallback.js'); require('./smtchecker.js'); require('./abi.js'); diff --git a/test/smtCheckerTests/smoke.sol b/test/smtCheckerTests/smoke.sol new file mode 100644 index 00000000..74796e91 --- /dev/null +++ b/test/smtCheckerTests/smoke.sol @@ -0,0 +1,5 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure { + } +} diff --git a/test/smtcallback.js b/test/smtcallback.js new file mode 100644 index 00000000..cd00d70d --- /dev/null +++ b/test/smtcallback.js @@ -0,0 +1,165 @@ +const tape = require('tape'); +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); +const solc = require('../index.js'); +const smtchecker = require('../smtchecker.js'); +const smtsolver = require('../smtsolver.js'); + +var pragmaSol = 'pragma solidity >=0.0;\n'; +var pragmaSMT = 'pragma experimental SMTChecker;\n'; + +function collectErrors (solOutput) { + if (solOutput === undefined) { + return []; + } + + var errors = []; + for (var i in solOutput.errors) { + var error = solOutput.errors[i]; + if (error.message.includes('This is a pre-release compiler version')) { + continue; + } + errors.push(error.message); + } + return errors; +} + +function expectErrors (errors, expectations) { + if (errors.length !== expectations.length) { + return false; + } + + for (var i in errors) { + if (!errors[i].includes(expectations[i])) { + return false; + } + } + + return true; +} + +tape('SMTCheckerCallback', function (t) { + t.test('Interface via callback', function (st) { + if (!semver.gt(solc.semver(), '0.5.99')) { + st.skip('SMT callback not implemented by this compiler version.'); + st.end(); + return; + } + + var satCallback = function (query) { + return { contents: 'sat\n' }; + }; + var unsatCallback = function (query) { + return { contents: 'unsat\n' }; + }; + var errorCallback = function (query) { + return { error: 'Fake SMT solver error.' }; + }; + + var input = { 'a': { content: pragmaSol + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } }; + var inputJSON = JSON.stringify({ + language: 'Solidity', + sources: input + }); + var tests = [ + { cb: satCallback, expectations: ['Assertion violation happens here'] }, + { cb: unsatCallback, expectations: [] }, + { cb: errorCallback, expectations: ['BMC analysis was not possible'] } + ]; + for (var i in tests) { + var test = tests[i]; + var output = JSON.parse(solc.compile( + inputJSON, + { smtSolver: test.cb } + )); + var errors = collectErrors(output); + st.ok(expectErrors(errors, test.expectations)); + } + st.end(); + }); + + t.test('Solidity smtCheckerTests', function (st) { + var testdir = path.resolve(__dirname, 'smtCheckerTests/'); + if (!fs.existsSync(testdir)) { + st.skip('SMT checker tests not present.'); + st.end(); + return; + } + + if (smtsolver.availableSolvers === 0) { + st.skip('No SMT solver available.'); + st.end(); + return; + } + + var sources = []; + + // BFS to get all test files + var dirs = [testdir]; + var i; + while (dirs.length > 0) { + var dir = dirs.shift(); + var files = fs.readdirSync(dir); + for (i in files) { + var file = path.join(dir, files[i]); + if (fs.statSync(file).isDirectory()) { + dirs.push(file); + } else { + sources.push(file); + } + } + } + + // Read tests and collect expectations + var tests = []; + for (i in sources) { + st.comment('Collecting ' + sources[i] + '...'); + var source = fs.readFileSync(sources[i], 'utf8'); + var expected = []; + var delimiter = '// ----'; + if (source.includes(delimiter)) { + expected = source.substring(source.indexOf('// ----') + 8, source.length).split('\n'); + // Sometimes the last expectation line ends with a '\n' + if (expected.length > 0 && expected[expected.length - 1] === '') { + expected.pop(); + } + } + tests[sources[i]] = { + expectations: expected, + solidity: { test: { content: pragmaSol + source } } + }; + } + + // Run all tests + for (i in tests) { + var test = tests[i]; + var output = JSON.parse(solc.compile( + JSON.stringify({ + language: 'Solidity', + sources: test.solidity + }), + { smtSolver: smtchecker.smtCallback(smtsolver.smtSolver) } + )); + st.ok(output); + + // Collect obtained error messages + test.errors = collectErrors(output); + + // These are errors in the SMTLib2Interface encoding. + if (test.errors.length > 0 && test.errors[test.errors.length - 1].includes('BMC analysis was not possible')) { + continue; + } + + // These are due to CHC not being supported via SMTLib2Interface yet. + if (test.expectations.length !== test.errors.length) { + continue; + } + + // Compare expected vs obtained errors + st.ok(expectErrors(test.expectations, test.errors)); + } + + st.end(); + }); +}); diff --git a/wrapper.js b/wrapper.js index ce9942c5..22072596 100644 --- a/wrapper.js +++ b/wrapper.js @@ -3,6 +3,7 @@ var translate = require('./translate.js'); var requireFromString = require('require-from-string'); var https = require('https'); var MemoryStream = require('memorystream'); +var semver = require('semver'); function setupMethods (soljson) { var version; @@ -37,8 +38,21 @@ function setupMethods (soljson) { var wrapCallback = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); - return function (path, contents, error) { - var result = callback(soljson.Pointer_stringify(path)); + return function (data, contents, error) { + var result = callback(soljson.Pointer_stringify(data)); + if (typeof result.contents === 'string') { + copyString(result.contents, contents); + } + if (typeof result.error === 'string') { + copyString(result.error, error); + } + }; + }; + + var wrapCallbackWithKind = function (callback) { + assert(typeof callback === 'function', 'Invalid callback specified.'); + return function (kind, data, contents, error) { + var result = callback(soljson.Pointer_stringify(kind), soljson.Pointer_stringify(data)); if (typeof result.contents === 'string') { copyString(result.contents, contents); } @@ -49,25 +63,55 @@ function setupMethods (soljson) { }; // This calls compile() with args || cb - var runWithReadCallback = function (readCallback, compile, args) { - if (readCallback) { - assert(typeof readCallback === 'object', 'Invalid callback object specified.'); - readCallback = readCallback.import; + var runWithCallbacks = function (callbacks, compile, args) { + if (callbacks) { + assert(typeof callbacks === 'object', 'Invalid callback object specified.'); + } else { + callbacks = {}; } + var readCallback = callbacks.import; if (readCallback === undefined) { - readCallback = function (path) { + readCallback = function (data) { return { error: 'File import callback not supported' }; }; } + var singleCallback; + if (semver.gt(versionToSemver(), '0.5.99')) { + // After 0.6.x multiple kind of callbacks are supported. + var smtSolverCallback = callbacks.smtSolver; + if (smtSolverCallback === undefined) { + smtSolverCallback = function (data) { + return { + error: 'SMT solver callback not supported' + }; + }; + } + + singleCallback = function (kind, data) { + if (kind === 'source') { + return readCallback(data); + } else if (kind === 'smt-query') { + return smtSolverCallback(data); + } else { + assert(false, 'Invalid callback kind specified.'); + } + }; + + singleCallback = wrapCallbackWithKind(singleCallback); + } else { + // Old Solidity version only supported imports. + singleCallback = wrapCallback(readCallback); + } + // This is to support multiple versions of Emscripten. var addFunction = soljson.addFunction || soljson.Runtime.addFunction; var removeFunction = soljson.removeFunction || soljson.Runtime.removeFunction; - var cb = addFunction(wrapCallback(readCallback)); + var cb = addFunction(singleCallback); var output; try { args.push(cb); @@ -94,7 +138,7 @@ function setupMethods (soljson) { if ('_compileJSONCallback' in soljson) { var compileInternal = soljson.cwrap('compileJSONCallback', 'string', ['string', 'number', 'number']); compileJSONCallback = function (input, optimize, readCallback) { - return runWithReadCallback(readCallback, compileInternal, [ input, optimize ]); + return runWithCallbacks(readCallback, compileInternal, [ input, optimize ]); }; } @@ -102,13 +146,13 @@ function setupMethods (soljson) { if ('_compileStandard' in soljson) { var compileStandardInternal = soljson.cwrap('compileStandard', 'string', ['string', 'number']); compileStandard = function (input, readCallback) { - return runWithReadCallback(readCallback, compileStandardInternal, [ input ]); + return runWithCallbacks(readCallback, compileStandardInternal, [ input ]); }; } if ('_solidity_compile' in soljson) { var solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number']); - compileStandard = function (input, readCallback) { - return runWithReadCallback(readCallback, solidityCompile, [ input ]); + compileStandard = function (input, callbacks) { + return runWithCallbacks(callbacks, solidityCompile, [ input ]); }; } From cfe1713ed19a8dc3ff14508cbb83454087e91dec Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 22 Nov 2019 11:46:02 +0000 Subject: [PATCH 004/153] Check compiler version once in the wrapper --- wrapper.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wrapper.js b/wrapper.js index 22072596..5c3c4cc3 100644 --- a/wrapper.js +++ b/wrapper.js @@ -17,6 +17,8 @@ function setupMethods (soljson) { return translate.versionToSemver(version()); }; + var isVersion6 = semver.gt(versionToSemver(), '0.5.99'); + var license; if ('_solidity_license' in soljson) { license = soljson.cwrap('solidity_license', 'string', []); @@ -80,7 +82,7 @@ function setupMethods (soljson) { } var singleCallback; - if (semver.gt(versionToSemver(), '0.5.99')) { + if (isVersion6) { // After 0.6.x multiple kind of callbacks are supported. var smtSolverCallback = callbacks.smtSolver; if (smtSolverCallback === undefined) { From 67b2693387c571574596b5f8cbeffd702172ff89 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 22 Nov 2019 11:48:51 +0000 Subject: [PATCH 005/153] Support callback context in 0.6.x --- wrapper.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/wrapper.js b/wrapper.js index 5c3c4cc3..3dba9833 100644 --- a/wrapper.js +++ b/wrapper.js @@ -53,7 +53,7 @@ function setupMethods (soljson) { var wrapCallbackWithKind = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); - return function (kind, data, contents, error) { + return function (context, kind, data, contents, error) { var result = callback(soljson.Pointer_stringify(kind), soljson.Pointer_stringify(data)); if (typeof result.contents === 'string') { copyString(result.contents, contents); @@ -117,6 +117,10 @@ function setupMethods (soljson) { var output; try { args.push(cb); + if (isVersion6) { + // Callback context. + args.push(null); + } output = compile.apply(undefined, args); } catch (e) { removeFunction(cb); @@ -152,7 +156,12 @@ function setupMethods (soljson) { }; } if ('_solidity_compile' in soljson) { - var solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number']); + var solidityCompile; + if (isVersion6) { + solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number', 'number']); + } else { + solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number']); + } compileStandard = function (input, callbacks) { return runWithCallbacks(callbacks, solidityCompile, [ input ]); }; From af7451ebcc2a4dfc7fb4869051ad6cf1f47f5a9e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 26 Nov 2019 16:19:23 +0100 Subject: [PATCH 006/153] Explain the parameters of the solc entry points --- wrapper.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wrapper.js b/wrapper.js index 3dba9833..5c256869 100644 --- a/wrapper.js +++ b/wrapper.js @@ -132,16 +132,19 @@ function setupMethods (soljson) { var compileJSON = null; if ('_compileJSON' in soljson) { + // input (text), optimize (bool) -> output (jsontext) compileJSON = soljson.cwrap('compileJSON', 'string', ['string', 'number']); } var compileJSONMulti = null; if ('_compileJSONMulti' in soljson) { + // input (jsontext), optimize (bool) -> output (jsontext) compileJSONMulti = soljson.cwrap('compileJSONMulti', 'string', ['string', 'number']); } var compileJSONCallback = null; if ('_compileJSONCallback' in soljson) { + // input (jsontext), optimize (bool), callback (ptr) -> output (jsontext) var compileInternal = soljson.cwrap('compileJSONCallback', 'string', ['string', 'number', 'number']); compileJSONCallback = function (input, optimize, readCallback) { return runWithCallbacks(readCallback, compileInternal, [ input, optimize ]); @@ -150,6 +153,7 @@ function setupMethods (soljson) { var compileStandard = null; if ('_compileStandard' in soljson) { + // input (jsontext), callback (ptr) -> output (jsontext) var compileStandardInternal = soljson.cwrap('compileStandard', 'string', ['string', 'number']); compileStandard = function (input, readCallback) { return runWithCallbacks(readCallback, compileStandardInternal, [ input ]); @@ -158,8 +162,10 @@ function setupMethods (soljson) { if ('_solidity_compile' in soljson) { var solidityCompile; if (isVersion6) { + // input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext) solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number', 'number']); } else { + // input (jsontext), callback (ptr) -> output (jsontext) solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number']); } compileStandard = function (input, callbacks) { From bb73f36f2db6a4fb14f19112589cc4f7ad02f014 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 16:41:56 +0100 Subject: [PATCH 007/153] Rename copyString to copyToCString --- wrapper.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wrapper.js b/wrapper.js index b43c0030..079f4a0e 100644 --- a/wrapper.js +++ b/wrapper.js @@ -28,7 +28,7 @@ function setupMethods (soljson) { }; } - var copyString = function (str, ptr) { + var copyToCString = function (str, ptr) { var length = soljson.lengthBytesUTF8(str); var buffer = soljson._malloc(length + 1); soljson.stringToUTF8(str, buffer, length + 1); @@ -40,10 +40,10 @@ function setupMethods (soljson) { return function (path, contents, error) { var result = callback(soljson.Pointer_stringify(path)); if (typeof result.contents === 'string') { - copyString(result.contents, contents); + copyToCString(result.contents, contents); } if (typeof result.error === 'string') { - copyString(result.error, error); + copyToCString(result.error, error); } }; }; From 013ce4bdda0c505e152619d834cf5cc2fbc807e0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 16:42:42 +0100 Subject: [PATCH 008/153] Add copyFromCString wrapper for Pointer_stringify --- wrapper.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wrapper.js b/wrapper.js index 079f4a0e..2f0f0f2c 100644 --- a/wrapper.js +++ b/wrapper.js @@ -35,10 +35,14 @@ function setupMethods (soljson) { soljson.setValue(ptr, buffer, '*'); }; + var copyFromCString = function (ptr) { + return soljson.Pointer_stringify(ptr); + } + var wrapCallback = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); return function (path, contents, error) { - var result = callback(soljson.Pointer_stringify(path)); + var result = callback(copyFromCString(path)); if (typeof result.contents === 'string') { copyToCString(result.contents, contents); } From 4cfef367db0b241e1b61ae01bf9d0f94670fb3d9 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 16:47:54 +0100 Subject: [PATCH 009/153] Support newer Emscripten which droppped Pointer_stringify in favour of UTF8ToString --- wrapper.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wrapper.js b/wrapper.js index 2f0f0f2c..72e89d98 100644 --- a/wrapper.js +++ b/wrapper.js @@ -35,9 +35,9 @@ function setupMethods (soljson) { soljson.setValue(ptr, buffer, '*'); }; - var copyFromCString = function (ptr) { - return soljson.Pointer_stringify(ptr); - } + // This is to support multiple versions of Emscripten. + // Take a single `ptr` and returns a `str`. + var copyFromCString = soljson.UTF8ToString || soljson.Pointer_stringify; var wrapCallback = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); From 4b3a359501577cb7a7f8260755c0ca619cdbc85d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 18:59:23 +0100 Subject: [PATCH 010/153] Add comment to copyToCString about memory allocation --- wrapper.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wrapper.js b/wrapper.js index 72e89d98..c3db217d 100644 --- a/wrapper.js +++ b/wrapper.js @@ -30,6 +30,9 @@ function setupMethods (soljson) { var copyToCString = function (str, ptr) { var length = soljson.lengthBytesUTF8(str); + // This is allocating memory using solc's allocator. + // Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. + // See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 var buffer = soljson._malloc(length + 1); soljson.stringToUTF8(str, buffer, length + 1); soljson.setValue(ptr, buffer, '*'); From fb1daa590170c265f0e15c4aa0f637e64dbe27df Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 25 Nov 2019 19:33:55 +0000 Subject: [PATCH 011/153] Simplify translateAsmJson's test --- test/compiler.js | 8 ++++++++ translate.js | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index 5acb2c3e..bb8dd9a4 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -654,3 +654,11 @@ for (var version in versions) { const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); runTests(newSolc, version); } + + +// add tests: +// - multiple sources with very old version (error) +// - compiler warnings/errors +// - no bytecode output (gas estimation) from compiler + + diff --git a/translate.js b/translate.js index 26e469fb..43e051ad 100644 --- a/translate.js +++ b/translate.js @@ -155,8 +155,9 @@ function escapeString (text) { .replace(/\t/g, '\\t'); } +// 'asm' can be an object or a string function formatAssemblyText (asm, prefix, source) { - if (typeof asm === typeof '' || asm === null || asm === undefined) { + if (typeof asm === 'string' || asm === null || asm === undefined) { return prefix + (asm || '') + '\n'; } var text = prefix + '.code\n'; From bcbe85aaabbd8e750dfa6bbe96c007e24e9327a4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 20:23:20 +0100 Subject: [PATCH 012/153] Move check for multiple files to the correct place within standardCompileWrapper --- test/compiler.js | 8 -------- wrapper.js | 8 +++----- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index bb8dd9a4..5acb2c3e 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -654,11 +654,3 @@ for (var version in versions) { const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); runTests(newSolc, version); } - - -// add tests: -// - multiple sources with very old version (error) -// - compiler warnings/errors -// - no bytecode output (gas estimation) from compiler - - diff --git a/wrapper.js b/wrapper.js index c3db217d..1c1f959a 100644 --- a/wrapper.js +++ b/wrapper.js @@ -163,11 +163,6 @@ function setupMethods (soljson) { return formatFatalError('No input sources specified.'); } - // Bail out early - if ((input['sources'].length > 1) && (compileJSONMulti === null)) { - return formatFatalError('Multiple sources provided, but compiler only supports single input.'); - } - function isOptimizerEnabled (input) { return input['settings'] && input['settings']['optimizer'] && input['settings']['optimizer']['enabled']; } @@ -223,6 +218,9 @@ function setupMethods (soljson) { // Try our luck with an ancient compiler if (compileJSON !== null) { + if (Object.keys(sources).length !== 1) { + return formatFatalError('Multiple sources provided, but compiler only supports single input.'); + } return translateOutput(compileJSON(sources[Object.keys(sources)[0]], isOptimizerEnabled(input)), libraries); } From 207a7b7f0e1fb55045588e36d35ff3b0597c24df Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 20:30:06 +0100 Subject: [PATCH 013/153] Add tests for gas estimation --- test/compiler.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index 5acb2c3e..cbffcbea 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -35,6 +35,20 @@ function runTests (solc, versionText) { } } + function getGasEstimate (output, fileName, contractName) { + try { + var outputFile; + if (semver.lt(solc.semver(), '0.4.9')) { + outputFile = output.contracts['']; + } else { + outputFile = output.contracts[fileName]; + } + return outputFile[contractName]['evm']['gasEstimates']; + } catch (e) { + return ''; + } + } + function expectError (output, errorType, message) { if (output.errors) { for (var error in output.errors) { @@ -406,7 +420,7 @@ function runTests (solc, versionText) { 'settings': { 'outputSelection': { '*': { - '*': [ 'evm.bytecode' ] + '*': [ 'evm.bytecode', 'evm.gasEstimates' ] } } }, @@ -415,7 +429,7 @@ function runTests (solc, versionText) { 'content': 'library L { function f() public returns (uint) { return 7; } }' }, 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } function h() internal {} }' } } }; @@ -424,6 +438,14 @@ function runTests (solc, versionText) { var x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); + var xGas = getGasEstimate(output, 'cont.sol', 'x'); + st.ok(typeof xGas === 'object'); + st.ok(typeof xGas['creation'] === 'object'); + st.ok(typeof xGas['creation']['codeDepositCost'] === 'string'); + st.ok(typeof xGas['external'] === 'object'); + st.ok(typeof xGas['external']['g()'] === 'string'); + st.ok(typeof xGas['internal'] === 'object'); + st.ok(typeof xGas['internal']['h()'] === 'string'); var L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); From 1ffa21e612349f84183052e77c3a29d504d8bad7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 29 Nov 2019 18:20:27 +0100 Subject: [PATCH 014/153] Ensure that no errors (from solc) are thrown in tests --- test/compiler.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/compiler.js b/test/compiler.js index 5acb2c3e..039f8f27 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -421,6 +421,7 @@ function runTests (solc, versionText) { }; var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectNoError(output)); var x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); @@ -463,6 +464,7 @@ function runTests (solc, versionText) { } var output = JSON.parse(solc.compile(JSON.stringify(input), findImports)); + st.ok(expectNoError(output)); var x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); @@ -508,6 +510,7 @@ function runTests (solc, versionText) { }; var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectNoError(output)); var x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); @@ -550,6 +553,7 @@ function runTests (solc, versionText) { }; var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); + st.ok(expectNoError(output)); var x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); From 5337b377fbc3f3b6503bd11a9a57e77d812c522a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 21:02:18 +0100 Subject: [PATCH 015/153] Workaround 0.3.x non-determinism bug (this test has been removed in 0.6.x anyway) --- test/compiler.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index 47338b8f..20e4b567 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -493,9 +493,49 @@ function runTests (solc, versionText) { var L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); + st.end(); + }); + + t.test('compiling standard JSON (with imports + new callback API)', function (st) { + // <0.2.1 doesn't have this + if (!solc.features.importCallback) { + st.skip('Not supported by solc'); + st.end(); + return; + } + + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': [ 'evm.bytecode' ] + } + } + }, + 'sources': { + 'cont.sol': { + 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + } + } + }; + + function findImports (path) { + if (path === 'lib.sol') { + return { contents: 'library L { function f() public returns (uint) { return 7; } }' }; + } else { + return { error: 'File not found' }; + } + } - var outputNewApi = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); - st.deepEqual(output, outputNewApi); + var output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); + st.ok(expectNoError(output)); + var x = getBytecodeStandard(output, 'cont.sol', 'x'); + st.ok(typeof x === 'string'); + st.ok(x.length > 0); + var L = getBytecodeStandard(output, 'lib.sol', 'L'); + st.ok(typeof L === 'string'); + st.ok(L.length > 0); st.end(); }); From b46886dd1950da9ebbc62304b35ebc931bc9be1e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 9 Dec 2019 14:19:18 +0000 Subject: [PATCH 016/153] In tests use inheritance and not libraries unless required for the test --- test/compiler.js | 168 +++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index 20e4b567..8b0c3354 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -102,9 +102,9 @@ function runTests (solc, versionText) { return; } - var output = JSON.parse(solc.lowlevel.compileSingle('contract x { function g() public {} }')); + var output = JSON.parse(solc.lowlevel.compileSingle('contract A { function g() public {} }')); st.ok('contracts' in output); - var bytecode = getBytecode(output, '', 'x'); + var bytecode = getBytecode(output, '', 'A'); st.ok(typeof bytecode === 'string'); st.ok(bytecode.length > 0); st.end(); @@ -152,16 +152,16 @@ function runTests (solc, versionText) { } var input = { - 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', - 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'a.sol': 'contract A { function f() public returns (uint) { return 7; } }', + 'b.sol': 'import "a.sol"; contract B is A { function g() public { f(); } }' }; var output = JSON.parse(solc.lowlevel.compileMulti(JSON.stringify({sources: input}))); - var x = getBytecode(output, 'cont.sol', 'x'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - var L = getBytecode(output, 'lib.sol', 'L'); - st.ok(typeof L === 'string'); - st.ok(L.length > 0); + var B = getBytecode(output, 'b.sol', 'B'); + st.ok(typeof B === 'string'); + st.ok(B.length > 0); + var A = getBytecode(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); st.end(); }); @@ -174,22 +174,22 @@ function runTests (solc, versionText) { } var input = { - 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': 'import "a.sol"; contract B is A { function g() public { f(); } }' }; function findImports (path) { - if (path === 'lib.sol') { - return { contents: 'library L { function f() public returns (uint) { return 7; } }' }; + if (path === 'a.sol') { + return { contents: 'contract A { function f() public returns (uint) { return 7; } }' }; } else { return { error: 'File not found' }; } } var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports)); - var x = getBytecode(output, 'cont.sol', 'x'); - var L = getBytecode(output, 'lib.sol', 'L'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - st.ok(typeof L === 'string'); - st.ok(L.length > 0); + var B = getBytecode(output, 'b.sol', 'B'); + st.ok(typeof B === 'string'); + st.ok(B.length > 0); + var A = getBytecode(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); st.end(); }); @@ -202,7 +202,7 @@ function runTests (solc, versionText) { } var input = { - 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': 'import "a.sol"; contract B { function g() public { f(); } }' }; function findImports (path) { return { error: 'File not found' }; @@ -232,7 +232,7 @@ function runTests (solc, versionText) { } var input = { - 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': 'import "a.sol"; contract B { function g() public { f(); } }' }; function findImports (path) { throw new Error('Could not implement this interface properly...'); @@ -269,7 +269,7 @@ function runTests (solc, versionText) { } var input = { - 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': 'import "a.sol"; contract B is A { function g() public { f(); } }' }; var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}))); st.plan(3); @@ -304,11 +304,11 @@ function runTests (solc, versionText) { } }, 'sources': { - 'lib.sol': { - 'content': 'library L { function f() public returns (uint) { return 7; } }' + 'a.sol': { + 'content': 'contract A { function f() public returns (uint) { return 7; } }' }, - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': { + 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; @@ -322,8 +322,8 @@ function runTests (solc, versionText) { } var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); - st.ok(bytecodeExists(output, 'cont.sol', 'x')); - st.ok(bytecodeExists(output, 'lib.sol', 'L')); + st.ok(bytecodeExists(output, 'a.sol', 'A')); + st.ok(bytecodeExists(output, 'b.sol', 'B')); st.end(); }); @@ -379,15 +379,15 @@ function runTests (solc, versionText) { } }, 'sources': { - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': { + 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; function findImports (path) { - if (path === 'lib.sol') { - return { contents: 'library L { function f() public returns (uint) { return 7; } }' }; + if (path === 'a.sol') { + return { contents: 'contract A { function f() public returns (uint) { return 7; } }' }; } else { return { error: 'File not found' }; } @@ -402,8 +402,8 @@ function runTests (solc, versionText) { } var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), findImports)); - st.ok(bytecodeExists(output, 'cont.sol', 'x')); - st.ok(bytecodeExists(output, 'lib.sol', 'L')); + st.ok(bytecodeExists(output, 'a.sol', 'A')); + st.ok(bytecodeExists(output, 'b.sol', 'B')); st.end(); }); @@ -425,31 +425,31 @@ function runTests (solc, versionText) { } }, 'sources': { - 'lib.sol': { - 'content': 'library L { function f() public returns (uint) { return 7; } }' + 'a.sol': { + 'content': 'contract A { function f() public returns (uint) { return 7; } }' }, - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } function h() internal {} }' + 'b.sol': { + 'content': 'import "a.sol"; contract B is A { function g() public { f(); } function h() internal {} }' } } }; var output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var x = getBytecodeStandard(output, 'cont.sol', 'x'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - var xGas = getGasEstimate(output, 'cont.sol', 'x'); - st.ok(typeof xGas === 'object'); - st.ok(typeof xGas['creation'] === 'object'); - st.ok(typeof xGas['creation']['codeDepositCost'] === 'string'); - st.ok(typeof xGas['external'] === 'object'); - st.ok(typeof xGas['external']['g()'] === 'string'); - st.ok(typeof xGas['internal'] === 'object'); - st.ok(typeof xGas['internal']['h()'] === 'string'); - var L = getBytecodeStandard(output, 'lib.sol', 'L'); - st.ok(typeof L === 'string'); - st.ok(L.length > 0); + var B = getBytecodeStandard(output, 'b.sol', 'B'); + st.ok(typeof B === 'string'); + st.ok(B.length > 0); + var BGas = getGasEstimate(output, 'b.sol', 'B'); + st.ok(typeof BGas === 'object'); + st.ok(typeof BGas['creation'] === 'object'); + st.ok(typeof BGas['creation']['codeDepositCost'] === 'string'); + st.ok(typeof BGas['external'] === 'object'); + st.ok(typeof BGas['external']['g()'] === 'string'); + st.ok(typeof BGas['internal'] === 'object'); + st.ok(typeof BGas['internal']['h()'] === 'string'); + var A = getBytecodeStandard(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); st.end(); }); @@ -471,15 +471,15 @@ function runTests (solc, versionText) { } }, 'sources': { - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': { + 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; function findImports (path) { - if (path === 'lib.sol') { - return { contents: 'library L { function f() public returns (uint) { return 7; } }' }; + if (path === 'a.sol') { + return { contents: 'contract A { function f() public returns (uint) { return 7; } }' }; } else { return { error: 'File not found' }; } @@ -487,12 +487,12 @@ function runTests (solc, versionText) { var output = JSON.parse(solc.compile(JSON.stringify(input), findImports)); st.ok(expectNoError(output)); - var x = getBytecodeStandard(output, 'cont.sol', 'x'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - var L = getBytecodeStandard(output, 'lib.sol', 'L'); - st.ok(typeof L === 'string'); - st.ok(L.length > 0); + var A = getBytecodeStandard(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); + var B = getBytecodeStandard(output, 'b.sol', 'B'); + st.ok(typeof B === 'string'); + st.ok(B.length > 0); st.end(); }); @@ -514,15 +514,15 @@ function runTests (solc, versionText) { } }, 'sources': { - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'b.sol': { + 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; function findImports (path) { - if (path === 'lib.sol') { - return { contents: 'library L { function f() public returns (uint) { return 7; } }' }; + if (path === 'a.sol') { + return { contents: 'contract A { function f() public returns (uint) { return 7; } }' }; } else { return { error: 'File not found' }; } @@ -530,12 +530,12 @@ function runTests (solc, versionText) { var output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); st.ok(expectNoError(output)); - var x = getBytecodeStandard(output, 'cont.sol', 'x'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - var L = getBytecodeStandard(output, 'lib.sol', 'L'); - st.ok(typeof L === 'string'); - st.ok(L.length > 0); + var A = getBytecodeStandard(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); + var B = getBytecodeStandard(output, 'b.sol', 'B'); + st.ok(typeof B === 'string'); + st.ok(B.length > 0); st.end(); }); @@ -565,18 +565,18 @@ function runTests (solc, versionText) { 'lib.sol': { 'content': 'library L { function f() public returns (uint) { return 7; } }' }, - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'a.sol': { + 'content': 'import "lib.sol"; contract A { function g() public { L.f(); } }' } } }; var output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var x = getBytecodeStandard(output, 'cont.sol', 'x'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - st.ok(Object.keys(linker.findLinkReferences(x)).length === 0); + var A = getBytecodeStandard(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); + st.ok(Object.keys(linker.findLinkReferences(A)).length === 0); var L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); @@ -608,18 +608,18 @@ function runTests (solc, versionText) { 'lib.sol': { 'content': 'library L { function f() public returns (uint) { return 7; } }' }, - 'cont.sol': { - 'content': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + 'a.sol': { + 'content': 'import "lib.sol"; contract A { function g() public { L.f(); } }' } } }; var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); st.ok(expectNoError(output)); - var x = getBytecodeStandard(output, 'cont.sol', 'x'); - st.ok(typeof x === 'string'); - st.ok(x.length > 0); - st.ok(Object.keys(linker.findLinkReferences(x)).length === 0); + var A = getBytecodeStandard(output, 'a.sol', 'A'); + st.ok(typeof A === 'string'); + st.ok(A.length > 0); + st.ok(Object.keys(linker.findLinkReferences(A)).length === 0); var L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); From d1b6b0bab836375024e4602225829ac4555c1f0f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 9 Dec 2019 14:42:30 +0000 Subject: [PATCH 017/153] In tests ensure that no link references are reported for non-library outputs --- test/compiler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/compiler.js b/test/compiler.js index 8b0c3354..939a08de 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -439,6 +439,7 @@ function runTests (solc, versionText) { var B = getBytecodeStandard(output, 'b.sol', 'B'); st.ok(typeof B === 'string'); st.ok(B.length > 0); + st.ok(Object.keys(linker.findLinkReferences(B)).length === 0); var BGas = getGasEstimate(output, 'b.sol', 'B'); st.ok(typeof BGas === 'object'); st.ok(typeof BGas['creation'] === 'object'); @@ -493,6 +494,7 @@ function runTests (solc, versionText) { var B = getBytecodeStandard(output, 'b.sol', 'B'); st.ok(typeof B === 'string'); st.ok(B.length > 0); + st.ok(Object.keys(linker.findLinkReferences(B)).length === 0); st.end(); }); From f654d6bae5cbcc2739aeae4e407589f093df57e4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 29 Nov 2019 18:55:06 +0100 Subject: [PATCH 018/153] Include 0.4.0 and 0.4.11 in compiler tests --- test/compiler.js | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index 939a08de..5b3dd33c 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -111,6 +111,13 @@ function runTests (solc, versionText) { }); t.test('invalid source code fails properly (using lowlevel API)', function (st) { + // TODO: try finding an example which doesn't crash it? + if (semver.eq(solc.semver(), '0.4.11')) { + st.skip('Skipping on broken compiler version'); + st.end(); + return; + } + if (typeof solc.lowlevel.compileSingle !== 'function') { st.skip('Low-level compileSingle interface not implemented by this compiler version.'); st.end(); @@ -328,6 +335,13 @@ function runTests (solc, versionText) { }); t.test('invalid source code fails properly with standard JSON (using lowlevel API)', function (st) { + // TODO: try finding an example which doesn't crash it? + if (semver.eq(solc.semver(), '0.4.11')) { + st.skip('Skipping on broken compiler version'); + st.end(); + return; + } + if (typeof solc.lowlevel.compileStandard !== 'function') { st.skip('Low-level compileStandard interface not implemented by this compiler version.'); st.end(); @@ -542,6 +556,13 @@ function runTests (solc, versionText) { }); t.test('compiling standard JSON (using libraries)', function (st) { + // 0.4.0 has a bug with libraries + if (semver.eq(solc.semver(), '0.4.0')) { + st.skip('Skipping on broken compiler version'); + st.end(); + return; + } + // <0.1.6 doesn't have this if (!solc.features.multipleInputs) { st.skip('Not supported by solc'); @@ -586,6 +607,13 @@ function runTests (solc, versionText) { }); t.test('compiling standard JSON (using libraries) (using lowlevel API)', function (st) { + // 0.4.0 has a bug with libraries + if (semver.eq(solc.semver(), '0.4.0')) { + st.skip('Skipping on broken compiler version'); + st.end(); + return; + } + if (typeof solc.lowlevel.compileStandard !== 'function') { st.skip('Low-level compileStandard interface not implemented by this compiler version.'); st.end(); @@ -707,13 +735,16 @@ function runTests (solc, versionText) { runTests(solc, 'latest'); -// New features 0.1.6, 0.2.1, 0.4.11, 0.5.0, etc. +// New compiler interface features 0.1.6, 0.2.1, 0.4.11, 0.5.0, etc. +// 0.4.0 added pragmas (used in tests above) const versions = [ 'v0.1.1+commit.6ff4cd6', 'v0.1.6+commit.d41f8b7', 'v0.2.0+commit.4dc2445', 'v0.2.1+commit.91a6b35', 'v0.3.6+commit.3fc68da', + 'v0.4.0+commit.acd334c9', + 'v0.4.11+commit.68ef5810', 'v0.4.26+commit.4563c3fc' ]; for (var version in versions) { From 83563ff44a084cb8d9baeaae90d80e4b72fa8ed7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 20:47:43 +0100 Subject: [PATCH 019/153] Add tests for abstract contracts Which results in no gas estimation. --- test/compiler.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/compiler.js b/test/compiler.js index 5b3dd33c..b02e420b 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -468,6 +468,40 @@ function runTests (solc, versionText) { st.end(); }); + t.test('compiling standard JSON (abstract contract)', function (st) { + // <0.1.6 doesn't have this + if (!solc.features.multipleInputs) { + st.skip('Not supported by solc'); + st.end(); + return; + } + + var isVersion6 = semver.gt(solc.semver(), '0.5.99'); + + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': [ 'evm.bytecode', 'evm.gasEstimates' ] + } + } + }, + 'sources': { + 'c.sol': { + 'content': (isVersion6 ? 'abstract ' : '') + 'contract C { function f() public; }' + } + } + }; + + var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectNoError(output)); + var C = getBytecodeStandard(output, 'c.sol', 'C'); + st.ok(typeof C === 'string'); + st.ok(C.length === 0); + st.end(); + }); + t.test('compiling standard JSON (with imports)', function (st) { // <0.2.1 doesn't have this if (!solc.features.importCallback) { From 4ab296fcb71e35444b3a3dfa76b8ee0453d7eab7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 21:10:10 +0100 Subject: [PATCH 020/153] Add tests for compiling single file with compileStandard --- test/compiler.js | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index b02e420b..ea82a2ca 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -421,7 +421,40 @@ function runTests (solc, versionText) { st.end(); }); - t.test('compiling standard JSON', function (st) { + t.test('compiling standard JSON (single file)', function (st) { + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': [ 'evm.bytecode', 'evm.gasEstimates' ] + } + } + }, + 'sources': { + 'c.sol': { + 'content': 'contract C { function g() public { } function h() internal {} }' + } + } + }; + + var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectNoError(output)); + var C = getBytecodeStandard(output, 'c.sol', 'C'); + st.ok(typeof C === 'string'); + st.ok(C.length > 0); + var CGas = getGasEstimate(output, 'c.sol', 'C'); + st.ok(typeof CGas === 'object'); + st.ok(typeof CGas['creation'] === 'object'); + st.ok(typeof CGas['creation']['codeDepositCost'] === 'string'); + st.ok(typeof CGas['external'] === 'object'); + st.ok(typeof CGas['external']['g()'] === 'string'); + st.ok(typeof CGas['internal'] === 'object'); + st.ok(typeof CGas['internal']['h()'] === 'string'); + st.end(); + }); + + t.test('compiling standard JSON (multiple files)', function (st) { // <0.1.6 doesn't have this if (!solc.features.multipleInputs) { st.skip('Not supported by solc'); From 19f7af2fad7376dc7dea81eab33be3437bdfb129 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 21:12:51 +0100 Subject: [PATCH 021/153] Add assertions to the linker --- linker.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linker.js b/linker.js index 6116aab3..b4f169ab 100644 --- a/linker.js +++ b/linker.js @@ -1,3 +1,4 @@ +var assert = require('assert'); var keccak256 = require('js-sha3').keccak256; function libraryHashPlaceholder (input) { @@ -5,6 +6,8 @@ function libraryHashPlaceholder (input) { } var linkBytecode = function (bytecode, libraries) { + assert(typeof bytecode === 'string'); + assert(typeof libraries === 'object'); // NOTE: for backwards compatibility support old compiler which didn't use file names var librariesComplete = {}; for (var libraryName in libraries) { @@ -52,6 +55,7 @@ var linkBytecode = function (bytecode, libraries) { }; var findLinkReferences = function (bytecode) { + assert(typeof bytecode === 'string'); // find 40 bytes in the pattern of __...<36 digits>...__ // e.g. __Lib.sol:L_____________________________ var linkReferences = {}; From cb852830ef59a3cbed4160a62fdedd906561d443 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 28 Nov 2019 21:16:32 +0100 Subject: [PATCH 022/153] Fix translator in case no bytecode was produced --- translate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/translate.js b/translate.js index 43e051ad..17dbc704 100644 --- a/translate.js +++ b/translate.js @@ -110,15 +110,15 @@ function translateJsonCompilerOutput (output, libraries) { 'evm': { 'legacyAssembly': contractInput['assembly'], 'bytecode': { - 'object': linker.linkBytecode(contractInput['bytecode'], libraries), + 'object': contractInput['bytecode'] && linker.linkBytecode(contractInput['bytecode'], libraries || {}), 'opcodes': contractInput['opcodes'], 'sourceMap': contractInput['srcmap'], - 'linkReferences': linker.findLinkReferences(contractInput['bytecode']) + 'linkReferences': contractInput['bytecode'] && linker.findLinkReferences(contractInput['bytecode']) }, 'deployedBytecode': { - 'object': linker.linkBytecode(contractInput['runtimeBytecode'], libraries), + 'object': contractInput['runtimeBytecode'] && linker.linkBytecode(contractInput['runtimeBytecode'], libraries || {}), 'sourceMap': contractInput['srcmapRuntime'], - 'linkReferences': linker.findLinkReferences(contractInput['runtimeBytecode']) + 'linkReferences': contractInput['runtimeBytecode'] && linker.findLinkReferences(contractInput['runtimeBytecode']) }, 'methodIdentifiers': contractInput['functionHashes'], 'gasEstimates': translatedGasEstimates From 3ad6ed1d4fcc7fdb609cf109b325464d5d10054f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 29 Nov 2019 18:49:09 +0100 Subject: [PATCH 023/153] Fix expectError helper in tests --- test/compiler.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index ea82a2ca..8875dbf5 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -55,9 +55,12 @@ function runTests (solc, versionText) { error = output.errors[error]; if (error.type === errorType) { if (message) { - return error.message.match(message) !== null; + if (error.message.match(message) !== null) { + return true; + } + } else { + return true; } - return true; } } } From 70775648a1e6306f44882dabd724dee78c96a515 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 29 Nov 2019 18:50:49 +0100 Subject: [PATCH 024/153] Add test case for forced warnings (only in >=0.4.0) --- test/compiler.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/compiler.js b/test/compiler.js index 8875dbf5..880d1352 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -676,6 +676,35 @@ function runTests (solc, versionText) { st.end(); }); + t.test('compiling standard JSON (with warning >=0.4.0)', function (st) { + // In 0.4.0 "pragma solidity" was added. Not including it is a warning. + if (semver.lt(solc.semver(), '0.4.0')) { + st.skip('Not supported by solc'); + st.end(); + return; + } + + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': [ 'evm.bytecode' ] + } + } + }, + 'sources': { + 'c.sol': { + 'content': 'contract C { function f() public { } }' + } + } + }; + + var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectError(output, 'Warning', 'Source file does not specify required compiler version!')); + st.end(); + }); + t.test('compiling standard JSON (using libraries) (using lowlevel API)', function (st) { // 0.4.0 has a bug with libraries if (semver.eq(solc.semver(), '0.4.0')) { From ec7c5da0686d1656b25389dd312a97ff3eb26486 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 9 Dec 2019 16:58:59 +0000 Subject: [PATCH 025/153] Fix test for abstract contracts on 0.6.0 --- test/compiler.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index ed201a9e..fc72d0d9 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -513,6 +513,12 @@ function runTests (solc, versionText) { } var isVersion6 = semver.gt(solc.semver(), '0.5.99'); + var source; + if (isVersion6) { + source = 'abstract contract C { function f() public virtual; }'; + } else { + source = 'contract C { function f() public; }'; + } var input = { 'language': 'Solidity', @@ -525,7 +531,7 @@ function runTests (solc, versionText) { }, 'sources': { 'c.sol': { - 'content': (isVersion6 ? 'abstract ' : '') + 'contract C { function f() public; }' + 'content': source } } }; From 7373354427653c1c3ac0b2ddc0e6bf493e49a69c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 26 Nov 2019 16:26:08 +0100 Subject: [PATCH 026/153] Add assertion that callback context is null --- wrapper.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrapper.js b/wrapper.js index 7439acd0..1a8e9ce5 100644 --- a/wrapper.js +++ b/wrapper.js @@ -61,6 +61,8 @@ function setupMethods (soljson) { var wrapCallbackWithKind = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); return function (context, kind, data, contents, error) { + // Must be a null pointer. + assert(context === 0, 'Callback context must be null.'); var result = callback(copyFromCString(kind), copyFromCString(data)); if (typeof result.contents === 'string') { copyToCString(result.contents, contents); From 25a7e09c0e36833f4fa2f727192161d1e6ae31d6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Dec 2019 19:16:42 +0100 Subject: [PATCH 027/153] Version 0.5.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d79502e..e5749aba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.5.13", + "version": "0.5.14", "description": "Solidity compiler", "main": "index.js", "bin": { From be3195fb61aa87a45744f77182b8d934d021f90f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 4 Dec 2019 18:42:46 +0100 Subject: [PATCH 028/153] Implement support for the new memory allocation API --- wrapper.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/wrapper.js b/wrapper.js index 1a8e9ce5..bee13d53 100644 --- a/wrapper.js +++ b/wrapper.js @@ -31,12 +31,30 @@ function setupMethods (soljson) { }; } + var alloc; + if ('_solidity_alloc' in soljson) { + alloc = soljson.cwrap('solidity_alloc', 'number', [ 'number' ]); + } else { + alloc = soljson._malloc; + assert(alloc, 'Expected malloc to be present.'); + } + + var reset; + if ('_solidity_reset' in soljson) { + reset = soljson.cwrap('solidity_reset', null, []); + } + var copyToCString = function (str, ptr) { var length = soljson.lengthBytesUTF8(str); // This is allocating memory using solc's allocator. - // Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. - // See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 - var buffer = soljson._malloc(length + 1); + // + // Before 0.6.0: + // Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. + // See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 + // + // After 0.6.0: + // The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end. + var buffer = alloc(length + 1); soljson.stringToUTF8(str, buffer, length + 1); soljson.setValue(ptr, buffer, '*'); }; @@ -136,6 +154,14 @@ function setupMethods (soljson) { throw e; } removeFunction(cb); + if (reset) { + // Explicitly free memory. + // + // NOTE: cwrap() of "compile" will copy the returned pointer into a + // Javascript string and it is not possible to call free() on it. + // reset() however will clear up all allocations. + reset(); + } return output; }; From 3e16176963d906583e529c241d050d34b8c87cf9 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 11 Dec 2019 18:37:05 +0100 Subject: [PATCH 029/153] Add smtCallback to README --- README.md | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0a91baa1..313aadfc 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,14 @@ There are two ways to use `solc`: The high-level API consists of a single method, `compile`, which expects the [Compiler Standard Input and Output JSON](https://solidity.readthedocs.io/en/v0.5.0/using-the-compiler.html#compiler-input-and-output-json-description). -It also accepts an optional callback function to resolve unmet dependencies. This callback receives a path and must synchronously return either an error or the content of the dependency as a string. -It cannot be used together with callback-based, asynchronous, filesystem access. A workaround is to collect the names of dependencies, return an error, and keep re-running the compiler until all -of them are resolved. +It also accepts an optional set of callback functions, which include the ``import`` and the ``smtSolver`` callbacks. +Starting 0.6.0 it only accepts an object in place of the callback to supply the callbacks. -Starting 0.5.12 it also accepts an object in place of the callback to supply different kind of callbacks, however only file imports are supported. +The ``import`` callback function is used to resolve unmet dependencies. +This callback receives a path and must synchronously return either an error or the content of the dependency +as a string. It cannot be used together with callback-based, asynchronous, +filesystem access. A workaround is to collect the names of dependencies, return +an error, and keep re-running the compiler until all of them are resolved. #### Example usage without the import callback @@ -115,10 +118,7 @@ function findImports(path) { else return { error: 'File not found' }; } -// Current syntax -var output = JSON.parse(solc.compile(JSON.stringify(input), findImports)); - -// New syntax (supported from 0.5.12) +// New syntax (supported from 0.5.12, mandatory from 0.6.0) var output = JSON.parse( solc.compile(JSON.stringify(input), { import: findImports }) ); @@ -133,6 +133,37 @@ for (var contractName in output.contracts['test.sol']) { } ``` +The ``smtSolver`` callback function is used to solve SMT queries generated by +Solidity's SMTChecker. If you have an SMT solver installed locally, it can +be used to solve the given queries, where the callback must synchronously +return either an error or the result from the solver. A default +``smtSolver`` callback is distributed by ``solc-js``, which relies on either +Z3 or CVC4 being installed locally. + +#### Example usage with smtSolver callback + +```javascript +var solc = require('solc'); +var smt = require('smtsolver'); +// Note that this example only works via node and not in the browser. + +var input = { + language: 'Solidity', + sources: { + 'test.sol': { + content: 'pragma experimental SMTChecker; contract C { function f(uint x) public { assert(x > 0); } }' + } + } +}; + +var output = JSON.parse( + solc.compile(JSON.stringify(input), { smtSolver: smt.smtSolver }) +); + +``` +The assertion is clearly false, and an ``assertion failure`` warning +should be returned. + #### Low-level API The low-level API is as follows: From 26802ccc5aef569f5981bc18a46c20533f0bb096 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Dec 2019 02:08:04 +0000 Subject: [PATCH 030/153] Support the version reported by 0.3.5 --- test/translate.js | 3 +++ translate.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/test/translate.js b/test/translate.js index c03bedd9..3d6cba9b 100644 --- a/test/translate.js +++ b/test/translate.js @@ -37,7 +37,10 @@ tape('Version string to Semver translator', function (t) { st.end(); }); t.test('Old style 0.3.5', function (st) { + // The one in the solc-bin list st.equal(versionToSemver('0.3.5-371690f0/Release-Emscripten/clang/Interpreter'), '0.3.5+commit.371690f0'); + // The actual one reported by the compiler + st.equal(versionToSemver('0.3.5-0/Release-Emscripten/clang/Interpreter'), '0.3.5'); st.end(); }); t.test('Old style 0.3.6', function (st) { diff --git a/translate.js b/translate.js index 17dbc704..5c4c76eb 100644 --- a/translate.js +++ b/translate.js @@ -3,6 +3,7 @@ var linker = require('./linker.js'); /// Translate old style version numbers to semver. /// Old style: 0.3.6-3fc68da5/Release-Emscripten/clang /// 0.3.5-371690f0/Release-Emscripten/clang/Interpreter +/// 0.3.5-0/Release-Emscripten/clang/Interpreter /// 0.2.0-e7098958/.-Emscripten/clang/int linked to libethereum-1.1.1-bbb80ab0/.-Emscripten/clang/int /// 0.1.3-0/.-/clang/int linked to libethereum-0.9.92-0/.-/clang/int /// 0.1.2-5c3bfd4b*/.-/clang/int @@ -17,6 +18,9 @@ function versionToSemver (version) { if (version.indexOf('0.1.3-0') !== -1) { return '0.1.3'; } + if (version.indexOf('0.3.5-0') !== -1) { + return '0.3.5'; + } // assume it is already semver compatible return version; } From c7b704290dcdb564f2fdbb436392b361e474eac2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Dec 2019 11:46:01 +0000 Subject: [PATCH 031/153] Support errors returned by 0.4.12 This makes the tests to work with all releases (so far). --- test/compiler.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index fc72d0d9..084d30da 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -145,7 +145,8 @@ function runTests (solc, versionText) { if ( output.errors[error].indexOf('ParserError') !== -1 || output.errors[error].indexOf('Error: Expected identifier') !== -1 || - output.errors[error].indexOf('Parser error: Expected identifier') !== -1 + output.errors[error].indexOf('Parser error: Expected identifier') !== -1 || + output.errors[error].indexOf(': Expected identifier') !== -1 // 0.4.12 ) { st.ok(true); } @@ -228,6 +229,10 @@ function runTests (solc, versionText) { // cont.sol:1:1: Error: Source "lib.sol" not found: File not found if (output.errors[error].indexOf('Error') !== -1 && output.errors[error].indexOf('File not found') !== -1) { st.ok(true); + } else if (output.errors[error].indexOf('not found: File not found') !== -1) { + // 0.4.12 had its own weird way: + // b.sol:1:1: : Source "a.sol" not found: File not found + st.ok(true); } } st.end(); @@ -292,6 +297,10 @@ function runTests (solc, versionText) { // cont.sol:1:1: Error: Source "lib.sol" not found: File import callback not supported if (output.errors[error].indexOf('Error') !== -1 && output.errors[error].indexOf('File import callback not supported') !== -1) { st.ok(true); + } else if (output.errors[error].indexOf('not found: File import callback not supported') !== -1) { + // 0.4.12 had its own weird way: + // b.sol:1:1: : Source "a.sol" not found: File import callback not supported + st.ok(true); } } st.end(); @@ -799,6 +808,7 @@ const versions = [ 'v0.3.6+commit.3fc68da', 'v0.4.0+commit.acd334c9', 'v0.4.11+commit.68ef5810', + 'v0.4.12+commit.194ff033', 'v0.4.26+commit.4563c3fc' ]; for (var version in versions) { From 418f27b654a71ade1294840e728509d220aafbca Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Dec 2019 20:32:36 +0100 Subject: [PATCH 032/153] Set version to 0.5.15. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5749aba..ae7b4842 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.5.14", + "version": "0.5.15", "description": "Solidity compiler", "main": "index.js", "bin": { From 8e106ce853cdab7e44532523238baf263dbffbb8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Dec 2019 23:58:51 +0100 Subject: [PATCH 033/153] Solidity 0.6.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae7b4842..0f7b6a23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.5.15", + "version": "0.6.0", "description": "Solidity compiler", "main": "index.js", "bin": { From b9b1136bc045efe92577855c8a1f365b1ad883ec Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 2 Jan 2020 23:29:37 +0100 Subject: [PATCH 034/153] Version 0.5.16 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae7b4842..44b67f49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.5.15", + "version": "0.5.16", "description": "Solidity compiler", "main": "index.js", "bin": { From e8f49299fbcbfc107cfb702889dd5f4ba1858ca6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 3 Jan 2020 13:21:50 +0100 Subject: [PATCH 035/153] Set version to 0.6.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f7b6a23..00433b31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.0", + "version": "0.6.1", "description": "Solidity compiler", "main": "index.js", "bin": { From 1707742684bb9a2098ec748e0ce7de2533636469 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 27 Jan 2020 16:38:18 +0100 Subject: [PATCH 036/153] Version 0.6.2. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 00433b31..ef2079fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.1", + "version": "0.6.2", "description": "Solidity compiler", "main": "index.js", "bin": { From 60dfcf3e74cdbfa5197537f99ec6c0e5771c21b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 27 Jan 2020 18:17:55 +0100 Subject: [PATCH 037/153] Disable tests for node v4 and node v6. --- .circleci/config.yml | 2 -- package.json | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c2e6caef..098ccfc7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,8 +2,6 @@ workflows: version: 2.1 node-multi-build: jobs: - - node-v4 - - node-v6 - node-v8 - node-v10 - node-v12: diff --git a/package.json b/package.json index ef2079fc..102a5a68 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,9 @@ "solidity", "compiler" ], + "engines": { + "node": ">=8.0.0" + }, "files": [ "abi.js", "index.js", From 9309f5aecaaa29613b90ab524e85989a020e2e64 Mon Sep 17 00:00:00 2001 From: Jonny Huxtable Date: Tue, 18 Feb 2020 10:59:26 +0000 Subject: [PATCH 038/153] Use GitHub raw URLs to avoid rate limits on remote download --- downloadCurrentVersion.js | 4 ++-- wrapper.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 wrapper.js diff --git a/downloadCurrentVersion.js b/downloadCurrentVersion.js index ee49b4f8..693ffbbe 100755 --- a/downloadCurrentVersion.js +++ b/downloadCurrentVersion.js @@ -13,7 +13,7 @@ function getVersionList (cb) { console.log('Retrieving available version list...'); var mem = new MemoryStream(null, { readable: false }); - https.get('https://ethereum.github.io/solc-bin/bin/list.json', function (response) { + https.get('https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.json', function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); process.exit(1); @@ -40,7 +40,7 @@ function downloadBinary (outputName, version, expectedHash) { }); var file = fs.createWriteStream(outputName, { encoding: 'binary' }); - https.get('https://ethereum.github.io/solc-bin/bin/' + version, function (response) { + https.get('https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/' + version, function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); process.exit(1); diff --git a/wrapper.js b/wrapper.js old mode 100644 new mode 100755 index bee13d53..f1a40c63 --- a/wrapper.js +++ b/wrapper.js @@ -328,7 +328,7 @@ function setupMethods (soljson) { // instead of from the local filesystem. loadRemoteVersion: function (versionString, cb) { var mem = new MemoryStream(null, {readable: false}); - var url = 'https://ethereum.github.io/solc-bin/bin/soljson-' + versionString + '.js'; + var url = 'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/soljson-' + versionString + '.js'; https.get(url, function (response) { if (response.statusCode !== 200) { cb(new Error('Error retrieving binary: ' + response.statusMessage)); From 755a180887e21a893a3b7b47c4bcf452d8b6bd2a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Feb 2020 21:33:45 +0100 Subject: [PATCH 039/153] Set version to 0.6.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 102a5a68..0b90d6c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.2", + "version": "0.6.3", "description": "Solidity compiler", "main": "index.js", "bin": { From 88de236b9151f9cee03beae714210dcf8664bc3b Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 9 Mar 2020 12:53:06 +0100 Subject: [PATCH 040/153] Do not hard fail if one of the tests exceeds limits --- test/smtcallback.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/smtcallback.js b/test/smtcallback.js index cd00d70d..4d4241b9 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -31,6 +31,9 @@ function expectErrors (errors, expectations) { } for (var i in errors) { + if (errors[i].includes('Error trying to invoke SMT solver') || expectations[i].includes('Error trying to invoke SMT solver')) { + continue; + } if (!errors[i].includes(expectations[i])) { return false; } From 75708a594e32234540f074b981cac699e2a7fa4b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2020 20:21:40 +0100 Subject: [PATCH 041/153] Version 0.6.4. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b90d6c6..f909fc02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.3", + "version": "0.6.4", "description": "Solidity compiler", "main": "index.js", "bin": { From d80b953ec65975d74f92c1253c3f6df09e06b555 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Mar 2020 18:20:46 +0100 Subject: [PATCH 042/153] Version 0.5.17 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44b67f49..f1c19039 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.5.16", + "version": "0.5.17", "description": "Solidity compiler", "main": "index.js", "bin": { From 83bec3ab3eac170a79582d6d1879a751f3e040be Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 6 Apr 2020 17:09:34 +0200 Subject: [PATCH 043/153] Version 0.6.5. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f909fc02..7fec1323 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.4", + "version": "0.6.5", "description": "Solidity compiler", "main": "index.js", "bin": { From 177fd3980ba9f171faa853b2ad0c5d51b7fe9d2a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 9 Apr 2020 16:34:08 +0200 Subject: [PATCH 044/153] Version 0.6.6. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fec1323..29baac4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.5", + "version": "0.6.6", "description": "Solidity compiler", "main": "index.js", "bin": { From 75d5eff3ce322347fee021cc61714bfc2779dbc2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 May 2020 17:50:37 +0200 Subject: [PATCH 045/153] Set version to 0.6.7. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29baac4c..38ca9cd1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.6", + "version": "0.6.7", "description": "Solidity compiler", "main": "index.js", "bin": { From 5f692288d832633b71b11bea43492a42a902ee20 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 May 2020 20:06:55 +0200 Subject: [PATCH 046/153] Add spdx version identifier during tests. --- test/smtcallback.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/smtcallback.js b/test/smtcallback.js index 4d4241b9..b48d8336 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -6,7 +6,7 @@ const solc = require('../index.js'); const smtchecker = require('../smtchecker.js'); const smtsolver = require('../smtsolver.js'); -var pragmaSol = 'pragma solidity >=0.0;\n'; +var preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; var pragmaSMT = 'pragma experimental SMTChecker;\n'; function collectErrors (solOutput) { @@ -60,7 +60,7 @@ tape('SMTCheckerCallback', function (t) { return { error: 'Fake SMT solver error.' }; }; - var input = { 'a': { content: pragmaSol + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } }; + var input = { 'a': { content: preamble + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } }; var inputJSON = JSON.stringify({ language: 'Solidity', sources: input @@ -130,7 +130,7 @@ tape('SMTCheckerCallback', function (t) { } tests[sources[i]] = { expectations: expected, - solidity: { test: { content: pragmaSol + source } } + solidity: { test: { content: preamble + source } } }; } From 9f5f255e160c0dcbf2606639a39c18dcf70d0398 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 14 May 2020 15:32:40 +0200 Subject: [PATCH 047/153] Set version to 0.6.8. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 38ca9cd1..cdcefc5a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.7", + "version": "0.6.8", "description": "Solidity compiler", "main": "index.js", "bin": { From 565b3ff95bcc8acbd16ad9a1482d1174e6c30ed4 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 14 May 2020 18:15:02 +0200 Subject: [PATCH 048/153] Fix tests for Solidity 0.6.9 which contains z3 --- test/smtcallback.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/smtcallback.js b/test/smtcallback.js index b48d8336..c5ae1fd7 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -65,11 +65,23 @@ tape('SMTCheckerCallback', function (t) { language: 'Solidity', sources: input }); - var tests = [ - { cb: satCallback, expectations: ['Assertion violation happens here'] }, - { cb: unsatCallback, expectations: [] }, - { cb: errorCallback, expectations: ['BMC analysis was not possible'] } - ]; + + // Solidity 0.6.9 comes with z3 + var tests; + if (!semver.gt(solc.semver(), '0.6.8')) { + tests = [ + { cb: satCallback, expectations: ['Assertion violation happens here'] }, + { cb: unsatCallback, expectations: [] }, + { cb: errorCallback, expectations: ['BMC analysis was not possible'] } + ]; + } else { + tests = [ + { cb: satCallback, expectations: ['Assertion violation happens here'] }, + { cb: unsatCallback, expectations: ['At least two SMT solvers provided conflicting answers. Results might not be sound.'] }, + { cb: errorCallback, expectations: ['Assertion violation happens here'] } + ]; + } + for (var i in tests) { var test = tests[i]; var output = JSON.parse(solc.compile( From 16ac5ab6a8f8ccca2a36028e4341a591e708d0b0 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 4 Jun 2020 11:13:21 +0200 Subject: [PATCH 049/153] Pass the wasm signature to addFunction. --- wrapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper.js b/wrapper.js index f1a40c63..d71a5acd 100755 --- a/wrapper.js +++ b/wrapper.js @@ -140,7 +140,7 @@ function setupMethods (soljson) { var addFunction = soljson.addFunction || soljson.Runtime.addFunction; var removeFunction = soljson.removeFunction || soljson.Runtime.removeFunction; - var cb = addFunction(singleCallback); + var cb = addFunction(singleCallback, 'viiiii'); var output; try { args.push(cb); From 4f5a09c769adcd735419c13b08896f9a82b18830 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jun 2020 13:33:14 +0200 Subject: [PATCH 050/153] Add default file read callback. --- README.md | 9 ++++++-- solcjs | 31 ++++++++++++++++++++++++---- test/cli.js | 42 ++++++++++++++++++++++++++++++++++++++ test/resources/importA.sol | 7 +++++++ test/resources/importB.sol | 5 +++++ 5 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 test/resources/importA.sol create mode 100644 test/resources/importB.sol diff --git a/README.md b/README.md index 313aadfc..9adfa17d 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,17 @@ To see all the supported features, execute: solcjs --help ``` +To compile a contract that imports other contracts via relative paths: +```bash +solcjs --bin --base-path . ./MainContract.sol +``` +The option ``--base-path`` enables automatic loading of imports from the filesystem and +takes a path as argument that contains the source files. + Note: this commandline interface is not compatible with `solc` provided by the Solidity compiler package and thus cannot be used in combination with an Ethereum client via the `eth.compile.solidity()` RPC method. Please refer to the [Solidity compiler documentation](https://solidity.readthedocs.io/) for instructions to install `solc`. Furthermore, the commandline interface to solc-js provides fewer features than the binary release. -One of the missing features is automatic loading of files from the filesystem if they are not explicitly -mentioned on the command line. ### Usage in Projects diff --git a/solcjs b/solcjs index a09ac370..e42353bb 100755 --- a/solcjs +++ b/solcjs @@ -22,6 +22,7 @@ program .option('--bin', 'Binary of the contracts in hex.') .option('--abi', 'ABI of the contracts.') .option('--standard-json', 'Turn on Standard JSON Input / Output mode.') + .option('--base-path ', 'Automatically resolve all imports inside the given path.') .option('-o, --output-dir ', 'Output directory for the contracts.'); program.parse(process.argv); @@ -33,14 +34,36 @@ function abort (msg) { process.exit(1); } +function readFileCallback(path) { + path = program.basePath + '/' + path; + if (fs.existsSync(path)) { + try { + return { 'contents': fs.readFileSync(path).toString('utf8') } + } catch (e) { + return { error: 'Error reading ' + path + ': ' + e }; + } + } else + return { error: 'File not found at ' + path} +} +function stripBasePath(path) { + if (program.basePath && path.startsWith(program.basePath)) + return path.slice(program.basePath.length); + else + return path; +} + +var callbacks = undefined +if (program.basePath) + callbacks = {'import': readFileCallback}; + if (program.standardJson) { var input = fs.readFileSync(process.stdin.fd).toString('utf8'); - var output = solc.compile(input); + var output = solc.compile(input, callbacks); try { var inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver); if (inputJSON) { - output = solc.compile(JSON.stringify(inputJSON)); + output = solc.compile(JSON.stringify(inputJSON), callbacks); } } catch (e) { @@ -74,7 +97,7 @@ var sources = {}; for (var i = 0; i < files.length; i++) { try { - sources[ files[i] ] = { content: fs.readFileSync(files[i]).toString() }; + sources[stripBasePath(files[i])] = { content: fs.readFileSync(files[i]).toString() }; } catch (e) { abort('Error reading ' + files[i] + ': ' + e); } @@ -93,7 +116,7 @@ var output = JSON.parse(solc.compile(JSON.stringify({ } }, sources: sources -}))); +}), callbacks)); let hasError = false; diff --git a/test/cli.js b/test/cli.js index fb0fbd67..edbad952 100644 --- a/test/cli.js +++ b/test/cli.js @@ -62,6 +62,19 @@ tape('CLI', function (t) { spt.end(); }); + t.test('no-base-path', function (st) { + var spt = spawn(st, './solcjs --bin test/resources/importA.sol'); + spt.stderr.match(/not found: File import callback not supported/); + spt.end(); + }); + + t.test('base-path', function (st) { + var spt = spawn(st, './solcjs --bin --base-path test/resources test/resources/importA.sol'); + spt.stderr.empty(); + spt.succeeds(); + spt.end(); + }); + t.test('standard json', function (st) { var input = { 'language': 'Solidity', @@ -90,4 +103,33 @@ tape('CLI', function (t) { spt.end(); }); }); + + t.test('standard json base path', function (st) { + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': [ 'metadata' ] + } + } + }, + 'sources': { + 'importA.sol': { + 'content': 'import "./importB.sol";' + } + } + }; + var spt = spawn(st, './solcjs --standard-json --base-path test/resources'); + spt.stdin.setEncoding('utf-8'); + spt.stdin.write(JSON.stringify(input)); + spt.stdin.end(); + spt.stdin.on('finish', function () { + spt.stderr.empty(); + console.log(spt.stdout); + spt.stdout.match(/{"contracts":{"importB.sol":{"D":{"metadata":/); + spt.succeeds(); + spt.end(); + }); + }); }); diff --git a/test/resources/importA.sol b/test/resources/importA.sol new file mode 100644 index 00000000..b0193e9e --- /dev/null +++ b/test/resources/importA.sol @@ -0,0 +1,7 @@ +import "./importB.sol"; + +contract C { + function f() public returns (uint) { + return 0; + } +} diff --git a/test/resources/importB.sol b/test/resources/importB.sol new file mode 100644 index 00000000..c1d8f321 --- /dev/null +++ b/test/resources/importB.sol @@ -0,0 +1,5 @@ +contract D { + function f() public returns (uint) { + return 0; + } +} From dc476efac284b3bb29f2e0ee56ce619c05f2ce0b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jun 2020 18:18:34 +0200 Subject: [PATCH 051/153] Solidity 0.6.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cdcefc5a..cfd237ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.8", + "version": "0.6.9", "description": "Solidity compiler", "main": "index.js", "bin": { From b43993bd768565b585725671524412006afc99bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jun 2020 18:55:05 +0200 Subject: [PATCH 052/153] Remove logging statement. --- test/cli.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/cli.js b/test/cli.js index edbad952..4e93d9ea 100644 --- a/test/cli.js +++ b/test/cli.js @@ -126,7 +126,6 @@ tape('CLI', function (t) { spt.stdin.end(); spt.stdin.on('finish', function () { spt.stderr.empty(); - console.log(spt.stdout); spt.stdout.match(/{"contracts":{"importB.sol":{"D":{"metadata":/); spt.succeeds(); spt.end(); From 30e9b8270e0cdebd2744af048418f89df5b6c4bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 11 Jun 2020 20:24:54 +0200 Subject: [PATCH 053/153] Set version to 0.6.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cfd237ba..5e16766e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.9", + "version": "0.6.10", "description": "Solidity compiler", "main": "index.js", "bin": { From 6a0037313c8ca56571d8dcbc0f07bcd4953e54f2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 7 Jul 2020 18:47:47 +0200 Subject: [PATCH 054/153] Set version to 0.6.11. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e16766e..4f1f1c14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.10", + "version": "0.6.11", "description": "Solidity compiler", "main": "index.js", "bin": { From 7e694f7962cda38faf84d7f421df972fa3bd516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 20 Jul 2020 19:13:42 +0200 Subject: [PATCH 055/153] Use ethereum.org domain rather than github when downloading files from solc-bin --- downloadCurrentVersion.js | 4 ++-- test/compiler.js | 2 +- wrapper.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/downloadCurrentVersion.js b/downloadCurrentVersion.js index 693ffbbe..c4ac5d67 100755 --- a/downloadCurrentVersion.js +++ b/downloadCurrentVersion.js @@ -13,7 +13,7 @@ function getVersionList (cb) { console.log('Retrieving available version list...'); var mem = new MemoryStream(null, { readable: false }); - https.get('https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.json', function (response) { + https.get('https://solc-bin.ethereum.org/bin/list.json', function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); process.exit(1); @@ -40,7 +40,7 @@ function downloadBinary (outputName, version, expectedHash) { }); var file = fs.createWriteStream(outputName, { encoding: 'binary' }); - https.get('https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/' + version, function (response) { + https.get('https://solc-bin.ethereum.org/bin/' + version, function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); process.exit(1); diff --git a/test/compiler.js b/test/compiler.js index 084d30da..3ba696c7 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -813,7 +813,7 @@ const versions = [ ]; for (var version in versions) { version = versions[version]; - execSync(`curl -o /tmp/${version}.js https://ethereum.github.io/solc-bin/bin/soljson-${version}.js`); + execSync(`curl -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); runTests(newSolc, version); } diff --git a/wrapper.js b/wrapper.js index d71a5acd..b616e016 100755 --- a/wrapper.js +++ b/wrapper.js @@ -328,7 +328,7 @@ function setupMethods (soljson) { // instead of from the local filesystem. loadRemoteVersion: function (versionString, cb) { var mem = new MemoryStream(null, {readable: false}); - var url = 'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/soljson-' + versionString + '.js'; + var url = 'https://solc-bin.ethereum.org/bin/soljson-' + versionString + '.js'; https.get(url, function (response) { if (response.statusCode !== 200) { cb(new Error('Error retrieving binary: ' + response.statusMessage)); From 53fc95ba9887eddd9243d1094eb2e8087e5b1e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 20 Jul 2020 19:14:52 +0200 Subject: [PATCH 056/153] Follow redirects when downloading solc-bin files with curl - NOTE: This is only for curl. https.get() used in three other places won't follow redirects. --- test/compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index 3ba696c7..e65c9604 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -813,7 +813,7 @@ const versions = [ ]; for (var version in versions) { version = versions[version]; - execSync(`curl -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); + execSync(`curl -L -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); runTests(newSolc, version); } From ebfd976188e721f7e606609594454eb47bb3166c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 22 Jul 2020 18:42:41 +0200 Subject: [PATCH 057/153] Version 0.6.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f1f1c14..e5b4316a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.11", + "version": "0.6.12", "description": "Solidity compiler", "main": "index.js", "bin": { From 68907e69954d909fb125abe2e76b29029cf3e289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 23 Jul 2020 11:43:00 +0200 Subject: [PATCH 058/153] Add a dependency on follow-redirects and use it when downloading files from solc-bin --- downloadCurrentVersion.js | 2 +- package.json | 3 ++- wrapper.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/downloadCurrentVersion.js b/downloadCurrentVersion.js index c4ac5d67..79133766 100755 --- a/downloadCurrentVersion.js +++ b/downloadCurrentVersion.js @@ -5,7 +5,7 @@ var pkg = require('./package.json'); var fs = require('fs'); -var https = require('https'); +var https = require('follow-redirects').https; var MemoryStream = require('memorystream'); var keccak256 = require('js-sha3').keccak256; diff --git a/package.json b/package.json index e5b4316a..c4a79090 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "memorystream": "^0.3.1", "require-from-string": "^2.0.0", "semver": "^5.5.0", - "tmp": "0.0.33" + "tmp": "0.0.33", + "follow-redirects": "^1.12.1" }, "devDependencies": { "coveralls": "^3.0.0", diff --git a/wrapper.js b/wrapper.js index b616e016..a41f9221 100755 --- a/wrapper.js +++ b/wrapper.js @@ -1,7 +1,7 @@ var assert = require('assert'); var translate = require('./translate.js'); var requireFromString = require('require-from-string'); -var https = require('https'); +var https = require('follow-redirects').https; var MemoryStream = require('memorystream'); var semver = require('semver'); From 3ea7e27aa8343b43f046239a486020ad1ac98796 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 23 Jul 2020 13:01:23 +0200 Subject: [PATCH 059/153] From 0.7.0 on CHC reports assertion failures without considering the callback --- test/smtcallback.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/smtcallback.js b/test/smtcallback.js index c5ae1fd7..913a00dd 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -66,20 +66,28 @@ tape('SMTCheckerCallback', function (t) { sources: input }); - // Solidity 0.6.9 comes with z3 var tests; if (!semver.gt(solc.semver(), '0.6.8')) { + // Up to version 0.6.8 there were no embedded solvers. tests = [ { cb: satCallback, expectations: ['Assertion violation happens here'] }, { cb: unsatCallback, expectations: [] }, { cb: errorCallback, expectations: ['BMC analysis was not possible'] } ]; - } else { + } else if (!semver.gt(solc.semver(), '0.6.12')) { + // Solidity 0.6.9 comes with z3. tests = [ { cb: satCallback, expectations: ['Assertion violation happens here'] }, { cb: unsatCallback, expectations: ['At least two SMT solvers provided conflicting answers. Results might not be sound.'] }, { cb: errorCallback, expectations: ['Assertion violation happens here'] } ]; + } else { + // Solidity 0.7.0 reports assertion violations via CHC. + tests = [ + { cb: satCallback, expectations: ['Assertion violation happens here'] }, + { cb: unsatCallback, expectations: ['Assertion violation happens here'] }, + { cb: errorCallback, expectations: ['Assertion violation happens here'] } + ]; } for (var i in tests) { From d68ad818c5055b2f58a2608ac29ee27d3fdb29e9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Jul 2020 16:39:21 +0200 Subject: [PATCH 060/153] Solidity 0.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c4a79090..03ccfbec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.6.12", + "version": "0.7.0", "description": "Solidity compiler", "main": "index.js", "bin": { From ff0d6f9ee92f79eec0454be0ba5aea82276951db Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 10 Aug 2020 10:44:22 +0200 Subject: [PATCH 061/153] Fix test expectation --- test/smtcallback.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/smtcallback.js b/test/smtcallback.js index 913a00dd..7bab0829 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -34,6 +34,7 @@ function expectErrors (errors, expectations) { if (errors[i].includes('Error trying to invoke SMT solver') || expectations[i].includes('Error trying to invoke SMT solver')) { continue; } + expectations[i] = expectations[i].replace('happens here.', 'happens here'); if (!errors[i].includes(expectations[i])) { return false; } From acee3a52ee0de1fec10a2aabbb2072cbcd2be551 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 12 Aug 2020 17:19:23 +0200 Subject: [PATCH 062/153] Don't check SMTChecker test if timeout --- test/smtcallback.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/smtcallback.js b/test/smtcallback.js index 7bab0829..e1b08589 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -158,6 +158,15 @@ tape('SMTCheckerCallback', function (t) { // Run all tests for (i in tests) { var test = tests[i]; + + // Z3's nondeterminism sometimes causes a test to timeout in one context but not in the other, + // so if we see timeout we skip a potentially misleading run. + var findError = (errorMsg) => { return errorMsg.includes('Error trying to invoke SMT solver'); }; + if (test.expectations.find(findError) !== undefined) { + st.skip('Test contains timeout which may have been caused by nondeterminism.'); + continue; + } + var output = JSON.parse(solc.compile( JSON.stringify({ language: 'Solidity', @@ -180,6 +189,11 @@ tape('SMTCheckerCallback', function (t) { continue; } + if (test.errors.find(findError) !== undefined) { + st.skip('Test contains timeout which may have been caused by nondeterminism.'); + continue; + } + // Compare expected vs obtained errors st.ok(expectErrors(test.expectations, test.errors)); } From 972e5f629bfa449ed14f9ce0b8a914148a8471d9 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 17 Aug 2020 11:41:45 +0200 Subject: [PATCH 063/153] Use the same z3 options as Solidity does --- smtsolver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smtsolver.js b/smtsolver.js index be9862f8..a38030ac 100644 --- a/smtsolver.js +++ b/smtsolver.js @@ -8,7 +8,7 @@ const timeout = 10000; var potentialSolvers = [ { name: 'z3', - params: '-smt2 -t:' + timeout + params: '-smt2 rlimit=20000000 rewriter.pull_cheap_ite=true fp.spacer.q3.use_qgen=true fp.spacer.mbqi=false fp.spacer.ground_pobs=false' }, { name: 'cvc4', @@ -33,7 +33,7 @@ function solve (query) { try { solverOutput = execSync( solvers[0].name + ' ' + solvers[0].params + ' ' + tmpFile.name, { - timeout: 10000 + stdio: 'pipe' } ).toString(); } catch (e) { From 1199db23c173768423dd349886043c75c98b1f3f Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 2 Sep 2020 18:07:42 +0200 Subject: [PATCH 064/153] Version 0.7.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03ccfbec..cbbbc81a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.0", + "version": "0.7.1", "description": "Solidity compiler", "main": "index.js", "bin": { From b2bdfce14fa2f157d280500e32f71f97cccdedd6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 28 Sep 2020 22:08:45 +0200 Subject: [PATCH 065/153] Solidity 0.7.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cbbbc81a..ed495cdf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.1", + "version": "0.7.2", "description": "Solidity compiler", "main": "index.js", "bin": { From ab9831695c38700e482acebea6cd9d42278020ed Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 7 Oct 2020 18:46:32 +0200 Subject: [PATCH 066/153] Set version to 0.7.3. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed495cdf..da5e54d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.2", + "version": "0.7.3", "description": "Solidity compiler", "main": "index.js", "bin": { From affd90bcb019d8938608f3d5712377639c4591b4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Oct 2020 17:34:22 +0200 Subject: [PATCH 067/153] Set version to 0.7.4. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da5e54d8..297f23d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.3", + "version": "0.7.4", "description": "Solidity compiler", "main": "index.js", "bin": { From af77be0d7a3a9dc9ab884a8be3a79636d916fa60 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 6 Nov 2020 16:16:43 +0000 Subject: [PATCH 068/153] Skip SMT tests that require specific engine --- test/smtcallback.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/smtcallback.js b/test/smtcallback.js index e1b08589..dca5e3f8 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -140,6 +140,12 @@ tape('SMTCheckerCallback', function (t) { for (i in sources) { st.comment('Collecting ' + sources[i] + '...'); var source = fs.readFileSync(sources[i], 'utf8'); + + if (source.includes('// SMTEngine')) { + st.skip('Test requires specific SMTChecker engine.'); + continue; + } + var expected = []; var delimiter = '// ----'; if (source.includes(delimiter)) { From caa550a8e299444b42f7621ceb3579639311ff0b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Nov 2020 14:05:45 +0000 Subject: [PATCH 069/153] Add more tests to the ABI translator --- test/abi.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/abi.js b/test/abi.js index 90c05c95..bf816bbd 100644 --- a/test/abi.js +++ b/test/abi.js @@ -15,11 +15,16 @@ tape('ABI translator', function (t) { st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); st.end(); }); - t.test('0.3.6 (function)', function (st) { + t.test('0.3.6 (non-constant function)', function (st) { var input = [ { inputs: [], type: 'function' } ]; st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); st.end(); }); + t.test('0.3.6 (constant function)', function (st) { + var input = [ { inputs: [], type: 'function', constant: true } ]; + st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], payable: true, constant: true, stateMutability: 'payable', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + st.end(); + }); t.test('0.3.6 (event)', function (st) { var input = [ { inputs: [], type: 'event' } ]; st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], type: 'event' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); @@ -35,11 +40,21 @@ tape('ABI translator', function (t) { st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], type: 'constructor', payable: true, stateMutability: 'payable' }, { type: 'fallback', stateMutability: 'nonpayable' } ]); st.end(); }); + t.test('0.4.0 (non-constant function)', function (st) { + var input = [ { inputs: [], type: 'function' } ]; + st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], stateMutability: 'nonpayable', type: 'function' } ]); + st.end(); + }); t.test('0.4.0 (constant function)', function (st) { var input = [ { inputs: [], type: 'function', constant: true } ]; st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], constant: true, stateMutability: 'view', type: 'function' } ]); st.end(); }); + t.test('0.4.0 (payable function)', function (st) { + var input = [ { inputs: [], payable: true, type: 'function' } ]; + st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'function' } ]); + st.end(); + }); t.test('0.4.1 (constructor not payable)', function (st) { var input = [ { inputs: [], payable: false, type: 'constructor' } ]; st.deepEqual(abi.update('0.4.1', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' } ]); From dd6874c34449d97c20c46e440120ad27072cd29f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Nov 2020 14:06:36 +0000 Subject: [PATCH 070/153] Do not translate constant functions from <0.4.0 as payable --- abi.js | 4 ++-- test/abi.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/abi.js b/abi.js index d0ca6a4c..7834a39a 100644 --- a/abi.js +++ b/abi.js @@ -19,8 +19,8 @@ function update (compilerVersion, abi) { } if (item.type !== 'event') { - // add 'payable' to everything - if (semver.lt(compilerVersion, '0.4.0')) { + // add 'payable' to everything, except constant functions + if (!item.constant && semver.lt(compilerVersion, '0.4.0')) { item.payable = true; } diff --git a/test/abi.js b/test/abi.js index bf816bbd..06f555e3 100644 --- a/test/abi.js +++ b/test/abi.js @@ -22,7 +22,7 @@ tape('ABI translator', function (t) { }); t.test('0.3.6 (constant function)', function (st) { var input = [ { inputs: [], type: 'function', constant: true } ]; - st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], payable: true, constant: true, stateMutability: 'payable', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], constant: true, stateMutability: 'view', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); st.end(); }); t.test('0.3.6 (event)', function (st) { From 2282779e613348ecfded7f1ba5894c2aca8d89a5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Nov 2020 15:39:41 +0100 Subject: [PATCH 071/153] Set version to 0.7.5. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 297f23d3..1676904a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.4", + "version": "0.7.5", "description": "Solidity compiler", "main": "index.js", "bin": { From 18ca1f6f4fe94a6c8426ee61289f55ee0907690d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 1 Dec 2020 17:29:17 +0000 Subject: [PATCH 072/153] Make CLI incorrectSource test less picky about error formatting --- test/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cli.js b/test/cli.js index 4e93d9ea..e6b610d6 100644 --- a/test/cli.js +++ b/test/cli.js @@ -44,7 +44,7 @@ tape('CLI', function (t) { t.test('incorrect source source', function (st) { var spt = spawn(st, './solcjs --bin test/resources/fixtureIncorrectSource.sol'); - spt.stderr.match(/^test\/resources\/fixtureIncorrectSource.sol:1:1: SyntaxError: Invalid pragma "contract"/); + spt.stderr.match(/SyntaxError: Invalid pragma "contract"/); spt.end(); }); From 0df18f668d610507d29d5f983e2b1d3b03e8b900 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 4 Dec 2020 14:00:11 +0100 Subject: [PATCH 073/153] Prepare SMT comparison for counterexamples --- test/smtcallback.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/smtcallback.js b/test/smtcallback.js index dca5e3f8..763aa509 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -25,7 +25,7 @@ function collectErrors (solOutput) { return errors; } -function expectErrors (errors, expectations) { +function expectErrors (expectations, errors, ignoreCex) { if (errors.length !== expectations.length) { return false; } @@ -34,8 +34,19 @@ function expectErrors (errors, expectations) { if (errors[i].includes('Error trying to invoke SMT solver') || expectations[i].includes('Error trying to invoke SMT solver')) { continue; } - expectations[i] = expectations[i].replace('happens here.', 'happens here'); - if (!errors[i].includes(expectations[i])) { + // Expectations containing counterexamples might have many '\n' in a single line. + // These are stored escaped in the test format (as '\\n'), whereas the actual error from the compiler has '\n'. + // Therefore we need to replace '\\n' by '\n' in the expectations. + // Function `replace` only replaces the first occurrence, and `replaceAll` is not standard yet. + // Replace all '\\n' by '\n' via split & join. + expectations[i] = expectations[i].split('\\n').join('\n'); + if (ignoreCex) { + expectations[i] = expectations[i].split('\nCounterexample')[0]; + errors[i] = errors[i].split('\nCounterexample')[0]; + } + // `expectations` have "// Warning ... " before the actual message, + // whereas `errors` have only the message. + if (!expectations[i].includes(errors[i])) { return false; } } @@ -157,7 +168,8 @@ tape('SMTCheckerCallback', function (t) { } tests[sources[i]] = { expectations: expected, - solidity: { test: { content: preamble + source } } + solidity: { test: { content: preamble + source } }, + ignoreCex: source.includes('// SMTIgnoreCex: yes') }; } @@ -201,7 +213,7 @@ tape('SMTCheckerCallback', function (t) { } // Compare expected vs obtained errors - st.ok(expectErrors(test.expectations, test.errors)); + st.ok(expectErrors(test.expectations, test.errors, test.ignoreCex)); } st.end(); From ff6caffa958e0f38b22f992040a1e1ac71d5d9c6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Dec 2020 17:23:59 +0100 Subject: [PATCH 074/153] Set version to 0.7.6. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1676904a..b75455f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.5", + "version": "0.7.6", "description": "Solidity compiler", "main": "index.js", "bin": { From e74b9f468190172ea991ae5c749378be2d83001d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Dec 2020 18:46:31 +0100 Subject: [PATCH 075/153] Solidity 0.8.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b75455f5..8c88573a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.7.6", + "version": "0.8.0", "description": "Solidity compiler", "main": "index.js", "bin": { From 8e9b96973471d5818a1a14541618dcf4135e5b50 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 Jan 2021 09:09:30 +0100 Subject: [PATCH 076/153] Use correct url for binaries. --- wrapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper.js b/wrapper.js index a41f9221..e337399e 100755 --- a/wrapper.js +++ b/wrapper.js @@ -328,7 +328,7 @@ function setupMethods (soljson) { // instead of from the local filesystem. loadRemoteVersion: function (versionString, cb) { var mem = new MemoryStream(null, {readable: false}); - var url = 'https://solc-bin.ethereum.org/bin/soljson-' + versionString + '.js'; + var url = 'https://binaries.soliditylang.org/bin/soljson-' + versionString + '.js'; https.get(url, function (response) { if (response.statusCode !== 200) { cb(new Error('Error retrieving binary: ' + response.statusMessage)); From 511428b56ed3c412d3d093936ceac8cf357636d4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Jan 2021 16:06:39 +0100 Subject: [PATCH 077/153] Version 0.8.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c88573a..44f50f21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.0", + "version": "0.8.1", "description": "Solidity compiler", "main": "index.js", "bin": { From 9523969227cb93d903de7bb4674b17cbf0a4a481 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 3 Mar 2021 13:43:15 +0100 Subject: [PATCH 078/153] Solidity 0.8.2 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 44f50f21..af6fcde3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.1", + "version": "0.8.2", "description": "Solidity compiler", "main": "index.js", "bin": { @@ -46,13 +46,13 @@ "dependencies": { "command-exists": "^1.2.8", "commander": "3.0.2", + "follow-redirects": "^1.12.1", "fs-extra": "^0.30.0", "js-sha3": "0.8.0", "memorystream": "^0.3.1", "require-from-string": "^2.0.0", "semver": "^5.5.0", - "tmp": "0.0.33", - "follow-redirects": "^1.12.1" + "tmp": "0.0.33" }, "devDependencies": { "coveralls": "^3.0.0", From e1c61ba7e6103d454341b140d0515825aaddac54 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 23 Mar 2021 15:47:11 +0100 Subject: [PATCH 079/153] Set version to 0.8.3. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af6fcde3..b0eca3a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.2", + "version": "0.8.3", "description": "Solidity compiler", "main": "index.js", "bin": { From 9b36bedb2cb990853e2c457a92d999f31584af56 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 6 Apr 2021 11:06:53 +0200 Subject: [PATCH 080/153] Use SMT engine --- test/smtCheckerTests/smoke_with_engine.sol | 6 +++ .../smoke_with_multi_engine.sol | 8 ++++ test/smtcallback.js | 43 ++++++++++++++++--- 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 test/smtCheckerTests/smoke_with_engine.sol create mode 100644 test/smtCheckerTests/smoke_with_multi_engine.sol diff --git a/test/smtCheckerTests/smoke_with_engine.sol b/test/smtCheckerTests/smoke_with_engine.sol new file mode 100644 index 00000000..12dd42ce --- /dev/null +++ b/test/smtCheckerTests/smoke_with_engine.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + } +} +// ==== +// SMTEngine: all diff --git a/test/smtCheckerTests/smoke_with_multi_engine.sol b/test/smtCheckerTests/smoke_with_multi_engine.sol new file mode 100644 index 00000000..f5a8428a --- /dev/null +++ b/test/smtCheckerTests/smoke_with_multi_engine.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure { + } +} +// ==== +// SMTEngine: all +// SMTEngine: chc diff --git a/test/smtcallback.js b/test/smtcallback.js index 763aa509..09cfd283 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -1,3 +1,4 @@ +const assert = require('assert'); const tape = require('tape'); const fs = require('fs'); const path = require('path'); @@ -7,7 +8,6 @@ const smtchecker = require('../smtchecker.js'); const smtsolver = require('../smtsolver.js'); var preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; -var pragmaSMT = 'pragma experimental SMTChecker;\n'; function collectErrors (solOutput) { if (solOutput === undefined) { @@ -72,10 +72,20 @@ tape('SMTCheckerCallback', function (t) { return { error: 'Fake SMT solver error.' }; }; + var pragmaSMT = ''; + var settings = {}; + // `pragma experimental SMTChecker;` was deprecated in 0.8.4 + if (!semver.gt(solc.semver(), '0.8.3')) { + pragmaSMT = 'pragma experimental SMTChecker;\n'; + } else { + settings = { modelChecker: { engine: 'all' } }; + } + var input = { 'a': { content: preamble + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } }; var inputJSON = JSON.stringify({ language: 'Solidity', - sources: input + sources: input, + settings: settings }); var tests; @@ -152,9 +162,20 @@ tape('SMTCheckerCallback', function (t) { st.comment('Collecting ' + sources[i] + '...'); var source = fs.readFileSync(sources[i], 'utf8'); - if (source.includes('// SMTEngine')) { - st.skip('Test requires specific SMTChecker engine.'); - continue; + var engine; + var option = '// SMTEngine: '; + if (source.includes(option)) { + let idx = source.indexOf(option); + if (source.indexOf(option, idx + 1) !== -1) { + st.skip('SMTEngine option given multiple times.'); + st.end(); + return; + } + let re = new RegExp(option + '(\\w+)'); + let m = source.match(re); + assert(m !== undefined); + assert(m.length >= 2); + engine = m[1]; } var expected = []; @@ -169,7 +190,8 @@ tape('SMTCheckerCallback', function (t) { tests[sources[i]] = { expectations: expected, solidity: { test: { content: preamble + source } }, - ignoreCex: source.includes('// SMTIgnoreCex: yes') + ignoreCex: source.includes('// SMTIgnoreCex: yes'), + engine: engine }; } @@ -185,10 +207,17 @@ tape('SMTCheckerCallback', function (t) { continue; } + var settings = {}; + // `pragma experimental SMTChecker;` was deprecated in 0.8.4 + if (semver.gt(solc.semver(), '0.8.3')) { + let engine = test.engine !== undefined ? test.engine : 'all'; + settings = { modelChecker: { engine: engine } }; + } var output = JSON.parse(solc.compile( JSON.stringify({ language: 'Solidity', - sources: test.solidity + sources: test.solidity, + settings: settings }), { smtSolver: smtchecker.smtCallback(smtsolver.smtSolver) } )); From 9651c8550bf015470de69bd02330484854fc1250 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Mar 2020 11:05:30 +0100 Subject: [PATCH 081/153] Add test option to disable remote version tests. --- test/compiler.js | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/test/compiler.js b/test/compiler.js index e65c9604..2e5a78b5 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -4,6 +4,8 @@ const solc = require('../index.js'); const linker = require('../linker.js'); const execSync = require('child_process').execSync; +var noRemoteVersions = (process.argv.indexOf('--no-remote-versions') >= 0); + function runTests (solc, versionText) { console.log(`Running tests with ${versionText} ${solc.version()}`); @@ -759,7 +761,7 @@ function runTests (solc, versionText) { }); // Only run on the latest version. - if (versionText === 'latest') { + if (versionText === 'latest' && !noRemoteVersions) { tape('Loading Legacy Versions', function (t) { t.test('loading remote version - development snapshot', function (st) { // getting the development snapshot @@ -798,22 +800,24 @@ function runTests (solc, versionText) { runTests(solc, 'latest'); -// New compiler interface features 0.1.6, 0.2.1, 0.4.11, 0.5.0, etc. -// 0.4.0 added pragmas (used in tests above) -const versions = [ - 'v0.1.1+commit.6ff4cd6', - 'v0.1.6+commit.d41f8b7', - 'v0.2.0+commit.4dc2445', - 'v0.2.1+commit.91a6b35', - 'v0.3.6+commit.3fc68da', - 'v0.4.0+commit.acd334c9', - 'v0.4.11+commit.68ef5810', - 'v0.4.12+commit.194ff033', - 'v0.4.26+commit.4563c3fc' -]; -for (var version in versions) { - version = versions[version]; - execSync(`curl -L -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); - const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); - runTests(newSolc, version); +if (!noRemoteVersions) { + // New compiler interface features 0.1.6, 0.2.1, 0.4.11, 0.5.0, etc. + // 0.4.0 added pragmas (used in tests above) + const versions = [ + 'v0.1.1+commit.6ff4cd6', + 'v0.1.6+commit.d41f8b7', + 'v0.2.0+commit.4dc2445', + 'v0.2.1+commit.91a6b35', + 'v0.3.6+commit.3fc68da', + 'v0.4.0+commit.acd334c9', + 'v0.4.11+commit.68ef5810', + 'v0.4.12+commit.194ff033', + 'v0.4.26+commit.4563c3fc' + ]; + for (var version in versions) { + version = versions[version]; + execSync(`curl -L -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); + const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); + runTests(newSolc, version); + } } From 0f68b9ca3d577353e4a4159600b5a1a69f5f08a4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Apr 2021 17:28:07 +0200 Subject: [PATCH 082/153] Solidity 0.8.4. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b0eca3a7..b46bf25a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.3", + "version": "0.8.4", "description": "Solidity compiler", "main": "index.js", "bin": { From abdcdd1ad0221c3fb469734ff2f4973505634dd0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Jun 2021 17:00:18 +0200 Subject: [PATCH 083/153] Set version to 0.8.5. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b46bf25a..fc4428f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.4", + "version": "0.8.5", "description": "Solidity compiler", "main": "index.js", "bin": { From d1e73edd5abbd068a91c9e98c9302cf9ee64e209 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 11 Jun 2021 13:26:00 +0200 Subject: [PATCH 084/153] solcjs: Set default value for --optimize flag to false - Without it the value is undefined and `enabled` key gets stripped from the optimizer settings dict. --- solcjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solcjs b/solcjs index e42353bb..96bb97c4 100755 --- a/solcjs +++ b/solcjs @@ -18,7 +18,7 @@ program.name('solcjs'); program.version(solc.version()); program .option('--version', 'Show version and exit.') - .option('--optimize', 'Enable bytecode optimizer.') + .option('--optimize', 'Enable bytecode optimizer.', false) .option('--bin', 'Binary of the contracts in hex.') .option('--abi', 'ABI of the contracts.') .option('--standard-json', 'Turn on Standard JSON Input / Output mode.') From 2b7a0acabddcd41d6c370f898cad447687e0f146 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 22 Jun 2021 15:58:41 +0200 Subject: [PATCH 085/153] Solidity 0.8.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fc4428f9..14c29002 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.5", + "version": "0.8.6", "description": "Solidity compiler", "main": "index.js", "bin": { From cfc853c585306d1a5af29c8f37abdc7aafb2f410 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 9 Jul 2021 14:05:07 +0200 Subject: [PATCH 086/153] solcjs: Rename 'path' parameters to unshadow the 'path' module --- solcjs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/solcjs b/solcjs index 96bb97c4..84fc1482 100755 --- a/solcjs +++ b/solcjs @@ -34,22 +34,22 @@ function abort (msg) { process.exit(1); } -function readFileCallback(path) { - path = program.basePath + '/' + path; - if (fs.existsSync(path)) { +function readFileCallback(sourcePath) { + sourcePath = program.basePath + '/' + sourcePath; + if (fs.existsSync(sourcePath)) { try { - return { 'contents': fs.readFileSync(path).toString('utf8') } + return { 'contents': fs.readFileSync(sourcePath).toString('utf8') } } catch (e) { - return { error: 'Error reading ' + path + ': ' + e }; + return { error: 'Error reading ' + sourcePath + ': ' + e }; } } else - return { error: 'File not found at ' + path} + return { error: 'File not found at ' + sourcePath} } -function stripBasePath(path) { - if (program.basePath && path.startsWith(program.basePath)) - return path.slice(program.basePath.length); +function stripBasePath(sourcePath) { + if (program.basePath && sourcePath.startsWith(program.basePath)) + return sourcePath.slice(program.basePath.length); else - return path; + return sourcePath; } var callbacks = undefined @@ -167,4 +167,4 @@ originalUncaughtExceptionListeners.forEach(function (listener) { if (hasError) { process.exit(1); -} \ No newline at end of file +} From 8352d01f6ad75d134e65db62a64df2d4aa9b96d1 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 9 Jul 2021 16:51:44 +0200 Subject: [PATCH 087/153] solcjs: Change base path stripping logic to match the new logic in solc --- solcjs | 30 +++++++++++++++++++++++++---- test/cli.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/solcjs b/solcjs index 84fc1482..c2fece60 100755 --- a/solcjs +++ b/solcjs @@ -4,6 +4,7 @@ var originalUncaughtExceptionListeners = process.listeners("uncaughtException"); var fs = require('fs-extra'); +var os = require('os'); var path = require('path'); var solc = require('./index.js'); var smtchecker = require('./smtchecker.js'); @@ -45,11 +46,32 @@ function readFileCallback(sourcePath) { } else return { error: 'File not found at ' + sourcePath} } + +function withUnixPathSeparators(filePath) { + if (os.platform !== 'win32') + // On UNIX-like systems forward slashes in paths are just a part of the file name. + return filePath; + + return filePath.replace(/\\/g, "/"); +} + function stripBasePath(sourcePath) { - if (program.basePath && sourcePath.startsWith(program.basePath)) - return sourcePath.slice(program.basePath.length); - else - return sourcePath; + const absoluteBasePath = (program.basePath ? path.resolve(program.basePath) : path.resolve('.')); + + // Compared to base path stripping logic in solc this is much simpler because path.resolve() + // handles symlinks correctly (does not resolve them except in work dir) and strips .. segments + // from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it + // ignores less important corner cases: drive letters are not stripped from absolute paths on + // Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has + // very little test coverage so there might be more differences that we are just not aware of. + const absoluteSourcePath = path.resolve(sourcePath); + const relativeSourcePath = path.relative(absoluteBasePath, absoluteSourcePath); + + if (relativeSourcePath.startsWith('../')) + // Path can't be made relative without stepping outside of base path so return absolute one. + return withUnixPathSeparators(absoluteSourcePath); + + return withUnixPathSeparators(relativeSourcePath); } var callbacks = undefined diff --git a/test/cli.js b/test/cli.js index e6b610d6..8f299fe1 100644 --- a/test/cli.js +++ b/test/cli.js @@ -1,5 +1,6 @@ const tape = require('tape'); const spawn = require('tape-spawn'); +const path = require('path'); const pkg = require('../package.json'); tape('CLI', function (t) { @@ -62,14 +63,58 @@ tape('CLI', function (t) { spt.end(); }); - t.test('no-base-path', function (st) { - var spt = spawn(st, './solcjs --bin test/resources/importA.sol'); - spt.stderr.match(/not found: File import callback not supported/); + t.test('no base path', function (st) { + var spt = spawn( + st, + './solcjs --bin ' + + 'test/resources/importA.sol ' + + './test/resources//importA.sol ' + + path.resolve('test/resources/importA.sol') + ' ' + + // Adding importB explicitly here should make compiler find it despite the lack of callback + 'test/resources/importB.sol ' + ); + spt.stderr.empty(); + spt.succeeds(); + spt.end(); + }); + + t.test('relative base path', function (st) { + // NOTE: This and other base path tests rely on the relative ./importB.sol import in importA.sol. + // If base path is not stripped correctly from all source paths below, they will not be found + // by the import callback when it appends the base path back. + var spt = spawn( + st, + './solcjs --bin --base-path test/resources ' + + 'test/resources/importA.sol ' + + './test/resources//importA.sol ' + + path.resolve('test/resources/importA.sol') + ); + spt.stderr.empty(); + spt.succeeds(); + spt.end(); + }); + + t.test('relative non canonical base path', function (st) { + var spt = spawn( + st, + './solcjs --bin --base-path ./test/resources ' + + 'test/resources/importA.sol ' + + './test/resources//importA.sol ' + + path.resolve('test/resources/importA.sol') + ); + spt.stderr.empty(); + spt.succeeds(); spt.end(); }); - t.test('base-path', function (st) { - var spt = spawn(st, './solcjs --bin --base-path test/resources test/resources/importA.sol'); + t.test('absolute base path', function (st) { + var spt = spawn( + st, + './solcjs --bin --base-path ' + path.resolve('test/resources') + ' ' + + 'test/resources/importA.sol ' + + './test/resources//importA.sol ' + + path.resolve('test/resources/importA.sol') + ); spt.stderr.empty(); spt.succeeds(); spt.end(); From eadcfdd275db014d2da5c4527afb7dbbb02e8429 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 9 Jul 2021 17:17:05 +0200 Subject: [PATCH 088/153] solcjs: Enable import callback without base path (except for --standard-json mode) --- solcjs | 5 +++-- test/cli.js | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/solcjs b/solcjs index c2fece60..b549154c 100755 --- a/solcjs +++ b/solcjs @@ -36,7 +36,8 @@ function abort (msg) { } function readFileCallback(sourcePath) { - sourcePath = program.basePath + '/' + sourcePath; + if (program.basePath) + sourcePath = program.basePath + '/' + sourcePath; if (fs.existsSync(sourcePath)) { try { return { 'contents': fs.readFileSync(sourcePath).toString('utf8') } @@ -75,7 +76,7 @@ function stripBasePath(sourcePath) { } var callbacks = undefined -if (program.basePath) +if (program.basePath || !program.standardJson) callbacks = {'import': readFileCallback}; if (program.standardJson) { diff --git a/test/cli.js b/test/cli.js index 8f299fe1..4b7e93c4 100644 --- a/test/cli.js +++ b/test/cli.js @@ -69,9 +69,7 @@ tape('CLI', function (t) { './solcjs --bin ' + 'test/resources/importA.sol ' + './test/resources//importA.sol ' + - path.resolve('test/resources/importA.sol') + ' ' + - // Adding importB explicitly here should make compiler find it despite the lack of callback - 'test/resources/importB.sol ' + path.resolve('test/resources/importA.sol') ); spt.stderr.empty(); spt.succeeds(); From 0327554fe69a1283447be024484bc04c7dbcb5f9 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Wed, 11 Aug 2021 16:43:38 +0200 Subject: [PATCH 089/153] Set version to 0.8.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14c29002..f2e730a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.6", + "version": "0.8.7", "description": "Solidity compiler", "main": "index.js", "bin": { From 43198c5be58155f0dbe126d49f68df70b71eeae5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Aug 2021 10:51:10 +0200 Subject: [PATCH 090/153] Use prepublishOnly. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f2e730a6..fa0a765a 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ }, "scripts": { "lint": "node ./node_modules/semistandard/bin/cmd.js", - "prepublish": "node downloadCurrentVersion.js && node verifyVersion.js", - "pretest": "npm run lint", + "prepublishOnly": "node downloadCurrentVersion.js && node verifyVersion.js", + "pretest": "npm run lint && npm run prepublishOnly", "test": "tape ./test/index.js", "coverage": "node ./node_modules/nyc/bin/nyc.js --reporter=lcov --reporter=text-summary ./node_modules/tape/bin/tape ./test/index.js", "coveralls": "npm run coverage && node ./node_modules/coveralls/bin/coveralls.js Date: Mon, 16 Aug 2021 16:32:04 +0200 Subject: [PATCH 091/153] package.json: Extract the compiler download into a new script with a more self-explanatory name --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fa0a765a..2a86b2f1 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ }, "scripts": { "lint": "node ./node_modules/semistandard/bin/cmd.js", - "prepublishOnly": "node downloadCurrentVersion.js && node verifyVersion.js", - "pretest": "npm run lint && npm run prepublishOnly", + "updateBinary": "node downloadCurrentVersion.js && node verifyVersion.js", + "prepublishOnly": "npm run updateBinary", + "pretest": "npm run lint && npm run updateBinary", "test": "tape ./test/index.js", "coverage": "node ./node_modules/nyc/bin/nyc.js --reporter=lcov --reporter=text-summary ./node_modules/tape/bin/tape ./test/index.js", "coveralls": "npm run coverage && node ./node_modules/coveralls/bin/coveralls.js Date: Mon, 16 Aug 2021 13:52:27 +0200 Subject: [PATCH 092/153] package.json: Remove `npm run prepublishOnly` from `pretest` --- .circleci/config.yml | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 098ccfc7..7e9e7967 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,6 +27,9 @@ jobs: - run: name: install-npm command: npm install + - run: + name: updateBinary + command: npm run updateBinary - run: name: test command: npm run test diff --git a/package.json b/package.json index 2a86b2f1..74b08538 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "lint": "node ./node_modules/semistandard/bin/cmd.js", "updateBinary": "node downloadCurrentVersion.js && node verifyVersion.js", "prepublishOnly": "npm run updateBinary", - "pretest": "npm run lint && npm run updateBinary", + "pretest": "npm run lint", "test": "tape ./test/index.js", "coverage": "node ./node_modules/nyc/bin/nyc.js --reporter=lcov --reporter=text-summary ./node_modules/tape/bin/tape ./test/index.js", "coveralls": "npm run coverage && node ./node_modules/coveralls/bin/coveralls.js Date: Fri, 27 Aug 2021 17:13:31 +0200 Subject: [PATCH 093/153] Upgrade 'commander' dependency to the latest version - New version allows having parameters that take multiple values --- package.json | 2 +- solcjs | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 74b08538..17b0ad24 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "homepage": "https://github.com/ethereum/solc-js#readme", "dependencies": { "command-exists": "^1.2.8", - "commander": "3.0.2", + "commander": "^8.1.0", "follow-redirects": "^1.12.1", "fs-extra": "^0.30.0", "js-sha3": "0.8.0", diff --git a/solcjs b/solcjs index b549154c..979608df 100755 --- a/solcjs +++ b/solcjs @@ -14,7 +14,7 @@ var smtsolver = require('./smtsolver.js'); process.removeAllListeners('uncaughtException'); var commander = require('commander'); -var program = new commander.Command(); +const program = new commander.Command(); program.name('solcjs'); program.version(solc.version()); program @@ -26,9 +26,10 @@ program .option('--base-path ', 'Automatically resolve all imports inside the given path.') .option('-o, --output-dir ', 'Output directory for the contracts.'); program.parse(process.argv); +const options = program.opts(); var files = program.args; -var destination = program.outputDir || '.' +var destination = options.outputDir || '.' function abort (msg) { console.error(msg || 'Error occured'); @@ -36,8 +37,8 @@ function abort (msg) { } function readFileCallback(sourcePath) { - if (program.basePath) - sourcePath = program.basePath + '/' + sourcePath; + if (options.basePath) + sourcePath = options.basePath + '/' + sourcePath; if (fs.existsSync(sourcePath)) { try { return { 'contents': fs.readFileSync(sourcePath).toString('utf8') } @@ -57,7 +58,7 @@ function withUnixPathSeparators(filePath) { } function stripBasePath(sourcePath) { - const absoluteBasePath = (program.basePath ? path.resolve(program.basePath) : path.resolve('.')); + const absoluteBasePath = (options.basePath ? path.resolve(options.basePath) : path.resolve('.')); // Compared to base path stripping logic in solc this is much simpler because path.resolve() // handles symlinks correctly (does not resolve them except in work dir) and strips .. segments @@ -76,10 +77,10 @@ function stripBasePath(sourcePath) { } var callbacks = undefined -if (program.basePath || !program.standardJson) +if (options.basePath || !options.standardJson) callbacks = {'import': readFileCallback}; -if (program.standardJson) { +if (options.standardJson) { var input = fs.readFileSync(process.stdin.fd).toString('utf8'); var output = solc.compile(input, callbacks); @@ -112,7 +113,7 @@ if (program.standardJson) { process.exit(1); } -if (!(program.bin || program.abi)) { +if (!(options.bin || options.abi)) { abort('Invalid option selected, must specify either --bin or --abi'); } @@ -130,7 +131,7 @@ var output = JSON.parse(solc.compile(JSON.stringify({ language: 'Solidity', settings: { optimizer: { - enabled: program.optimize + enabled: options.optimize }, outputSelection: { '*': { @@ -173,11 +174,11 @@ for (var fileName in output.contracts) { var contractFileName = fileName + ':' + contractName; contractFileName = contractFileName.replace(/[:./\\]/g, '_'); - if (program.bin) { + if (options.bin) { writeFile(contractFileName + '.bin', output.contracts[fileName][contractName].evm.bytecode.object); } - if (program.abi) { + if (options.abi) { writeFile(contractFileName + '.abi', JSON.stringify(output.contracts[fileName][contractName].abi)); } } From 03bdec4ddb984411dca3b61f66652aee02a4548e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 15 Sep 2021 20:15:47 +0100 Subject: [PATCH 094/153] CircleCI: Run tests on Node 14 and Node 16 --- .circleci/config.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7e9e7967..c0964320 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,6 +6,8 @@ workflows: - node-v10 - node-v12: run_coveralls: true + - node-v14 + - node-v16 version: 2.1 jobs: @@ -64,3 +66,11 @@ jobs: <<: *node-base docker: - image: circleci/node:12 + node-v14: + <<: *node-base + docker: + - image: circleci/node:14 + node-v16: + <<: *node-base + docker: + - image: circleci/node:16 From 67eba08db69f72829ab4a3572f54f715c7683974 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 15 Sep 2021 20:16:57 +0100 Subject: [PATCH 095/153] Drop support for Travis --- .travis.yml | 29 ----------------------------- README.md | 1 - 2 files changed, 30 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9f749089..00000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: node_js - -branches: - # We need to whitelist the branches which we want to have "push" automation. - # Pull request automation is not constrained to this set of branches. - only: - - master -matrix: - fast_finish: true - include: - - os: linux - node_js: '4' - env: CXX=g++-4.8 TEST_SUITE=test - - os: linux - node_js: '6' - env: CXX=g++-4.8 TEST_SUITE=test - - os: linux - node_js: '8' - env: CXX=g++-4.8 TEST_SUITE=test - - os: linux - node_js: '10' - env: CXX=g++-4.8 TEST_SUITE=test - - os: linux - node_js: '12' - env: CXX=g++-4.8 TEST_SUITE=test - - os: linux - node_js: '12' - env: CXX=g++-4.8 TEST_SUITE=coveralls -script: npm run $TEST_SUITE diff --git a/README.md b/README.md index 9adfa17d..5006ee8c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![Build Status](https://img.shields.io/travis/ethereum/solc-js.svg?branch=master&style=flat-square)](https://travis-ci.org/ethereum/solc-js) [![CircleCI](https://img.shields.io/circleci/project/github/ethereum/solc-js/master.svg?style=flat-square)](https://circleci.com/gh/ethereum/solc-js/tree/master) [![Coverage Status](https://img.shields.io/coveralls/ethereum/solc-js.svg?style=flat-square)](https://coveralls.io/r/ethereum/solc-js) From f2995aacbe8a8f3b57fda1b7729a25ba4ec14e3e Mon Sep 17 00:00:00 2001 From: Rory Date: Mon, 13 Sep 2021 15:57:55 +1000 Subject: [PATCH 096/153] Adding parameter to solcjs for the optimizer run option --- solcjs | 18 +++++++++++++++++- test/cli.js | 13 +++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/solcjs b/solcjs index 979608df..bab43250 100755 --- a/solcjs +++ b/solcjs @@ -15,16 +15,31 @@ process.removeAllListeners('uncaughtException'); var commander = require('commander'); const program = new commander.Command(); +const commanderParseInt = function(value) { + const parsedValue = parseInt(value, 10); + if (isNaN(parsedValue)) { + throw new commander.InvalidArgumentError("Not a valid integer."); + } + return parsedValue; +}; + program.name('solcjs'); program.version(solc.version()); program .option('--version', 'Show version and exit.') .option('--optimize', 'Enable bytecode optimizer.', false) + .option( + '--optimize-runs ', + 'The number of runs specifies roughly how often each opcode of the deployed code will be executed across the lifetime of the contract. ' + + 'Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage.', + commanderParseInt + ) .option('--bin', 'Binary of the contracts in hex.') .option('--abi', 'ABI of the contracts.') .option('--standard-json', 'Turn on Standard JSON Input / Output mode.') .option('--base-path ', 'Automatically resolve all imports inside the given path.') .option('-o, --output-dir ', 'Output directory for the contracts.'); + program.parse(process.argv); const options = program.opts(); @@ -131,7 +146,8 @@ var output = JSON.parse(solc.compile(JSON.stringify({ language: 'Solidity', settings: { optimizer: { - enabled: options.optimize + enabled: options.optimize, + runs: options.optimizeRuns, }, outputSelection: { '*': { diff --git a/test/cli.js b/test/cli.js index 4b7e93c4..a2f671ff 100644 --- a/test/cli.js +++ b/test/cli.js @@ -37,6 +37,19 @@ tape('CLI', function (t) { spt.end(); }); + t.test('--bin --optimize-runs 666', function (st) { + var spt = spawn(st, './solcjs --bin --optimize-runs 666 test/resources/fixtureSmoke.sol'); + spt.stderr.empty(); + spt.succeeds(); + spt.end(); + }); + + t.test('--bin --optimize-runs not-a-number', function (st) { + var spt = spawn(st, './solcjs --bin --optimize-runs not-a-number test/resources/fixtureSmoke.sol'); + spt.stderr.match(/^error: option '--optimize-runs ' argument 'not-a-number' is invalid/); + spt.end(); + }); + t.test('invalid file specified', function (st) { var spt = spawn(st, './solcjs --bin test/fileNotFound.sol'); spt.stderr.match(/^Error reading /); From bbab02bcf80cc5f62e80e8ee52e49201abfb3df8 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 9 Jul 2021 14:36:20 +0200 Subject: [PATCH 097/153] solcjs: Add --pretty-json option --- solcjs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/solcjs b/solcjs index bab43250..b8f0f84b 100755 --- a/solcjs +++ b/solcjs @@ -38,7 +38,8 @@ program .option('--abi', 'ABI of the contracts.') .option('--standard-json', 'Turn on Standard JSON Input / Output mode.') .option('--base-path ', 'Automatically resolve all imports inside the given path.') - .option('-o, --output-dir ', 'Output directory for the contracts.'); + .option('-o, --output-dir ', 'Output directory for the contracts.') + .option('-p, --pretty-json', 'Pretty-print all JSON output.', false); program.parse(process.argv); const options = program.opts(); @@ -91,18 +92,26 @@ function stripBasePath(sourcePath) { return withUnixPathSeparators(relativeSourcePath); } +function toFormattedJson(input) { + return JSON.stringify(input, null, program.prettyJson ? 4 : 0); +} + +function reformatJsonIfRequested(inputJson) { + return (program.prettyJson ? toFormattedJson(JSON.parse(inputJson)) : inputJson); +} + var callbacks = undefined if (options.basePath || !options.standardJson) callbacks = {'import': readFileCallback}; if (options.standardJson) { var input = fs.readFileSync(process.stdin.fd).toString('utf8'); - var output = solc.compile(input, callbacks); + var output = reformatJsonIfRequested(solc.compile(input, callbacks)); try { var inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver); if (inputJSON) { - output = solc.compile(JSON.stringify(inputJSON), callbacks); + output = reformatJsonIfRequested(solc.compile(JSON.stringify(inputJSON), callbacks)); } } catch (e) { @@ -118,7 +127,7 @@ if (options.standardJson) { outputJSON.errors = [] } outputJSON.errors.push(addError); - output = JSON.stringify(outputJSON); + output = toFormattedJson(outputJSON); } console.log(output); @@ -195,7 +204,7 @@ for (var fileName in output.contracts) { } if (options.abi) { - writeFile(contractFileName + '.abi', JSON.stringify(output.contracts[fileName][contractName].abi)); + writeFile(contractFileName + '.abi', toFormattedJson(output.contracts[fileName][contractName].abi)); } } } From aa49a83e3fc8bc8c2e7a81f8049e69bdc7e52024 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 9 Jul 2021 15:20:16 +0200 Subject: [PATCH 098/153] solcjs: Add --verbose option --- solcjs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/solcjs b/solcjs index b8f0f84b..8a3554a6 100755 --- a/solcjs +++ b/solcjs @@ -39,7 +39,8 @@ program .option('--standard-json', 'Turn on Standard JSON Input / Output mode.') .option('--base-path ', 'Automatically resolve all imports inside the given path.') .option('-o, --output-dir ', 'Output directory for the contracts.') - .option('-p, --pretty-json', 'Pretty-print all JSON output.', false); + .option('-p, --pretty-json', 'Pretty-print all JSON output.', false) + .option('-v, --verbose', 'More detailed console output.', false); program.parse(process.argv); const options = program.opts(); @@ -106,11 +107,15 @@ if (options.basePath || !options.standardJson) if (options.standardJson) { var input = fs.readFileSync(process.stdin.fd).toString('utf8'); + if (program.verbose) + console.log('>>> Compiling:\n' + reformatJsonIfRequested(input) + "\n") var output = reformatJsonIfRequested(solc.compile(input, callbacks)); try { var inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver); if (inputJSON) { + if (program.verbose) + console.log('>>> Retrying compilation with SMT:\n' + toFormattedJson(inputJSON) + "\n") output = reformatJsonIfRequested(solc.compile(JSON.stringify(inputJSON), callbacks)); } } @@ -130,6 +135,8 @@ if (options.standardJson) { output = toFormattedJson(outputJSON); } + if (program.verbose) + console.log('>>> Compilation result:') console.log(output); process.exit(0); } else if (files.length === 0) { @@ -151,7 +158,7 @@ for (var i = 0; i < files.length; i++) { } } -var output = JSON.parse(solc.compile(JSON.stringify({ +const cliInput = { language: 'Solidity', settings: { optimizer: { @@ -165,7 +172,10 @@ var output = JSON.parse(solc.compile(JSON.stringify({ } }, sources: sources -}), callbacks)); +}; +if (program.verbose) + console.log('>>> Compiling:\n' + toFormattedJson(cliInput) + "\n") +var output = JSON.parse(solc.compile(JSON.stringify(cliInput), callbacks)); let hasError = false; From 1584d306931ca49e03412fb8280cf82bc3d84c96 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 27 Aug 2021 21:03:53 +0200 Subject: [PATCH 099/153] solcjs: Add --include-path option --- README.md | 13 +++- solcjs | 64 ++++++++++++----- test/cli.js | 72 +++++++++++++++++++ .../importCallback/base/contractA.sol | 8 +++ .../importCallback/base/contractB.sol | 6 ++ test/resources/importCallback/contractC.sol | 4 ++ .../importCallback/includeA/libX.sol | 4 ++ .../importCallback/includeA/libY.sol | 6 ++ .../importCallback/includeA/utils.sol | 4 ++ .../importCallback/includeB/libZ.sol | 4 ++ 10 files changed, 165 insertions(+), 20 deletions(-) create mode 100644 test/resources/importCallback/base/contractA.sol create mode 100644 test/resources/importCallback/base/contractB.sol create mode 100644 test/resources/importCallback/contractC.sol create mode 100644 test/resources/importCallback/includeA/libX.sol create mode 100644 test/resources/importCallback/includeA/libY.sol create mode 100644 test/resources/importCallback/includeA/utils.sol create mode 100644 test/resources/importCallback/includeB/libZ.sol diff --git a/README.md b/README.md index 5006ee8c..c877e2c0 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,17 @@ solcjs --help To compile a contract that imports other contracts via relative paths: ```bash -solcjs --bin --base-path . ./MainContract.sol +solcjs --bin --include-path node_modules/ --base-path . MainContract.sol ``` -The option ``--base-path`` enables automatic loading of imports from the filesystem and -takes a path as argument that contains the source files. +Use the ``--base-path`` and ``--include-path`` options to describe the layout of your project. +``--base-path`` represents the root of your own source tree while ``--include-path`` allows you to +specify extra locations containing external code (e.g. libraries installed with a package manager). + +Note: ensure that all the files you specify on the command line are located inside the base path or +one of the include paths. +The compiler refers to files from outside of these directories using absolute paths. +Having absolute paths in contract metadata will result in your bytecode being reproducible only +when it's placed in these exact absolute locations. Note: this commandline interface is not compatible with `solc` provided by the Solidity compiler package and thus cannot be used in combination with an Ethereum client via the `eth.compile.solidity()` RPC method. Please refer to the diff --git a/solcjs b/solcjs index 8a3554a6..890ce006 100755 --- a/solcjs +++ b/solcjs @@ -37,7 +37,13 @@ program .option('--bin', 'Binary of the contracts in hex.') .option('--abi', 'ABI of the contracts.') .option('--standard-json', 'Turn on Standard JSON Input / Output mode.') - .option('--base-path ', 'Automatically resolve all imports inside the given path.') + .option('--base-path ', 'Root of the project source tree. ' + + 'The import callback will attempt to interpret all import paths as relative to this directory.' + ) + .option('--include-path ', 'Extra source directories available to the import callback. ' + + 'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' + + 'Can be used multiple times to provide multiple locations.' + ) .option('-o, --output-dir ', 'Output directory for the contracts.') .option('-p, --pretty-json', 'Pretty-print all JSON output.', false) .option('-v, --verbose', 'More detailed console output.', false); @@ -54,16 +60,21 @@ function abort (msg) { } function readFileCallback(sourcePath) { - if (options.basePath) - sourcePath = options.basePath + '/' + sourcePath; - if (fs.existsSync(sourcePath)) { - try { - return { 'contents': fs.readFileSync(sourcePath).toString('utf8') } - } catch (e) { - return { error: 'Error reading ' + sourcePath + ': ' + e }; + const prefixes = [options.basePath ? options.basePath : ""].concat( + options.includePath ? options.includePath : [] + ); + for (const prefix of prefixes) { + const prefixedSourcePath = (prefix ? prefix + '/' : "") + sourcePath; + + if (fs.existsSync(prefixedSourcePath)) { + try { + return {'contents': fs.readFileSync(prefixedSourcePath).toString('utf8')} + } catch (e) { + return {error: 'Error reading ' + prefixedSourcePath + ': ' + e}; + } } - } else - return { error: 'File not found at ' + sourcePath} + } + return {error: 'File not found inside the base path or any of the include paths.'} } function withUnixPathSeparators(filePath) { @@ -74,8 +85,13 @@ function withUnixPathSeparators(filePath) { return filePath.replace(/\\/g, "/"); } -function stripBasePath(sourcePath) { +function makeSourcePathRelativeIfPossible(sourcePath) { const absoluteBasePath = (options.basePath ? path.resolve(options.basePath) : path.resolve('.')); + const absoluteIncludePaths = ( + options.includePath ? + options.includePath.map((prefix) => { return path.resolve(prefix); }) : + [] + ); // Compared to base path stripping logic in solc this is much simpler because path.resolve() // handles symlinks correctly (does not resolve them except in work dir) and strips .. segments @@ -84,13 +100,16 @@ function stripBasePath(sourcePath) { // Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has // very little test coverage so there might be more differences that we are just not aware of. const absoluteSourcePath = path.resolve(sourcePath); - const relativeSourcePath = path.relative(absoluteBasePath, absoluteSourcePath); - if (relativeSourcePath.startsWith('../')) - // Path can't be made relative without stepping outside of base path so return absolute one. - return withUnixPathSeparators(absoluteSourcePath); + for (const absolutePrefix of [absoluteBasePath].concat(absoluteIncludePaths)) { + const relativeSourcePath = path.relative(absolutePrefix, absoluteSourcePath); + + if (!relativeSourcePath.startsWith('../')) + return withUnixPathSeparators(relativeSourcePath); + } - return withUnixPathSeparators(relativeSourcePath); + // File is not located inside base path or include paths so use its absolute path. + return withUnixPathSeparators(absoluteSourcePath); } function toFormattedJson(input) { @@ -148,11 +167,22 @@ if (!(options.bin || options.abi)) { abort('Invalid option selected, must specify either --bin or --abi'); } +if (!options.basePath && options.includePath && options.includePath.length > 0) { + abort('--include-path option requires a non-empty base path.'); +} + +if (options.includePath) + for (const includePath of options.includePath) + if (!includePath) + abort('Empty values are not allowed in --include-path.'); + var sources = {}; for (var i = 0; i < files.length; i++) { try { - sources[stripBasePath(files[i])] = { content: fs.readFileSync(files[i]).toString() }; + sources[makeSourcePathRelativeIfPossible(files[i])] = { + content: fs.readFileSync(files[i]).toString() + }; } catch (e) { abort('Error reading ' + files[i] + ': ' + e); } diff --git a/test/cli.js b/test/cli.js index a2f671ff..d5ae337c 100644 --- a/test/cli.js +++ b/test/cli.js @@ -131,6 +131,48 @@ tape('CLI', function (t) { spt.end(); }); + t.test('include paths', function (st) { + const spt = spawn( + st, + './solcjs --bin ' + + 'test/resources/importCallback/base/contractB.sol ' + + 'test/resources/importCallback/includeA/libY.sol ' + + './test/resources/importCallback/includeA//libY.sol ' + + path.resolve('test/resources/importCallback/includeA/libY.sol') + ' ' + + '--base-path test/resources/importCallback/base ' + + '--include-path test/resources/importCallback/includeA ' + + '--include-path ' + path.resolve('test/resources/importCallback/includeB/') + ); + spt.stderr.empty(); + spt.succeeds(); + spt.end(); + }); + + t.test('include paths without base path', function (st) { + const spt = spawn( + st, + './solcjs --bin ' + + 'test/resources/importCallback/contractC.sol ' + + '--include-path test/resources/importCallback/includeA' + ); + spt.stderr.match(/--include-path option requires a non-empty base path\./); + spt.fails(); + spt.end(); + }); + + t.test('empty include paths', function (st) { + const spt = spawn( + st, + './solcjs --bin ' + + 'test/resources/importCallback/contractC.sol ' + + '--base-path test/resources/importCallback/base ' + + '--include-path=' + ); + spt.stderr.match(/Empty values are not allowed in --include-path\./); + spt.fails(); + spt.end(); + }); + t.test('standard json', function (st) { var input = { 'language': 'Solidity', @@ -187,4 +229,34 @@ tape('CLI', function (t) { spt.end(); }); }); + + t.test('standard json include paths', function (st) { + var input = { + 'language': 'Solidity', + 'sources': { + 'contractB.sol': { + 'content': + '// SPDX-License-Identifier: GPL-3.0\n' + + 'pragma solidity >=0.0;\n' + + 'import "./contractA.sol";\n' + } + } + }; + var spt = spawn( + st, + './solcjs --standard-json ' + + '--base-path test/resources/importCallback/base ' + + '--include-path test/resources/importCallback/includeA ' + + '--include-path ' + path.resolve('test/resources/importCallback/includeB/') + ); + spt.stdin.setEncoding('utf-8'); + spt.stdin.write(JSON.stringify(input)); + spt.stdin.end(); + spt.stdin.on('finish', function () { + spt.stderr.empty(); + spt.stdout.match(/{"sources":{"contractA.sol":{"id":0},"contractB.sol":{"id":1},"libX.sol":{"id":2},"libY.sol":{"id":3},"libZ.sol":{"id":4},"utils.sol":{"id":5}}}/); + spt.succeeds(); + spt.end(); + }); + }); }); diff --git a/test/resources/importCallback/base/contractA.sol b/test/resources/importCallback/base/contractA.sol new file mode 100644 index 00000000..a0136e74 --- /dev/null +++ b/test/resources/importCallback/base/contractA.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +import "libX.sol"; +import "libY.sol"; +import "libZ.sol"; + +contract A {} diff --git a/test/resources/importCallback/base/contractB.sol b/test/resources/importCallback/base/contractB.sol new file mode 100644 index 00000000..c6792746 --- /dev/null +++ b/test/resources/importCallback/base/contractB.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +import "./contractA.sol"; + +contract B {} diff --git a/test/resources/importCallback/contractC.sol b/test/resources/importCallback/contractC.sol new file mode 100644 index 00000000..f123f6c8 --- /dev/null +++ b/test/resources/importCallback/contractC.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C {} diff --git a/test/resources/importCallback/includeA/libX.sol b/test/resources/importCallback/includeA/libX.sol new file mode 100644 index 00000000..763fc402 --- /dev/null +++ b/test/resources/importCallback/includeA/libX.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +library X {} diff --git a/test/resources/importCallback/includeA/libY.sol b/test/resources/importCallback/includeA/libY.sol new file mode 100644 index 00000000..55be31f6 --- /dev/null +++ b/test/resources/importCallback/includeA/libY.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +import "./utils.sol"; + +library Y {} diff --git a/test/resources/importCallback/includeA/utils.sol b/test/resources/importCallback/includeA/utils.sol new file mode 100644 index 00000000..f544b941 --- /dev/null +++ b/test/resources/importCallback/includeA/utils.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +library Utils {} diff --git a/test/resources/importCallback/includeB/libZ.sol b/test/resources/importCallback/includeB/libZ.sol new file mode 100644 index 00000000..6e3e6193 --- /dev/null +++ b/test/resources/importCallback/includeB/libZ.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +library Z {} From a2c12b7b86100903ec4f4ffd374b124286cd4e59 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Mon, 27 Sep 2021 16:29:35 +0200 Subject: [PATCH 100/153] A fix for the CI. --- test/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cli.js b/test/cli.js index d5ae337c..4f1707a1 100644 --- a/test/cli.js +++ b/test/cli.js @@ -254,7 +254,7 @@ tape('CLI', function (t) { spt.stdin.end(); spt.stdin.on('finish', function () { spt.stderr.empty(); - spt.stdout.match(/{"sources":{"contractA.sol":{"id":0},"contractB.sol":{"id":1},"libX.sol":{"id":2},"libY.sol":{"id":3},"libZ.sol":{"id":4},"utils.sol":{"id":5}}}/); + spt.stdout.match(/"sources":{"contractA.sol":{"id":0},"contractB.sol":{"id":1},"libX.sol":{"id":2},"libY.sol":{"id":3},"libZ.sol":{"id":4},"utils.sol":{"id":5}}}/); spt.succeeds(); spt.end(); }); From 9781f00d0dfde4582d2fd6dd7ee43cc8fb85a929 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 27 Sep 2021 19:20:11 +0200 Subject: [PATCH 101/153] Set version to 0.8.8. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17b0ad24..7fc3b195 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.7", + "version": "0.8.8", "description": "Solidity compiler", "main": "index.js", "bin": { From 3c07ff0ef8cfeed109dd2728946e18cc414e186f Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Sep 2021 18:39:48 +0200 Subject: [PATCH 102/153] Set version to 0.8.9. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fc3b195..e61d7316 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.8", + "version": "0.8.9", "description": "Solidity compiler", "main": "index.js", "bin": { From 8edc46cd64f21ecd5afc328c0585a00f871c8bdb Mon Sep 17 00:00:00 2001 From: cameel Date: Tue, 12 Oct 2021 21:36:43 +0200 Subject: [PATCH 103/153] Don't assume that file names do not contain colons when translating compiler output from 0.4.10 --- test/compiler.js | 26 ++++++++++++++++++++++++++ translate.js | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/test/compiler.js b/test/compiler.js index 2e5a78b5..263408d9 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -757,6 +757,31 @@ function runTests (solc, versionText) { } st.end(); }); + + t.test('compiling standard JSON (file names containing symbols)', function (st) { + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': ['evm.bytecode'] + } + } + }, + 'sources': { + '!@#$%^&*()_+-=[]{}\\|"\';:~`<>,.?/': { + 'content': 'contract C {}' + } + } + }; + + var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectNoError(output)); + var C = getBytecodeStandard(output, '!@#$%^&*()_+-=[]{}\\|"\';:~`<>,.?/', 'C'); + st.ok(typeof C === 'string'); + st.ok(C.length > 0); + st.end(); + }); }); }); @@ -810,6 +835,7 @@ if (!noRemoteVersions) { 'v0.2.1+commit.91a6b35', 'v0.3.6+commit.3fc68da', 'v0.4.0+commit.acd334c9', + 'v0.4.10+commit.f0d539ae', 'v0.4.11+commit.68ef5810', 'v0.4.12+commit.194ff033', 'v0.4.26+commit.4563c3fc' diff --git a/translate.js b/translate.js index 5c4c76eb..4a9d0e3d 100644 --- a/translate.js +++ b/translate.js @@ -78,7 +78,7 @@ function translateJsonCompilerOutput (output, libraries) { ret['contracts'] = {}; for (var contract in output['contracts']) { // Split name first, can be `contract`, `:contract` or `filename:contract` - var tmp = contract.match(/^(([^:]*):)?([^:]+)$/); + var tmp = contract.match(/^((.*):)?([^:]+)$/); if (tmp.length !== 4) { // Force abort return null; From 7c78d4cbe5a4cefeb841ab99ff247bab0238e00d Mon Sep 17 00:00:00 2001 From: cameel Date: Tue, 12 Oct 2021 21:45:43 +0200 Subject: [PATCH 104/153] Corectly handle Standard JSON with wrong file name splitting from 0.4.11-0.4.19 in tests --- test/compiler.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/compiler.js b/test/compiler.js index 263408d9..62a8cdf8 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -1,3 +1,4 @@ +const assert = require('assert'); const tape = require('tape'); const semver = require('semver'); const solc = require('../index.js'); @@ -9,6 +10,16 @@ var noRemoteVersions = (process.argv.indexOf('--no-remote-versions') >= 0); function runTests (solc, versionText) { console.log(`Running tests with ${versionText} ${solc.version()}`); + function resplitFileNameOnFirstColon (fileName, contractName) { + assert(!contractName.includes(':')); + + let contractNameComponents = fileName.split(':'); + const truncatedFileName = contractNameComponents.shift(); + contractNameComponents.push(contractName); + + return [truncatedFileName, contractNameComponents.join(':')]; + } + function getBytecode (output, fileName, contractName) { try { var outputContract; @@ -29,6 +40,9 @@ function runTests (solc, versionText) { if (semver.lt(solc.semver(), '0.4.9')) { outputFile = output.contracts['']; } else { + if (semver.gt(solc.semver(), '0.4.10') && semver.lt(solc.semver(), '0.4.20')) { + [fileName, contractName] = resplitFileNameOnFirstColon(fileName, contractName); + } outputFile = output.contracts[fileName]; } return outputFile[contractName]['evm']['bytecode']['object']; @@ -43,6 +57,9 @@ function runTests (solc, versionText) { if (semver.lt(solc.semver(), '0.4.9')) { outputFile = output.contracts['']; } else { + if (semver.gt(solc.semver(), '0.4.10') && semver.gt(solc.semver(), '0.4.20')) { + [fileName, contractName] = resplitFileNameOnFirstColon(fileName, contractName); + } outputFile = output.contracts[fileName]; } return outputFile[contractName]['evm']['gasEstimates']; @@ -782,6 +799,31 @@ function runTests (solc, versionText) { st.ok(C.length > 0); st.end(); }); + + t.test('compiling standard JSON (file names containing multiple semicolons)', function (st) { + var input = { + 'language': 'Solidity', + 'settings': { + 'outputSelection': { + '*': { + '*': ['evm.bytecode'] + } + } + }, + 'sources': { + 'a:b:c:d:e:f:G.sol': { + 'content': 'contract G {}' + } + } + }; + + var output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(expectNoError(output)); + var G = getBytecodeStandard(output, 'a:b:c:d:e:f:G.sol', 'G'); + st.ok(typeof G === 'string'); + st.ok(G.length > 0); + st.end(); + }); }); }); @@ -835,9 +877,12 @@ if (!noRemoteVersions) { 'v0.2.1+commit.91a6b35', 'v0.3.6+commit.3fc68da', 'v0.4.0+commit.acd334c9', + 'v0.4.9+commit.364da425', 'v0.4.10+commit.f0d539ae', 'v0.4.11+commit.68ef5810', 'v0.4.12+commit.194ff033', + 'v0.4.19+commit.c4cbbb05', + 'v0.4.20+commit.3155dd80', 'v0.4.26+commit.4563c3fc' ]; for (var version in versions) { From a69934957e122d226b98d1bde7efc564a63b8eb7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 9 Nov 2021 14:23:17 +0100 Subject: [PATCH 105/153] Set version to 0.8.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e61d7316..0bd36fd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.9", + "version": "0.8.10", "description": "Solidity compiler", "main": "index.js", "bin": { From 2ff01167f91659e0a05cb086dd0e318cf7473dc8 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 10 Nov 2021 22:20:07 +0100 Subject: [PATCH 106/153] Add CI job for nodejs 17 --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c0964320..e1f7679f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,7 @@ workflows: run_coveralls: true - node-v14 - node-v16 + - node-v17 version: 2.1 jobs: @@ -74,3 +75,7 @@ jobs: <<: *node-base docker: - image: circleci/node:16 + node-v17: + <<: *node-base + docker: + - image: circleci/node:17 From 1a98ddfc11f046ca073e4bcdeef35aa905d00441 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 10 Nov 2021 22:20:18 +0100 Subject: [PATCH 107/153] Move coveralls to nodejs 16 --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e1f7679f..e467baf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,10 +4,10 @@ workflows: jobs: - node-v8 - node-v10 - - node-v12: - run_coveralls: true + - node-v12 - node-v14 - - node-v16 + - node-v16: + run_coveralls: true - node-v17 version: 2.1 From 1a083131c8aff5858e48832ed75aa4af08f30ee8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 20 Dec 2021 16:37:23 +0100 Subject: [PATCH 108/153] Version 0.8.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0bd36fd4..d16efe14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.10", + "version": "0.8.11", "description": "Solidity compiler", "main": "index.js", "bin": { From 760613e957a7688cda1dd4021f6192fbc0dc3a81 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 14 Dec 2021 15:50:13 +0100 Subject: [PATCH 109/153] Remove fs-extra. --- package.json | 1 - solcjs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index d16efe14..55ade0e8 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "command-exists": "^1.2.8", "commander": "^8.1.0", "follow-redirects": "^1.12.1", - "fs-extra": "^0.30.0", "js-sha3": "0.8.0", "memorystream": "^0.3.1", "require-from-string": "^2.0.0", diff --git a/solcjs b/solcjs index 890ce006..bd34b0d1 100755 --- a/solcjs +++ b/solcjs @@ -3,7 +3,7 @@ // hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end var originalUncaughtExceptionListeners = process.listeners("uncaughtException"); -var fs = require('fs-extra'); +var fs = require('fs'); var os = require('os'); var path = require('path'); var solc = require('./index.js'); @@ -223,7 +223,7 @@ if (!output) { } } -fs.ensureDirSync (destination); +fs.mkdirSync(destination, {recursive: true}); function writeFile (file, content) { file = path.join(destination, file); From 8d88fcf04768b9aec687499eaaf96b04e70d7973 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 20 Dec 2021 18:05:56 +0100 Subject: [PATCH 110/153] Remove node v8 --- .circleci/config.yml | 13 ------------- package.json | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e467baf1..23b36898 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,6 @@ workflows: version: 2.1 node-multi-build: jobs: - - node-v8 - node-v10 - node-v12 - node-v14 @@ -47,18 +46,6 @@ jobs: paths: - ./node_modules - node-v4: - <<: *node-base - docker: - - image: circleci/node:4 - node-v6: - <<: *node-base - docker: - - image: circleci/node:6 - node-v8: - <<: *node-base - docker: - - image: circleci/node:8 node-v10: <<: *node-base docker: diff --git a/package.json b/package.json index 55ade0e8..ad997877 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "compiler" ], "engines": { - "node": ">=8.0.0" + "node": ">=10.0.0" }, "files": [ "abi.js", From bc6f774300d4747ea789d6ff4bf71e31758e12f5 Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Fri, 19 Nov 2021 20:51:46 +0000 Subject: [PATCH 111/153] Upgrade linting from semistandard to eslint with standard support Migrating to eslint allows using the mainstream highly maintained linting tool that will directly support linting, code style enforcement and support of locating errors. * Directly supports integrating with Typescript for future proofing * Supports enabling semi colon enforcement via rules --- .eslintrc.js | 18 ++++++++++++++++++ package.json | 14 +++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..710c4aaa --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true + }, + extends: [ + 'standard' + ], + parserOptions: { + ecmaVersion: 12, + sourceType: 'module' + }, + rules: { + semi: ['error', 'always'] + }, + ignorePatterns: ['dist', 'soljson.js'] +}; diff --git a/package.json b/package.json index ad997877..c89bdd62 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "solcjs": "solcjs" }, "scripts": { - "lint": "node ./node_modules/semistandard/bin/cmd.js", + "lint": "eslint .", + "lint:fix": "eslint --fix .", "updateBinary": "node downloadCurrentVersion.js && node verifyVersion.js", "prepublishOnly": "npm run updateBinary", "pretest": "npm run lint", @@ -56,16 +57,15 @@ }, "devDependencies": { "coveralls": "^3.0.0", + "eslint": "^7.32.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.1", "nyc": "^14.1.0", - "semistandard": "^12.0.0", "tape": "^4.11.0", "tape-spawn": "^1.4.2" }, - "semistandard": { - "ignore": [ - "soljson.js" - ] - }, "nyc": { "exclude": [ "soljson.js" From 9af4205978aac6d59197a57e68d35210cfebf7a8 Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Fri, 19 Nov 2021 20:55:35 +0000 Subject: [PATCH 112/153] Lint existing codebase with eslint + standard Linting has been ran throughout the application which has mainly shifted `var` to `const` and `var` to `let` where applicable. While also correcting string index references to direct references e.g a['test'] to a.test. --- abi.js | 10 +- downloadCurrentVersion.js | 24 +-- index.js | 2 +- linker.js | 36 ++-- smtchecker.js | 10 +- smtsolver.js | 16 +- test/abi.js | 50 +++--- test/cli.js | 72 ++++---- test/compiler.js | 360 +++++++++++++++++++------------------- test/linker.js | 50 +++--- test/smtcallback.js | 76 ++++---- test/smtchecker.js | 12 +- test/translate.js | 6 +- translate.js | 124 ++++++------- verifyVersion.js | 6 +- wrapper.js | 120 ++++++------- 16 files changed, 487 insertions(+), 487 deletions(-) diff --git a/abi.js b/abi.js index 7834a39a..df5dde2b 100644 --- a/abi.js +++ b/abi.js @@ -1,11 +1,11 @@ -var semver = require('semver'); +const semver = require('semver'); function update (compilerVersion, abi) { - var hasConstructor = false; - var hasFallback = false; + let hasConstructor = false; + let hasFallback = false; - for (var i = 0; i < abi.length; i++) { - var item = abi[i]; + for (let i = 0; i < abi.length; i++) { + const item = abi[i]; if (item.type === 'constructor') { hasConstructor = true; diff --git a/downloadCurrentVersion.js b/downloadCurrentVersion.js index 79133766..4aa36cdb 100755 --- a/downloadCurrentVersion.js +++ b/downloadCurrentVersion.js @@ -3,16 +3,16 @@ // This is used to download the correct binary version // as part of the prepublish step. -var pkg = require('./package.json'); -var fs = require('fs'); -var https = require('follow-redirects').https; -var MemoryStream = require('memorystream'); -var keccak256 = require('js-sha3').keccak256; +const pkg = require('./package.json'); +const fs = require('fs'); +const https = require('follow-redirects').https; +const MemoryStream = require('memorystream'); +const keccak256 = require('js-sha3').keccak256; function getVersionList (cb) { console.log('Retrieving available version list...'); - var mem = new MemoryStream(null, { readable: false }); + const mem = new MemoryStream(null, { readable: false }); https.get('https://solc-bin.ethereum.org/bin/list.json', function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); @@ -39,7 +39,7 @@ function downloadBinary (outputName, version, expectedHash) { process.exit(1); }); - var file = fs.createWriteStream(outputName, { encoding: 'binary' }); + const file = fs.createWriteStream(outputName, { encoding: 'binary' }); https.get('https://solc-bin.ethereum.org/bin/' + version, function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); @@ -48,7 +48,7 @@ function downloadBinary (outputName, version, expectedHash) { response.pipe(file); file.on('finish', function () { file.close(function () { - var hash = '0x' + keccak256(fs.readFileSync(outputName, { encoding: 'binary' })); + const hash = '0x' + keccak256(fs.readFileSync(outputName, { encoding: 'binary' })); if (expectedHash !== hash) { console.log('Hash mismatch: ' + expectedHash + ' vs ' + hash); process.exit(1); @@ -63,13 +63,13 @@ console.log('Downloading correct solidity binary...'); getVersionList(function (list) { list = JSON.parse(list); - var wanted = pkg.version.match(/^(\d+\.\d+\.\d+)$/)[1]; - var releaseFileName = list.releases[wanted]; - var expectedFile = list.builds.filter(function (entry) { return entry.path === releaseFileName; })[0]; + const wanted = pkg.version.match(/^(\d+\.\d+\.\d+)$/)[1]; + const releaseFileName = list.releases[wanted]; + const expectedFile = list.builds.filter(function (entry) { return entry.path === releaseFileName; })[0]; if (!expectedFile) { console.log('Version list is invalid or corrupted?'); process.exit(1); } - var expectedHash = expectedFile.keccak256; + const expectedHash = expectedFile.keccak256; downloadBinary('soljson.js', releaseFileName, expectedHash); }); diff --git a/index.js b/index.js index 5925da69..78026179 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,3 @@ -var wrapper = require('./wrapper.js'); +const wrapper = require('./wrapper.js'); module.exports = wrapper(require('./soljson.js')); diff --git a/linker.js b/linker.js index b4f169ab..8345105d 100644 --- a/linker.js +++ b/linker.js @@ -1,25 +1,25 @@ -var assert = require('assert'); -var keccak256 = require('js-sha3').keccak256; +const assert = require('assert'); +const keccak256 = require('js-sha3').keccak256; function libraryHashPlaceholder (input) { return '$' + keccak256(input).slice(0, 34) + '$'; } -var linkBytecode = function (bytecode, libraries) { +const linkBytecode = function (bytecode, libraries) { assert(typeof bytecode === 'string'); assert(typeof libraries === 'object'); // NOTE: for backwards compatibility support old compiler which didn't use file names - var librariesComplete = {}; - for (var libraryName in libraries) { + const librariesComplete = {}; + for (const libraryName in libraries) { if (typeof libraries[libraryName] === 'object') { // API compatible with the standard JSON i/o - for (var lib in libraries[libraryName]) { + for (const lib in libraries[libraryName]) { librariesComplete[lib] = libraries[libraryName][lib]; librariesComplete[libraryName + ':' + lib] = libraries[libraryName][lib]; } } else { // backwards compatible API for early solc-js versions - var parsed = libraryName.match(/^([^:]+):(.+)$/); + const parsed = libraryName.match(/^([^:]+):(.+)$/); if (parsed) { librariesComplete[parsed[2]] = libraries[libraryName]; } @@ -27,8 +27,8 @@ var linkBytecode = function (bytecode, libraries) { } } - for (libraryName in librariesComplete) { - var hexAddress = librariesComplete[libraryName]; + for (const libraryName in librariesComplete) { + let hexAddress = librariesComplete[libraryName]; if (hexAddress.slice(0, 2) !== '0x' || hexAddress.length > 42) { throw new Error('Invalid address specified for ' + libraryName); } @@ -38,10 +38,10 @@ var linkBytecode = function (bytecode, libraries) { // Support old (library name) and new (hash of library name) // placeholders. - var replace = function (name) { + const replace = function (name) { // truncate to 37 characters - var truncatedName = name.slice(0, 36); - var libLabel = '__' + truncatedName + Array(37 - truncatedName.length).join('_') + '__'; + const truncatedName = name.slice(0, 36); + const libLabel = '__' + truncatedName + Array(37 - truncatedName.length).join('_') + '__'; while (bytecode.indexOf(libLabel) >= 0) { bytecode = bytecode.replace(libLabel, hexAddress); } @@ -54,22 +54,22 @@ var linkBytecode = function (bytecode, libraries) { return bytecode; }; -var findLinkReferences = function (bytecode) { +const findLinkReferences = function (bytecode) { assert(typeof bytecode === 'string'); // find 40 bytes in the pattern of __...<36 digits>...__ // e.g. __Lib.sol:L_____________________________ - var linkReferences = {}; - var offset = 0; + const linkReferences = {}; + let offset = 0; while (true) { - var found = bytecode.match(/__(.{36})__/); + const found = bytecode.match(/__(.{36})__/); if (!found) { break; } - var start = found.index; + const start = found.index; // trim trailing underscores // NOTE: this has no way of knowing if the trailing underscore was part of the name - var libraryName = found[1].replace(/_+$/gm, ''); + const libraryName = found[1].replace(/_+$/gm, ''); if (!linkReferences[libraryName]) { linkReferences[libraryName] = []; diff --git a/smtchecker.js b/smtchecker.js index 5a1b85b3..f1acdb35 100644 --- a/smtchecker.js +++ b/smtchecker.js @@ -4,18 +4,18 @@ // another run. // Returns null if no solving is requested. function handleSMTQueries (inputJSON, outputJSON, solver) { - var auxInputReq = outputJSON.auxiliaryInputRequested; + const auxInputReq = outputJSON.auxiliaryInputRequested; if (!auxInputReq) { return null; } - var queries = auxInputReq.smtlib2queries; + const queries = auxInputReq.smtlib2queries; if (!queries || Object.keys(queries).length === 0) { return null; } - var responses = {}; - for (var query in queries) { + const responses = {}; + for (const query in queries) { responses[query] = solver(queries[query]); } @@ -28,7 +28,7 @@ function handleSMTQueries (inputJSON, outputJSON, solver) { function smtCallback (solver) { return function (query) { try { - var result = solver(query); + const result = solver(query); return { contents: result }; } catch (err) { return { error: err }; diff --git a/smtsolver.js b/smtsolver.js index a38030ac..9c163fce 100644 --- a/smtsolver.js +++ b/smtsolver.js @@ -1,11 +1,11 @@ -var commandExistsSync = require('command-exists').sync; -var execSync = require('child_process').execSync; -var fs = require('fs'); -var tmp = require('tmp'); +const commandExistsSync = require('command-exists').sync; +const execSync = require('child_process').execSync; +const fs = require('fs'); +const tmp = require('tmp'); const timeout = 10000; -var potentialSolvers = [ +const potentialSolvers = [ { name: 'z3', params: '-smt2 rlimit=20000000 rewriter.pull_cheap_ite=true fp.spacer.q3.use_qgen=true fp.spacer.mbqi=false fp.spacer.ground_pobs=false' @@ -15,21 +15,21 @@ var potentialSolvers = [ params: '--lang=smt2 --tlimit=' + timeout } ]; -var solvers = potentialSolvers.filter(solver => commandExistsSync(solver.name)); +const solvers = potentialSolvers.filter(solver => commandExistsSync(solver.name)); function solve (query) { if (solvers.length === 0) { throw new Error('No SMT solver available. Assertion checking will not be performed.'); } - var tmpFile = tmp.fileSync({ postfix: '.smt2' }); + const tmpFile = tmp.fileSync({ postfix: '.smt2' }); fs.writeFileSync(tmpFile.name, query); // TODO For now only the first SMT solver found is used. // At some point a computation similar to the one done in // SMTPortfolio::check should be performed, where the results // given by different solvers are compared and an error is // reported if solvers disagree (i.e. SAT vs UNSAT). - var solverOutput; + let solverOutput; try { solverOutput = execSync( solvers[0].name + ' ' + solvers[0].params + ' ' + tmpFile.name, { diff --git a/test/abi.js b/test/abi.js index 06f555e3..63cd7fa7 100644 --- a/test/abi.js +++ b/test/abi.js @@ -7,67 +7,67 @@ tape('ABI translator', function (t) { st.end(); }); t.test('0.1.1 (no constructor)', function (st) { - st.deepEqual(abi.update('0.1.1', []), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + st.deepEqual(abi.update('0.1.1', []), [{ inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' }, { payable: true, stateMutability: 'payable', type: 'fallback' }]); st.end(); }); t.test('0.3.6 (constructor)', function (st) { - var input = [ { inputs: [], type: 'constructor' } ]; - st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + const input = [{ inputs: [], type: 'constructor' }]; + st.deepEqual(abi.update('0.3.6', input), [{ inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' }, { payable: true, stateMutability: 'payable', type: 'fallback' }]); st.end(); }); t.test('0.3.6 (non-constant function)', function (st) { - var input = [ { inputs: [], type: 'function' } ]; - st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + const input = [{ inputs: [], type: 'function' }]; + st.deepEqual(abi.update('0.3.6', input), [{ inputs: [], payable: true, stateMutability: 'payable', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' }]); st.end(); }); t.test('0.3.6 (constant function)', function (st) { - var input = [ { inputs: [], type: 'function', constant: true } ]; - st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], constant: true, stateMutability: 'view', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + const input = [{ inputs: [], type: 'function', constant: true }]; + st.deepEqual(abi.update('0.3.6', input), [{ inputs: [], constant: true, stateMutability: 'view', type: 'function' }, { payable: true, stateMutability: 'payable', type: 'fallback' }]); st.end(); }); t.test('0.3.6 (event)', function (st) { - var input = [ { inputs: [], type: 'event' } ]; - st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], type: 'event' }, { payable: true, stateMutability: 'payable', type: 'fallback' } ]); + const input = [{ inputs: [], type: 'event' }]; + st.deepEqual(abi.update('0.3.6', input), [{ inputs: [], type: 'event' }, { payable: true, stateMutability: 'payable', type: 'fallback' }]); st.end(); }); t.test('0.3.6 (has no fallback)', function (st) { - var input = [ { inputs: [], type: 'constructor' } ]; - st.deepEqual(abi.update('0.3.6', input), [ { inputs: [], type: 'constructor', payable: true, stateMutability: 'payable' }, { type: 'fallback', payable: true, stateMutability: 'payable' } ]); + const input = [{ inputs: [], type: 'constructor' }]; + st.deepEqual(abi.update('0.3.6', input), [{ inputs: [], type: 'constructor', payable: true, stateMutability: 'payable' }, { type: 'fallback', payable: true, stateMutability: 'payable' }]); st.end(); }); t.test('0.4.0 (has fallback)', function (st) { - var input = [ { inputs: [], type: 'constructor' }, { type: 'fallback' } ]; - st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], type: 'constructor', payable: true, stateMutability: 'payable' }, { type: 'fallback', stateMutability: 'nonpayable' } ]); + const input = [{ inputs: [], type: 'constructor' }, { type: 'fallback' }]; + st.deepEqual(abi.update('0.4.0', input), [{ inputs: [], type: 'constructor', payable: true, stateMutability: 'payable' }, { type: 'fallback', stateMutability: 'nonpayable' }]); st.end(); }); t.test('0.4.0 (non-constant function)', function (st) { - var input = [ { inputs: [], type: 'function' } ]; - st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], stateMutability: 'nonpayable', type: 'function' } ]); + const input = [{ inputs: [], type: 'function' }]; + st.deepEqual(abi.update('0.4.0', input), [{ inputs: [], stateMutability: 'nonpayable', type: 'function' }]); st.end(); }); t.test('0.4.0 (constant function)', function (st) { - var input = [ { inputs: [], type: 'function', constant: true } ]; - st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], constant: true, stateMutability: 'view', type: 'function' } ]); + const input = [{ inputs: [], type: 'function', constant: true }]; + st.deepEqual(abi.update('0.4.0', input), [{ inputs: [], constant: true, stateMutability: 'view', type: 'function' }]); st.end(); }); t.test('0.4.0 (payable function)', function (st) { - var input = [ { inputs: [], payable: true, type: 'function' } ]; - st.deepEqual(abi.update('0.4.0', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'function' } ]); + const input = [{ inputs: [], payable: true, type: 'function' }]; + st.deepEqual(abi.update('0.4.0', input), [{ inputs: [], payable: true, stateMutability: 'payable', type: 'function' }]); st.end(); }); t.test('0.4.1 (constructor not payable)', function (st) { - var input = [ { inputs: [], payable: false, type: 'constructor' } ]; - st.deepEqual(abi.update('0.4.1', input), [ { inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' } ]); + const input = [{ inputs: [], payable: false, type: 'constructor' }]; + st.deepEqual(abi.update('0.4.1', input), [{ inputs: [], payable: true, stateMutability: 'payable', type: 'constructor' }]); st.end(); }); t.test('0.4.5 (constructor payable)', function (st) { - var input = [ { inputs: [], payable: false, type: 'constructor' } ]; - st.deepEqual(abi.update('0.4.5', input), [ { inputs: [], payable: false, stateMutability: 'nonpayable', type: 'constructor' } ]); + const input = [{ inputs: [], payable: false, type: 'constructor' }]; + st.deepEqual(abi.update('0.4.5', input), [{ inputs: [], payable: false, stateMutability: 'nonpayable', type: 'constructor' }]); st.end(); }); t.test('0.4.16 (statemutability)', function (st) { - var input = [ { inputs: [], payable: false, stateMutability: 'pure', type: 'function' } ]; - st.deepEqual(abi.update('0.4.16', input), [ { inputs: [], payable: false, stateMutability: 'pure', type: 'function' } ]); + const input = [{ inputs: [], payable: false, stateMutability: 'pure', type: 'function' }]; + st.deepEqual(abi.update('0.4.16', input), [{ inputs: [], payable: false, stateMutability: 'pure', type: 'function' }]); st.end(); }); }); diff --git a/test/cli.js b/test/cli.js index 4f1707a1..7eb90107 100644 --- a/test/cli.js +++ b/test/cli.js @@ -5,79 +5,79 @@ const pkg = require('../package.json'); tape('CLI', function (t) { t.test('--version', function (st) { - var spt = spawn(st, './solcjs --version'); + const spt = spawn(st, './solcjs --version'); spt.stdout.match(RegExp(pkg.version + '(-[^a-zA-A0-9.+]+)?(\\+[^a-zA-Z0-9.-]+)?')); spt.stderr.empty(); spt.end(); }); t.test('no parameters', function (st) { - var spt = spawn(st, './solcjs'); + const spt = spawn(st, './solcjs'); spt.stderr.match(/^Must provide a file/); spt.end(); }); t.test('no mode specified', function (st) { - var spt = spawn(st, './solcjs test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs test/resources/fixtureSmoke.sol'); spt.stderr.match(/^Invalid option selected/); spt.end(); }); t.test('--bin', function (st) { - var spt = spawn(st, './solcjs --bin test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs --bin test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --optimize', function (st) { - var spt = spawn(st, './solcjs --bin --optimize test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs --bin --optimize test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --optimize-runs 666', function (st) { - var spt = spawn(st, './solcjs --bin --optimize-runs 666 test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs --bin --optimize-runs 666 test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --optimize-runs not-a-number', function (st) { - var spt = spawn(st, './solcjs --bin --optimize-runs not-a-number test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs --bin --optimize-runs not-a-number test/resources/fixtureSmoke.sol'); spt.stderr.match(/^error: option '--optimize-runs ' argument 'not-a-number' is invalid/); spt.end(); }); t.test('invalid file specified', function (st) { - var spt = spawn(st, './solcjs --bin test/fileNotFound.sol'); + const spt = spawn(st, './solcjs --bin test/fileNotFound.sol'); spt.stderr.match(/^Error reading /); spt.end(); }); t.test('incorrect source source', function (st) { - var spt = spawn(st, './solcjs --bin test/resources/fixtureIncorrectSource.sol'); + const spt = spawn(st, './solcjs --bin test/resources/fixtureIncorrectSource.sol'); spt.stderr.match(/SyntaxError: Invalid pragma "contract"/); spt.end(); }); t.test('--abi', function (st) { - var spt = spawn(st, './solcjs --abi test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs --abi test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --abi', function (st) { - var spt = spawn(st, './solcjs --bin --abi test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solcjs --bin --abi test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('no base path', function (st) { - var spt = spawn( + const spt = spawn( st, './solcjs --bin ' + 'test/resources/importA.sol ' + @@ -93,7 +93,7 @@ tape('CLI', function (t) { // NOTE: This and other base path tests rely on the relative ./importB.sol import in importA.sol. // If base path is not stripped correctly from all source paths below, they will not be found // by the import callback when it appends the base path back. - var spt = spawn( + const spt = spawn( st, './solcjs --bin --base-path test/resources ' + 'test/resources/importA.sol ' + @@ -106,7 +106,7 @@ tape('CLI', function (t) { }); t.test('relative non canonical base path', function (st) { - var spt = spawn( + const spt = spawn( st, './solcjs --bin --base-path ./test/resources ' + 'test/resources/importA.sol ' + @@ -119,7 +119,7 @@ tape('CLI', function (t) { }); t.test('absolute base path', function (st) { - var spt = spawn( + const spt = spawn( st, './solcjs --bin --base-path ' + path.resolve('test/resources') + ' ' + 'test/resources/importA.sol ' + @@ -174,22 +174,22 @@ tape('CLI', function (t) { }); t.test('standard json', function (st) { - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode', 'userdoc' ] + '*': ['evm.bytecode', 'userdoc'] } } }, - 'sources': { + sources: { 'Contract.sol': { - 'content': 'pragma solidity >=0.5.0; contract Contract { function f() pure public {} }' + content: 'pragma solidity >=0.5.0; contract Contract { function f() pure public {} }' } } }; - var spt = spawn(st, './solcjs --standard-json'); + const spt = spawn(st, './solcjs --standard-json'); spt.stdin.setEncoding('utf-8'); spt.stdin.write(JSON.stringify(input)); spt.stdin.end(); @@ -203,22 +203,22 @@ tape('CLI', function (t) { }); t.test('standard json base path', function (st) { - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'metadata' ] + '*': ['metadata'] } } }, - 'sources': { + sources: { 'importA.sol': { - 'content': 'import "./importB.sol";' + content: 'import "./importB.sol";' } } }; - var spt = spawn(st, './solcjs --standard-json --base-path test/resources'); + const spt = spawn(st, './solcjs --standard-json --base-path test/resources'); spt.stdin.setEncoding('utf-8'); spt.stdin.write(JSON.stringify(input)); spt.stdin.end(); @@ -231,18 +231,18 @@ tape('CLI', function (t) { }); t.test('standard json include paths', function (st) { - var input = { - 'language': 'Solidity', - 'sources': { + const input = { + language: 'Solidity', + sources: { 'contractB.sol': { - 'content': + content: '// SPDX-License-Identifier: GPL-3.0\n' + 'pragma solidity >=0.0;\n' + 'import "./contractA.sol";\n' } } }; - var spt = spawn( + const spt = spawn( st, './solcjs --standard-json ' + '--base-path test/resources/importCallback/base ' + diff --git a/test/compiler.js b/test/compiler.js index 62a8cdf8..a8adf5a3 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -5,7 +5,7 @@ const solc = require('../index.js'); const linker = require('../linker.js'); const execSync = require('child_process').execSync; -var noRemoteVersions = (process.argv.indexOf('--no-remote-versions') >= 0); +const noRemoteVersions = (process.argv.indexOf('--no-remote-versions') >= 0); function runTests (solc, versionText) { console.log(`Running tests with ${versionText} ${solc.version()}`); @@ -13,7 +13,7 @@ function runTests (solc, versionText) { function resplitFileNameOnFirstColon (fileName, contractName) { assert(!contractName.includes(':')); - let contractNameComponents = fileName.split(':'); + const contractNameComponents = fileName.split(':'); const truncatedFileName = contractNameComponents.shift(); contractNameComponents.push(contractName); @@ -22,13 +22,13 @@ function runTests (solc, versionText) { function getBytecode (output, fileName, contractName) { try { - var outputContract; + let outputContract; if (semver.lt(solc.semver(), '0.4.9')) { outputContract = output.contracts[contractName]; } else { outputContract = output.contracts[fileName + ':' + contractName]; } - return outputContract['bytecode']; + return outputContract.bytecode; } catch (e) { return ''; } @@ -36,7 +36,7 @@ function runTests (solc, versionText) { function getBytecodeStandard (output, fileName, contractName) { try { - var outputFile; + let outputFile; if (semver.lt(solc.semver(), '0.4.9')) { outputFile = output.contracts['']; } else { @@ -45,7 +45,7 @@ function runTests (solc, versionText) { } outputFile = output.contracts[fileName]; } - return outputFile[contractName]['evm']['bytecode']['object']; + return outputFile[contractName].evm.bytecode.object; } catch (e) { return ''; } @@ -53,7 +53,7 @@ function runTests (solc, versionText) { function getGasEstimate (output, fileName, contractName) { try { - var outputFile; + let outputFile; if (semver.lt(solc.semver(), '0.4.9')) { outputFile = output.contracts['']; } else { @@ -62,7 +62,7 @@ function runTests (solc, versionText) { } outputFile = output.contracts[fileName]; } - return outputFile[contractName]['evm']['gasEstimates']; + return outputFile[contractName].evm.gasEstimates; } catch (e) { return ''; } @@ -70,7 +70,7 @@ function runTests (solc, versionText) { function expectError (output, errorType, message) { if (output.errors) { - for (var error in output.errors) { + for (let error in output.errors) { error = output.errors[error]; if (error.type === errorType) { if (message) { @@ -88,7 +88,7 @@ function runTests (solc, versionText) { function expectNoError (output) { if (output.errors) { - for (var error in output.errors) { + for (let error in output.errors) { error = output.errors[error]; if (error.severity === 'error') { return false; @@ -99,7 +99,7 @@ function runTests (solc, versionText) { } tape(versionText, function (t) { - var tape = t.test; + const tape = t.test; tape('Version and license', function (t) { t.test('check version', function (st) { @@ -124,9 +124,9 @@ function runTests (solc, versionText) { return; } - var output = JSON.parse(solc.lowlevel.compileSingle('contract A { function g() public {} }')); + const output = JSON.parse(solc.lowlevel.compileSingle('contract A { function g() public {} }')); st.ok('contracts' in output); - var bytecode = getBytecode(output, '', 'A'); + const bytecode = getBytecode(output, '', 'A'); st.ok(typeof bytecode === 'string'); st.ok(bytecode.length > 0); st.end(); @@ -146,7 +146,7 @@ function runTests (solc, versionText) { return; } - var output = JSON.parse(solc.lowlevel.compileSingle('contract x { this is an invalid contract }')); + const output = JSON.parse(solc.lowlevel.compileSingle('contract x { this is an invalid contract }')); if (semver.lt(solc.semver(), '0.1.4')) { st.ok(output.error.indexOf('Parser error: Expected identifier') !== -1); st.end(); @@ -156,7 +156,7 @@ function runTests (solc, versionText) { st.ok('errors' in output); // Check if the ParserError exists, but allow others too st.ok(output.errors.length >= 1); - for (var error in output.errors) { + for (const error in output.errors) { // Error should be something like: // ParserError // Error: Expected identifier @@ -181,15 +181,15 @@ function runTests (solc, versionText) { return; } - var input = { + const input = { 'a.sol': 'contract A { function f() public returns (uint) { return 7; } }', 'b.sol': 'import "a.sol"; contract B is A { function g() public { f(); } }' }; - var output = JSON.parse(solc.lowlevel.compileMulti(JSON.stringify({sources: input}))); - var B = getBytecode(output, 'b.sol', 'B'); + const output = JSON.parse(solc.lowlevel.compileMulti(JSON.stringify({ sources: input }))); + const B = getBytecode(output, 'b.sol', 'B'); st.ok(typeof B === 'string'); st.ok(B.length > 0); - var A = getBytecode(output, 'a.sol', 'A'); + const A = getBytecode(output, 'a.sol', 'A'); st.ok(typeof A === 'string'); st.ok(A.length > 0); st.end(); @@ -203,7 +203,7 @@ function runTests (solc, versionText) { return; } - var input = { + const input = { 'b.sol': 'import "a.sol"; contract B is A { function g() public { f(); } }' }; function findImports (path) { @@ -213,11 +213,11 @@ function runTests (solc, versionText) { return { error: 'File not found' }; } } - var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports })); - var B = getBytecode(output, 'b.sol', 'B'); + const output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({ sources: input }), 0, { import: findImports })); + const B = getBytecode(output, 'b.sol', 'B'); st.ok(typeof B === 'string'); st.ok(B.length > 0); - var A = getBytecode(output, 'a.sol', 'A'); + const A = getBytecode(output, 'a.sol', 'A'); st.ok(typeof A === 'string'); st.ok(A.length > 0); st.end(); @@ -231,18 +231,18 @@ function runTests (solc, versionText) { return; } - var input = { + const input = { 'b.sol': 'import "a.sol"; contract B { function g() public { f(); } }' }; function findImports (path) { return { error: 'File not found' }; } - var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports })); + const output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({ sources: input }), 0, { import: findImports })); st.plan(3); st.ok('errors' in output); // Check if the ParserError exists, but allow others too st.ok(output.errors.length >= 1); - for (var error in output.errors) { + for (const error in output.errors) { // Error should be something like: // cont.sol:1:1: ParserError: Source "lib.sol" not found: File not found // cont.sol:1:1: Error: Source "lib.sol" not found: File not found @@ -265,14 +265,14 @@ function runTests (solc, versionText) { return; } - var input = { + const input = { 'b.sol': 'import "a.sol"; contract B { function g() public { f(); } }' }; function findImports (path) { throw new Error('Could not implement this interface properly...'); } st.throws(function () { - solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports }); + solc.lowlevel.compileCallback(JSON.stringify({ sources: input }), 0, { import: findImports }); }, /^Error: Could not implement this interface properly.../); st.end(); }); @@ -285,11 +285,11 @@ function runTests (solc, versionText) { return; } - var input = { + const input = { 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' }; st.throws(function () { - solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, "this isn't a callback"); + solc.lowlevel.compileCallback(JSON.stringify({ sources: input }), 0, "this isn't a callback"); }, /Invalid callback object specified./); st.end(); }); @@ -302,15 +302,15 @@ function runTests (solc, versionText) { return; } - var input = { + const input = { 'b.sol': 'import "a.sol"; contract B is A { function g() public { f(); } }' }; - var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}))); + const output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({ sources: input }))); st.plan(3); st.ok('errors' in output); // Check if the ParserError exists, but allow others too st.ok(output.errors.length >= 1); - for (var error in output.errors) { + for (const error in output.errors) { // Error should be something like: // cont.sol:1:1: ParserError: Source "lib.sol" not found: File import callback not supported // cont.sol:1:1: Error: Source "lib.sol" not found: File import callback not supported @@ -332,34 +332,34 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'a.sol': { - 'content': 'contract A { function f() public returns (uint) { return 7; } }' + content: 'contract A { function f() public returns (uint) { return 7; } }' }, 'b.sol': { - 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' + content: 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; function bytecodeExists (output, fileName, contractName) { try { - return output.contracts[fileName][contractName]['evm']['bytecode']['object'].length > 0; + return output.contracts[fileName][contractName].evm.bytecode.object.length > 0; } catch (e) { return false; } } - var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); + const output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); st.ok(bytecodeExists(output, 'a.sol', 'A')); st.ok(bytecodeExists(output, 'b.sol', 'B')); st.end(); @@ -379,27 +379,27 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'x.sol': { - 'content': 'contract x { this is an invalid contract }' + content: 'contract x { this is an invalid contract }' } } }; - var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); + const output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); st.plan(3); st.ok('errors' in output); st.ok(output.errors.length >= 1); // Check if the ParserError exists, but allow others too - for (var error in output.errors) { + for (const error in output.errors) { if (output.errors[error].type === 'ParserError') { st.ok(true); } @@ -414,18 +414,18 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'b.sol': { - 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' + content: 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; @@ -440,48 +440,48 @@ function runTests (solc, versionText) { function bytecodeExists (output, fileName, contractName) { try { - return output.contracts[fileName][contractName]['evm']['bytecode']['object'].length > 0; + return output.contracts[fileName][contractName].evm.bytecode.object.length > 0; } catch (e) { return false; } } - var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), { import: findImports })); + const output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), { import: findImports })); st.ok(bytecodeExists(output, 'a.sol', 'A')); st.ok(bytecodeExists(output, 'b.sol', 'B')); st.end(); }); t.test('compiling standard JSON (single file)', function (st) { - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode', 'evm.gasEstimates' ] + '*': ['evm.bytecode', 'evm.gasEstimates'] } } }, - 'sources': { + sources: { 'c.sol': { - 'content': 'contract C { function g() public { } function h() internal {} }' + content: 'contract C { function g() public { } function h() internal {} }' } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var C = getBytecodeStandard(output, 'c.sol', 'C'); + const C = getBytecodeStandard(output, 'c.sol', 'C'); st.ok(typeof C === 'string'); st.ok(C.length > 0); - var CGas = getGasEstimate(output, 'c.sol', 'C'); + const CGas = getGasEstimate(output, 'c.sol', 'C'); st.ok(typeof CGas === 'object'); - st.ok(typeof CGas['creation'] === 'object'); - st.ok(typeof CGas['creation']['codeDepositCost'] === 'string'); - st.ok(typeof CGas['external'] === 'object'); - st.ok(typeof CGas['external']['g()'] === 'string'); - st.ok(typeof CGas['internal'] === 'object'); - st.ok(typeof CGas['internal']['h()'] === 'string'); + st.ok(typeof CGas.creation === 'object'); + st.ok(typeof CGas.creation.codeDepositCost === 'string'); + st.ok(typeof CGas.external === 'object'); + st.ok(typeof CGas.external['g()'] === 'string'); + st.ok(typeof CGas.internal === 'object'); + st.ok(typeof CGas.internal['h()'] === 'string'); st.end(); }); @@ -493,40 +493,40 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode', 'evm.gasEstimates' ] + '*': ['evm.bytecode', 'evm.gasEstimates'] } } }, - 'sources': { + sources: { 'a.sol': { - 'content': 'contract A { function f() public returns (uint) { return 7; } }' + content: 'contract A { function f() public returns (uint) { return 7; } }' }, 'b.sol': { - 'content': 'import "a.sol"; contract B is A { function g() public { f(); } function h() internal {} }' + content: 'import "a.sol"; contract B is A { function g() public { f(); } function h() internal {} }' } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var B = getBytecodeStandard(output, 'b.sol', 'B'); + const B = getBytecodeStandard(output, 'b.sol', 'B'); st.ok(typeof B === 'string'); st.ok(B.length > 0); st.ok(Object.keys(linker.findLinkReferences(B)).length === 0); - var BGas = getGasEstimate(output, 'b.sol', 'B'); + const BGas = getGasEstimate(output, 'b.sol', 'B'); st.ok(typeof BGas === 'object'); - st.ok(typeof BGas['creation'] === 'object'); - st.ok(typeof BGas['creation']['codeDepositCost'] === 'string'); - st.ok(typeof BGas['external'] === 'object'); - st.ok(typeof BGas['external']['g()'] === 'string'); - st.ok(typeof BGas['internal'] === 'object'); - st.ok(typeof BGas['internal']['h()'] === 'string'); - var A = getBytecodeStandard(output, 'a.sol', 'A'); + st.ok(typeof BGas.creation === 'object'); + st.ok(typeof BGas.creation.codeDepositCost === 'string'); + st.ok(typeof BGas.external === 'object'); + st.ok(typeof BGas.external['g()'] === 'string'); + st.ok(typeof BGas.internal === 'object'); + st.ok(typeof BGas.internal['h()'] === 'string'); + const A = getBytecodeStandard(output, 'a.sol', 'A'); st.ok(typeof A === 'string'); st.ok(A.length > 0); st.end(); @@ -540,33 +540,33 @@ function runTests (solc, versionText) { return; } - var isVersion6 = semver.gt(solc.semver(), '0.5.99'); - var source; + const isVersion6 = semver.gt(solc.semver(), '0.5.99'); + let source; if (isVersion6) { source = 'abstract contract C { function f() public virtual; }'; } else { source = 'contract C { function f() public; }'; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode', 'evm.gasEstimates' ] + '*': ['evm.bytecode', 'evm.gasEstimates'] } } }, - 'sources': { + sources: { 'c.sol': { - 'content': source + content: source } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var C = getBytecodeStandard(output, 'c.sol', 'C'); + const C = getBytecodeStandard(output, 'c.sol', 'C'); st.ok(typeof C === 'string'); st.ok(C.length === 0); st.end(); @@ -580,18 +580,18 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'b.sol': { - 'content': 'import "a.sol"; contract B is A { function g() public { f(); } }' + content: 'import "a.sol"; contract B is A { function g() public { f(); } }' } } }; @@ -604,12 +604,12 @@ function runTests (solc, versionText) { } } - var output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); + const output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports })); st.ok(expectNoError(output)); - var A = getBytecodeStandard(output, 'a.sol', 'A'); + const A = getBytecodeStandard(output, 'a.sol', 'A'); st.ok(typeof A === 'string'); st.ok(A.length > 0); - var B = getBytecodeStandard(output, 'b.sol', 'B'); + const B = getBytecodeStandard(output, 'b.sol', 'B'); st.ok(typeof B === 'string'); st.ok(B.length > 0); st.ok(Object.keys(linker.findLinkReferences(B)).length === 0); @@ -631,37 +631,37 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'libraries': { + const input = { + language: 'Solidity', + settings: { + libraries: { 'lib.sol': { - 'L': '0x4200000000000000000000000000000000000001' + L: '0x4200000000000000000000000000000000000001' } }, - 'outputSelection': { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'lib.sol': { - 'content': 'library L { function f() public returns (uint) { return 7; } }' + content: 'library L { function f() public returns (uint) { return 7; } }' }, 'a.sol': { - 'content': 'import "lib.sol"; contract A { function g() public { L.f(); } }' + content: 'import "lib.sol"; contract A { function g() public { L.f(); } }' } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var A = getBytecodeStandard(output, 'a.sol', 'A'); + const A = getBytecodeStandard(output, 'a.sol', 'A'); st.ok(typeof A === 'string'); st.ok(A.length > 0); st.ok(Object.keys(linker.findLinkReferences(A)).length === 0); - var L = getBytecodeStandard(output, 'lib.sol', 'L'); + const L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); st.end(); @@ -675,23 +675,23 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'c.sol': { - 'content': 'contract C { function f() public { } }' + content: 'contract C { function f() public { } }' } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectError(output, 'Warning', 'Source file does not specify required compiler version!')); st.end(); }); @@ -710,63 +710,63 @@ function runTests (solc, versionText) { return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'libraries': { + const input = { + language: 'Solidity', + settings: { + libraries: { 'lib.sol': { - 'L': '0x4200000000000000000000000000000000000001' + L: '0x4200000000000000000000000000000000000001' } }, - 'outputSelection': { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'lib.sol': { - 'content': 'library L { function f() public returns (uint) { return 7; } }' + content: 'library L { function f() public returns (uint) { return 7; } }' }, 'a.sol': { - 'content': 'import "lib.sol"; contract A { function g() public { L.f(); } }' + content: 'import "lib.sol"; contract A { function g() public { L.f(); } }' } } }; - var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); + const output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input))); st.ok(expectNoError(output)); - var A = getBytecodeStandard(output, 'a.sol', 'A'); + const A = getBytecodeStandard(output, 'a.sol', 'A'); st.ok(typeof A === 'string'); st.ok(A.length > 0); st.ok(Object.keys(linker.findLinkReferences(A)).length === 0); - var L = getBytecodeStandard(output, 'lib.sol', 'L'); + const L = getBytecodeStandard(output, 'lib.sol', 'L'); st.ok(typeof L === 'string'); st.ok(L.length > 0); st.end(); }); t.test('compiling standard JSON (invalid JSON)', function (st) { - var output = JSON.parse(solc.compile('{invalid')); + const output = JSON.parse(solc.compile('{invalid')); // TODO: change wrapper to output matching error st.ok(expectError(output, 'JSONError', 'Line 1, Column 2\n Missing \'}\' or object member name') || expectError(output, 'JSONError', 'Invalid JSON supplied:')); st.end(); }); t.test('compiling standard JSON (invalid language)', function (st) { - var output = JSON.parse(solc.compile('{"language":"InvalidSolidity","sources":{"cont.sol":{"content":""}}}')); + const output = JSON.parse(solc.compile('{"language":"InvalidSolidity","sources":{"cont.sol":{"content":""}}}')); st.ok(expectError(output, 'JSONError', 'supported as a language.') && expectError(output, 'JSONError', '"Solidity"')); st.end(); }); t.test('compiling standard JSON (no sources)', function (st) { - var output = JSON.parse(solc.compile('{"language":"Solidity"}')); + const output = JSON.parse(solc.compile('{"language":"Solidity"}')); st.ok(expectError(output, 'JSONError', 'No input sources specified.')); st.end(); }); t.test('compiling standard JSON (multiple sources on old compiler)', function (st) { - var output = JSON.parse(solc.compile('{"language":"Solidity","sources":{"cont.sol":{"content":"import \\"lib.sol\\";"},"lib.sol":{"content":""}}}')); + const output = JSON.parse(solc.compile('{"language":"Solidity","sources":{"cont.sol":{"content":"import \\"lib.sol\\";"},"lib.sol":{"content":""}}}')); if (solc.features.multipleInputs) { st.ok(expectNoError(output)); } else { @@ -776,50 +776,50 @@ function runTests (solc, versionText) { }); t.test('compiling standard JSON (file names containing symbols)', function (st) { - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { '!@#$%^&*()_+-=[]{}\\|"\';:~`<>,.?/': { - 'content': 'contract C {}' + content: 'contract C {}' } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var C = getBytecodeStandard(output, '!@#$%^&*()_+-=[]{}\\|"\';:~`<>,.?/', 'C'); + const C = getBytecodeStandard(output, '!@#$%^&*()_+-=[]{}\\|"\';:~`<>,.?/', 'C'); st.ok(typeof C === 'string'); st.ok(C.length > 0); st.end(); }); t.test('compiling standard JSON (file names containing multiple semicolons)', function (st) { - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'a:b:c:d:e:f:G.sol': { - 'content': 'contract G {}' + content: 'contract G {}' } } }; - var output = JSON.parse(solc.compile(JSON.stringify(input))); + const output = JSON.parse(solc.compile(JSON.stringify(input))); st.ok(expectNoError(output)); - var G = getBytecodeStandard(output, 'a:b:c:d:e:f:G.sol', 'G'); + const G = getBytecodeStandard(output, 'a:b:c:d:e:f:G.sol', 'G'); st.ok(typeof G === 'string'); st.ok(G.length > 0); st.end(); @@ -840,23 +840,23 @@ function runTests (solc, versionText) { st.end(); return; } - var input = { - 'language': 'Solidity', - 'settings': { - 'outputSelection': { + const input = { + language: 'Solidity', + settings: { + outputSelection: { '*': { - '*': [ 'evm.bytecode' ] + '*': ['evm.bytecode'] } } }, - 'sources': { + sources: { 'cont.sol': { - 'content': 'contract x { function g() public {} }' + content: 'contract x { function g() public {} }' } } }; - var output = JSON.parse(solcSnapshot.compile(JSON.stringify(input))); - var x = getBytecodeStandard(output, 'cont.sol', 'x'); + const output = JSON.parse(solcSnapshot.compile(JSON.stringify(input))); + const x = getBytecodeStandard(output, 'cont.sol', 'x'); st.ok(typeof x === 'string'); st.ok(x.length > 0); }); @@ -885,7 +885,7 @@ if (!noRemoteVersions) { 'v0.4.20+commit.3155dd80', 'v0.4.26+commit.4563c3fc' ]; - for (var version in versions) { + for (let version in versions) { version = versions[version]; execSync(`curl -L -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); diff --git a/test/linker.js b/test/linker.js index 0ddc6401..e7893742 100644 --- a/test/linker.js +++ b/test/linker.js @@ -13,59 +13,59 @@ tape('Link references', function (t) { }); t.test('One reference', function (st) { - var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058207979b30bd4a07c77b02774a511f2a1dd04d7e5d65b5c2735b5fc96ad61d43ae40029'; - st.deepEqual(linker.findLinkReferences(bytecode), { 'lib2.sol:L': [ { start: 116, length: 20 } ] }); + const bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058207979b30bd4a07c77b02774a511f2a1dd04d7e5d65b5c2735b5fc96ad61d43ae40029'; + st.deepEqual(linker.findLinkReferences(bytecode), { 'lib2.sol:L': [{ start: 116, length: 20 }] }); st.end(); }); t.test('Two references', function (st) { - var bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__linkref.sol:Lx________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029'; + const bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__linkref.sol:Lx________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029'; st.deepEqual( linker.findLinkReferences(bytecode), - { 'lib2.sol:L': [ { start: 92, length: 20 } ], 'linkref.sol:Lx': [ { start: 180, length: 20 } ] } + { 'lib2.sol:L': [{ start: 92, length: 20 }], 'linkref.sol:Lx': [{ start: 180, length: 20 }] } ); st.end(); }); t.test('Library name with leading underscore', function (st) { - var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:_L___________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582089689827bbf0b7dc385ffcb4b1deb9f9e61741f61f89b4af65f806ff2b0d73470029'; + const bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:_L___________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582089689827bbf0b7dc385ffcb4b1deb9f9e61741f61f89b4af65f806ff2b0d73470029'; st.deepEqual( linker.findLinkReferences(bytecode), - { 'lib2.sol:_L': [ { start: 116, length: 20 } ] } + { 'lib2.sol:_L': [{ start: 116, length: 20 }] } ); st.end(); }); t.test('Library name with leading underscore (without fqdn)', function (st) { - var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73___L____________________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582089689827bbf0b7dc385ffcb4b1deb9f9e61741f61f89b4af65f806ff2b0d73470029'; + const bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73___L____________________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582089689827bbf0b7dc385ffcb4b1deb9f9e61741f61f89b4af65f806ff2b0d73470029'; st.deepEqual( linker.findLinkReferences(bytecode), - { '_L': [ { start: 116, length: 20 } ] } + { _L: [{ start: 116, length: 20 }] } ); st.end(); }); t.test('Library name with underscore in the name', function (st) { - var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L_L__________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058205cb324a27452cc7f8894a57cb0e3ddce2dce0c423e4fc993a3dd51287abd49110029'; + const bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L_L__________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058205cb324a27452cc7f8894a57cb0e3ddce2dce0c423e4fc993a3dd51287abd49110029'; st.deepEqual( linker.findLinkReferences(bytecode), - { 'lib2.sol:L_L': [ { start: 116, length: 20 } ] } + { 'lib2.sol:L_L': [{ start: 116, length: 20 }] } ); st.end(); }); // Note: this is a case the reference finder cannot properly handle as there's no way to tell t.test('Library name with trailing underscore', function (st) { - var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582058e61511a603707222cfa83fd3ae4269f94eb86513cb9042cf0b44877403d85c0029'; + const bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582058e61511a603707222cfa83fd3ae4269f94eb86513cb9042cf0b44877403d85c0029'; st.deepEqual( linker.findLinkReferences(bytecode), - { 'lib2.sol:L': [ { start: 116, length: 20 } ] } + { 'lib2.sol:L': [{ start: 116, length: 20 }] } ); st.end(); }); t.test('Invalid input (too short)', function (st) { - var bytecode = '6060604052341561000____66606060606060'; + const bytecode = '6060604052341561000____66606060606060'; st.deepEqual( linker.findLinkReferences(bytecode), {} @@ -74,7 +74,7 @@ tape('Link references', function (t) { }); t.test('Invalid input (1 byte short)', function (st) { - var bytecode = '6060604052341561000__lib2.sol:L___________________________66606060606060'; + const bytecode = '6060604052341561000__lib2.sol:L___________________________66606060606060'; st.deepEqual( linker.findLinkReferences(bytecode), {} @@ -83,10 +83,10 @@ tape('Link references', function (t) { }); t.test('Two references with same library name', function (st) { - var bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029'; + const bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029'; st.deepEqual( linker.findLinkReferences(bytecode), - { 'lib2.sol:L': [ { start: 92, length: 20 }, { start: 180, length: 20 } ] } + { 'lib2.sol:L': [{ start: 92, length: 20 }, { start: 180, length: 20 }] } ); st.end(); }); @@ -98,7 +98,7 @@ tape('Linking', function (t) { 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' */ - var bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; bytecode = linker.linkBytecode(bytecode, { 'lib.sol:L': '0x123456' }); st.ok(bytecode.indexOf('_') < 0); st.end(); @@ -109,8 +109,8 @@ tape('Linking', function (t) { 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' */ - var bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; - bytecode = linker.linkBytecode(bytecode, { 'lib.sol': { 'L': '0x123456' } }); + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + bytecode = linker.linkBytecode(bytecode, { 'lib.sol': { L: '0x123456' } }); st.ok(bytecode.indexOf('_') < 0); st.end(); }); @@ -120,7 +120,7 @@ tape('Linking', function (t) { 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' */ - var bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; bytecode = linker.linkBytecode(bytecode, { }); st.ok(bytecode.indexOf('_') >= 0); st.end(); @@ -131,7 +131,7 @@ tape('Linking', function (t) { 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' */ - var bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + const bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; st.throws(function () { linker.linkBytecode(bytecode, { 'lib.sol:L': '' }); }); @@ -143,14 +143,14 @@ tape('Linking', function (t) { 'lib.sol': 'library L1234567890123456789012345678901234567890 { function f() public returns (uint) { return 7; } }', 'cont.sol': 'import "lib.sol"; contract x { function g() public { L1234567890123456789012345678901234567890.f(); } }' */ - var bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L123456789012345678901234567__6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a723058209f88ff686bd8ceb0fc08853dc1332d5ff81dbcf5af3a1e9aa366828091761f8c0029'; + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L123456789012345678901234567__6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a723058209f88ff686bd8ceb0fc08853dc1332d5ff81dbcf5af3a1e9aa366828091761f8c0029'; bytecode = linker.linkBytecode(bytecode, { 'lib.sol:L1234567890123456789012345678901234567890': '0x123456' }); st.ok(bytecode.indexOf('_') < 0); st.end(); }); t.test('hashed placeholder', function (st) { - var bytecode = '6060604052341561000__$cb901161e812ceb78cfe30ca65050c4337$__66606060606060'; + let bytecode = '6060604052341561000__$cb901161e812ceb78cfe30ca65050c4337$__66606060606060'; bytecode = linker.linkBytecode(bytecode, { 'lib2.sol:L': '0x123456' }); st.equal(bytecode, '6060604052341561000000000000000000000000000000000000012345666606060606060'); st.end(); @@ -161,8 +161,8 @@ tape('Linking', function (t) { 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' */ - var bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__libName_______________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; - bytecode = linker.linkBytecode(bytecode, { 'libName': '0x123456' }); + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__libName_______________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + bytecode = linker.linkBytecode(bytecode, { libName: '0x123456' }); st.ok(bytecode.indexOf('_') < 0); st.end(); }); diff --git a/test/smtcallback.js b/test/smtcallback.js index 09cfd283..4314fe1a 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -7,16 +7,16 @@ const solc = require('../index.js'); const smtchecker = require('../smtchecker.js'); const smtsolver = require('../smtsolver.js'); -var preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; +const preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; function collectErrors (solOutput) { if (solOutput === undefined) { return []; } - var errors = []; - for (var i in solOutput.errors) { - var error = solOutput.errors[i]; + const errors = []; + for (const i in solOutput.errors) { + const error = solOutput.errors[i]; if (error.message.includes('This is a pre-release compiler version')) { continue; } @@ -30,7 +30,7 @@ function expectErrors (expectations, errors, ignoreCex) { return false; } - for (var i in errors) { + for (const i in errors) { if (errors[i].includes('Error trying to invoke SMT solver') || expectations[i].includes('Error trying to invoke SMT solver')) { continue; } @@ -62,18 +62,18 @@ tape('SMTCheckerCallback', function (t) { return; } - var satCallback = function (query) { + const satCallback = function (query) { return { contents: 'sat\n' }; }; - var unsatCallback = function (query) { + const unsatCallback = function (query) { return { contents: 'unsat\n' }; }; - var errorCallback = function (query) { + const errorCallback = function (query) { return { error: 'Fake SMT solver error.' }; }; - var pragmaSMT = ''; - var settings = {}; + let pragmaSMT = ''; + let settings = {}; // `pragma experimental SMTChecker;` was deprecated in 0.8.4 if (!semver.gt(solc.semver(), '0.8.3')) { pragmaSMT = 'pragma experimental SMTChecker;\n'; @@ -81,14 +81,14 @@ tape('SMTCheckerCallback', function (t) { settings = { modelChecker: { engine: 'all' } }; } - var input = { 'a': { content: preamble + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } }; - var inputJSON = JSON.stringify({ + const input = { a: { content: preamble + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } }; + const inputJSON = JSON.stringify({ language: 'Solidity', sources: input, settings: settings }); - var tests; + let tests; if (!semver.gt(solc.semver(), '0.6.8')) { // Up to version 0.6.8 there were no embedded solvers. tests = [ @@ -112,20 +112,20 @@ tape('SMTCheckerCallback', function (t) { ]; } - for (var i in tests) { - var test = tests[i]; - var output = JSON.parse(solc.compile( + for (const i in tests) { + const test = tests[i]; + const output = JSON.parse(solc.compile( inputJSON, { smtSolver: test.cb } )); - var errors = collectErrors(output); + const errors = collectErrors(output); st.ok(expectErrors(errors, test.expectations)); } st.end(); }); t.test('Solidity smtCheckerTests', function (st) { - var testdir = path.resolve(__dirname, 'smtCheckerTests/'); + const testdir = path.resolve(__dirname, 'smtCheckerTests/'); if (!fs.existsSync(testdir)) { st.skip('SMT checker tests not present.'); st.end(); @@ -138,16 +138,16 @@ tape('SMTCheckerCallback', function (t) { return; } - var sources = []; + const sources = []; // BFS to get all test files - var dirs = [testdir]; - var i; + const dirs = [testdir]; + let i; while (dirs.length > 0) { - var dir = dirs.shift(); - var files = fs.readdirSync(dir); + const dir = dirs.shift(); + const files = fs.readdirSync(dir); for (i in files) { - var file = path.join(dir, files[i]); + const file = path.join(dir, files[i]); if (fs.statSync(file).isDirectory()) { dirs.push(file); } else { @@ -157,29 +157,29 @@ tape('SMTCheckerCallback', function (t) { } // Read tests and collect expectations - var tests = []; + const tests = []; for (i in sources) { st.comment('Collecting ' + sources[i] + '...'); - var source = fs.readFileSync(sources[i], 'utf8'); + const source = fs.readFileSync(sources[i], 'utf8'); - var engine; - var option = '// SMTEngine: '; + let engine; + const option = '// SMTEngine: '; if (source.includes(option)) { - let idx = source.indexOf(option); + const idx = source.indexOf(option); if (source.indexOf(option, idx + 1) !== -1) { st.skip('SMTEngine option given multiple times.'); st.end(); return; } - let re = new RegExp(option + '(\\w+)'); - let m = source.match(re); + const re = new RegExp(option + '(\\w+)'); + const m = source.match(re); assert(m !== undefined); assert(m.length >= 2); engine = m[1]; } - var expected = []; - var delimiter = '// ----'; + let expected = []; + const delimiter = '// ----'; if (source.includes(delimiter)) { expected = source.substring(source.indexOf('// ----') + 8, source.length).split('\n'); // Sometimes the last expectation line ends with a '\n' @@ -197,23 +197,23 @@ tape('SMTCheckerCallback', function (t) { // Run all tests for (i in tests) { - var test = tests[i]; + const test = tests[i]; // Z3's nondeterminism sometimes causes a test to timeout in one context but not in the other, // so if we see timeout we skip a potentially misleading run. - var findError = (errorMsg) => { return errorMsg.includes('Error trying to invoke SMT solver'); }; + const findError = (errorMsg) => { return errorMsg.includes('Error trying to invoke SMT solver'); }; if (test.expectations.find(findError) !== undefined) { st.skip('Test contains timeout which may have been caused by nondeterminism.'); continue; } - var settings = {}; + let settings = {}; // `pragma experimental SMTChecker;` was deprecated in 0.8.4 if (semver.gt(solc.semver(), '0.8.3')) { - let engine = test.engine !== undefined ? test.engine : 'all'; + const engine = test.engine !== undefined ? test.engine : 'all'; settings = { modelChecker: { engine: engine } }; } - var output = JSON.parse(solc.compile( + const output = JSON.parse(solc.compile( JSON.stringify({ language: 'Solidity', sources: test.solidity, diff --git a/test/smtchecker.js b/test/smtchecker.js index 0ec2e24d..70563b21 100644 --- a/test/smtchecker.js +++ b/test/smtchecker.js @@ -3,22 +3,22 @@ const smtchecker = require('../smtchecker.js'); tape('SMTChecker', function (t) { t.test('smoke test with no axuiliaryInputRequested', function (st) { - var input = {}; - var output = {}; + const input = {}; + const output = {}; st.equal(smtchecker.handleSMTQueries(input, output), null); st.end(); }); t.test('smoke test with no smtlib2queries', function (st) { - var input = {}; - var output = { auxiliaryInputRequested: {} }; + const input = {}; + const output = { auxiliaryInputRequested: {} }; st.equal(smtchecker.handleSMTQueries(input, output), null); st.end(); }); t.test('smoke test with empty smtlib2queries', function (st) { - var input = {}; - var output = { auxiliaryInputRequested: { smtlib2queries: { } } }; + const input = {}; + const output = { auxiliaryInputRequested: { smtlib2queries: { } } }; st.equal(smtchecker.handleSMTQueries(input, output), null); st.end(); }); diff --git a/test/translate.js b/test/translate.js index 3d6cba9b..cf321bde 100644 --- a/test/translate.js +++ b/test/translate.js @@ -51,9 +51,9 @@ tape('Version string to Semver translator', function (t) { tape('prettyPrintLegacyAssemblyJSON', function (t) { t.test('Works properly', function (st) { - var fixtureAsmJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'resources/fixtureAsmJson.json')).toString()); - var fixtureAsmJsonSource = fs.readFileSync(path.resolve(__dirname, 'resources/fixtureAsmJson.sol')).toString(); - var fixtureAsmJsonOutput = fs.readFileSync(path.resolve(__dirname, 'resources/fixtureAsmJson.output')).toString(); + const fixtureAsmJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'resources/fixtureAsmJson.json')).toString()); + const fixtureAsmJsonSource = fs.readFileSync(path.resolve(__dirname, 'resources/fixtureAsmJson.sol')).toString(); + const fixtureAsmJsonOutput = fs.readFileSync(path.resolve(__dirname, 'resources/fixtureAsmJson.output')).toString(); st.equal(translate.prettyPrintLegacyAssemblyJSON(fixtureAsmJson, fixtureAsmJsonSource), fixtureAsmJsonOutput); st.end(); }); diff --git a/translate.js b/translate.js index 4a9d0e3d..9ee560ba 100644 --- a/translate.js +++ b/translate.js @@ -1,4 +1,4 @@ -var linker = require('./linker.js'); +const linker = require('./linker.js'); /// Translate old style version numbers to semver. /// Old style: 0.3.6-3fc68da5/Release-Emscripten/clang @@ -11,7 +11,7 @@ var linker = require('./linker.js'); /// New style: 0.4.5+commit.b318366e.Emscripten.clang function versionToSemver (version) { // FIXME: parse more detail, but this is a good start - var parsed = version.match(/^([0-9]+\.[0-9]+\.[0-9]+)-([0-9a-f]{8})[/*].*$/); + const parsed = version.match(/^([0-9]+\.[0-9]+\.[0-9]+)-([0-9a-f]{8})[/*].*$/); if (parsed) { return parsed[1] + '+commit.' + parsed[2]; } @@ -26,9 +26,9 @@ function versionToSemver (version) { } function translateErrors (ret, errors) { - for (var error in errors) { - var type = 'error'; - var extractType = /^(.*):(\d+):(\d+):(.*):/; + for (const error in errors) { + let type = 'error'; + let extractType = /^(.*):(\d+):(\d+):(.*):/; extractType = extractType.exec(errors[error]); if (extractType) { type = extractType[4].trim(); @@ -56,96 +56,96 @@ function translateGasEstimates (gasEstimates) { return gasEstimates.toString(); } - var gasEstimatesTranslated = {}; - for (var func in gasEstimates) { + const gasEstimatesTranslated = {}; + for (const func in gasEstimates) { gasEstimatesTranslated[func] = translateGasEstimates(gasEstimates[func]); } return gasEstimatesTranslated; } function translateJsonCompilerOutput (output, libraries) { - var ret = {}; + const ret = {}; - ret['errors'] = []; - var errors; - if (output['error']) { - errors = [ output['error'] ]; + ret.errors = []; + let errors; + if (output.error) { + errors = [output.error]; } else { - errors = output['errors']; + errors = output.errors; } - translateErrors(ret['errors'], errors); + translateErrors(ret.errors, errors); - ret['contracts'] = {}; - for (var contract in output['contracts']) { + ret.contracts = {}; + for (const contract in output.contracts) { // Split name first, can be `contract`, `:contract` or `filename:contract` - var tmp = contract.match(/^((.*):)?([^:]+)$/); + const tmp = contract.match(/^((.*):)?([^:]+)$/); if (tmp.length !== 4) { // Force abort return null; } - var fileName = tmp[2]; + let fileName = tmp[2]; if (fileName === undefined) { // this is the case of `contract` fileName = ''; } - var contractName = tmp[3]; + const contractName = tmp[3]; - var contractInput = output['contracts'][contract]; + const contractInput = output.contracts[contract]; - var gasEstimates = contractInput['gasEstimates']; - var translatedGasEstimates = {}; + const gasEstimates = contractInput.gasEstimates; + const translatedGasEstimates = {}; - if (gasEstimates['creation']) { - translatedGasEstimates['creation'] = { - 'codeDepositCost': translateGasEstimates(gasEstimates['creation'][1]), - 'executionCost': translateGasEstimates(gasEstimates['creation'][0]) + if (gasEstimates.creation) { + translatedGasEstimates.creation = { + codeDepositCost: translateGasEstimates(gasEstimates.creation[1]), + executionCost: translateGasEstimates(gasEstimates.creation[0]) }; } - if (gasEstimates['internal']) { - translatedGasEstimates['internal'] = translateGasEstimates(gasEstimates['internal']); + if (gasEstimates.internal) { + translatedGasEstimates.internal = translateGasEstimates(gasEstimates.internal); } - if (gasEstimates['external']) { - translatedGasEstimates['external'] = translateGasEstimates(gasEstimates['external']); + if (gasEstimates.external) { + translatedGasEstimates.external = translateGasEstimates(gasEstimates.external); } - var contractOutput = { - 'abi': JSON.parse(contractInput['interface']), - 'metadata': contractInput['metadata'], - 'evm': { - 'legacyAssembly': contractInput['assembly'], - 'bytecode': { - 'object': contractInput['bytecode'] && linker.linkBytecode(contractInput['bytecode'], libraries || {}), - 'opcodes': contractInput['opcodes'], - 'sourceMap': contractInput['srcmap'], - 'linkReferences': contractInput['bytecode'] && linker.findLinkReferences(contractInput['bytecode']) + const contractOutput = { + abi: JSON.parse(contractInput.interface), + metadata: contractInput.metadata, + evm: { + legacyAssembly: contractInput.assembly, + bytecode: { + object: contractInput.bytecode && linker.linkBytecode(contractInput.bytecode, libraries || {}), + opcodes: contractInput.opcodes, + sourceMap: contractInput.srcmap, + linkReferences: contractInput.bytecode && linker.findLinkReferences(contractInput.bytecode) }, - 'deployedBytecode': { - 'object': contractInput['runtimeBytecode'] && linker.linkBytecode(contractInput['runtimeBytecode'], libraries || {}), - 'sourceMap': contractInput['srcmapRuntime'], - 'linkReferences': contractInput['runtimeBytecode'] && linker.findLinkReferences(contractInput['runtimeBytecode']) + deployedBytecode: { + object: contractInput.runtimeBytecode && linker.linkBytecode(contractInput.runtimeBytecode, libraries || {}), + sourceMap: contractInput.srcmapRuntime, + linkReferences: contractInput.runtimeBytecode && linker.findLinkReferences(contractInput.runtimeBytecode) }, - 'methodIdentifiers': contractInput['functionHashes'], - 'gasEstimates': translatedGasEstimates + methodIdentifiers: contractInput.functionHashes, + gasEstimates: translatedGasEstimates } }; - if (!ret['contracts'][fileName]) { - ret['contracts'][fileName] = {}; + if (!ret.contracts[fileName]) { + ret.contracts[fileName] = {}; } - ret['contracts'][fileName][contractName] = contractOutput; + ret.contracts[fileName][contractName] = contractOutput; } - var sourceMap = {}; - for (var sourceId in output['sourceList']) { - sourceMap[output['sourceList'][sourceId]] = sourceId; + const sourceMap = {}; + for (const sourceId in output.sourceList) { + sourceMap[output.sourceList[sourceId]] = sourceId; } - ret['sources'] = {}; - for (var source in output['sources']) { - ret['sources'][source] = { + ret.sources = {}; + for (const source in output.sources) { + ret.sources[source] = { id: sourceMap[source], - legacyAST: output['sources'][source].AST + legacyAST: output.sources[source].AST }; } @@ -164,10 +164,10 @@ function formatAssemblyText (asm, prefix, source) { if (typeof asm === 'string' || asm === null || asm === undefined) { return prefix + (asm || '') + '\n'; } - var text = prefix + '.code\n'; + let text = prefix + '.code\n'; asm['.code'].forEach(function (item, i) { - var v = item.value === undefined ? '' : item.value; - var src = ''; + const v = item.value === undefined ? '' : item.value; + let src = ''; if (source !== undefined && item.begin !== undefined && item.end !== undefined) { src = escapeString(source.slice(item.begin, item.end)); } @@ -180,9 +180,9 @@ function formatAssemblyText (asm, prefix, source) { text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n'; }); text += prefix + '.data\n'; - var asmData = asm['.data'] || []; - for (var i in asmData) { - var item = asmData[i]; + const asmData = asm['.data'] || []; + for (const i in asmData) { + const item = asmData[i]; text += ' ' + prefix + '' + i + ':\n'; text += formatAssemblyText(item, prefix + ' ', source); } diff --git a/verifyVersion.js b/verifyVersion.js index 6cd0568c..497b3f39 100755 --- a/verifyVersion.js +++ b/verifyVersion.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -var semver = require('semver'); +const semver = require('semver'); -var packageVersion = require('./package.json').version; -var solcVersion = require('./index.js').version(); +const packageVersion = require('./package.json').version; +const solcVersion = require('./index.js').version(); console.log('solcVersion: ' + solcVersion); console.log('packageVersion: ' + packageVersion); diff --git a/wrapper.js b/wrapper.js index e337399e..516f8479 100755 --- a/wrapper.js +++ b/wrapper.js @@ -1,25 +1,25 @@ -var assert = require('assert'); -var translate = require('./translate.js'); -var requireFromString = require('require-from-string'); -var https = require('follow-redirects').https; -var MemoryStream = require('memorystream'); -var semver = require('semver'); +const assert = require('assert'); +const translate = require('./translate.js'); +const requireFromString = require('require-from-string'); +const https = require('follow-redirects').https; +const MemoryStream = require('memorystream'); +const semver = require('semver'); function setupMethods (soljson) { - var version; + let version; if ('_solidity_version' in soljson) { version = soljson.cwrap('solidity_version', 'string', []); } else { version = soljson.cwrap('version', 'string', []); } - var versionToSemver = function () { + const versionToSemver = function () { return translate.versionToSemver(version()); }; - var isVersion6 = semver.gt(versionToSemver(), '0.5.99'); + const isVersion6 = semver.gt(versionToSemver(), '0.5.99'); - var license; + let license; if ('_solidity_license' in soljson) { license = soljson.cwrap('solidity_license', 'string', []); } else if ('_license' in soljson) { @@ -31,21 +31,21 @@ function setupMethods (soljson) { }; } - var alloc; + let alloc; if ('_solidity_alloc' in soljson) { - alloc = soljson.cwrap('solidity_alloc', 'number', [ 'number' ]); + alloc = soljson.cwrap('solidity_alloc', 'number', ['number']); } else { alloc = soljson._malloc; assert(alloc, 'Expected malloc to be present.'); } - var reset; + let reset; if ('_solidity_reset' in soljson) { reset = soljson.cwrap('solidity_reset', null, []); } - var copyToCString = function (str, ptr) { - var length = soljson.lengthBytesUTF8(str); + const copyToCString = function (str, ptr) { + const length = soljson.lengthBytesUTF8(str); // This is allocating memory using solc's allocator. // // Before 0.6.0: @@ -54,19 +54,19 @@ function setupMethods (soljson) { // // After 0.6.0: // The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end. - var buffer = alloc(length + 1); + const buffer = alloc(length + 1); soljson.stringToUTF8(str, buffer, length + 1); soljson.setValue(ptr, buffer, '*'); }; // This is to support multiple versions of Emscripten. // Take a single `ptr` and returns a `str`. - var copyFromCString = soljson.UTF8ToString || soljson.Pointer_stringify; + const copyFromCString = soljson.UTF8ToString || soljson.Pointer_stringify; - var wrapCallback = function (callback) { + const wrapCallback = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); return function (data, contents, error) { - var result = callback(copyFromCString(data)); + const result = callback(copyFromCString(data)); if (typeof result.contents === 'string') { copyToCString(result.contents, contents); } @@ -76,12 +76,12 @@ function setupMethods (soljson) { }; }; - var wrapCallbackWithKind = function (callback) { + const wrapCallbackWithKind = function (callback) { assert(typeof callback === 'function', 'Invalid callback specified.'); return function (context, kind, data, contents, error) { // Must be a null pointer. assert(context === 0, 'Callback context must be null.'); - var result = callback(copyFromCString(kind), copyFromCString(data)); + const result = callback(copyFromCString(kind), copyFromCString(data)); if (typeof result.contents === 'string') { copyToCString(result.contents, contents); } @@ -92,14 +92,14 @@ function setupMethods (soljson) { }; // This calls compile() with args || cb - var runWithCallbacks = function (callbacks, compile, args) { + const runWithCallbacks = function (callbacks, compile, args) { if (callbacks) { assert(typeof callbacks === 'object', 'Invalid callback object specified.'); } else { callbacks = {}; } - var readCallback = callbacks.import; + let readCallback = callbacks.import; if (readCallback === undefined) { readCallback = function (data) { return { @@ -108,10 +108,10 @@ function setupMethods (soljson) { }; } - var singleCallback; + let singleCallback; if (isVersion6) { // After 0.6.x multiple kind of callbacks are supported. - var smtSolverCallback = callbacks.smtSolver; + let smtSolverCallback = callbacks.smtSolver; if (smtSolverCallback === undefined) { smtSolverCallback = function (data) { return { @@ -137,11 +137,11 @@ function setupMethods (soljson) { } // This is to support multiple versions of Emscripten. - var addFunction = soljson.addFunction || soljson.Runtime.addFunction; - var removeFunction = soljson.removeFunction || soljson.Runtime.removeFunction; + const addFunction = soljson.addFunction || soljson.Runtime.addFunction; + const removeFunction = soljson.removeFunction || soljson.Runtime.removeFunction; - var cb = addFunction(singleCallback, 'viiiii'); - var output; + const cb = addFunction(singleCallback, 'viiiii'); + let output; try { args.push(cb); if (isVersion6) { @@ -165,37 +165,37 @@ function setupMethods (soljson) { return output; }; - var compileJSON = null; + let compileJSON = null; if ('_compileJSON' in soljson) { // input (text), optimize (bool) -> output (jsontext) compileJSON = soljson.cwrap('compileJSON', 'string', ['string', 'number']); } - var compileJSONMulti = null; + let compileJSONMulti = null; if ('_compileJSONMulti' in soljson) { // input (jsontext), optimize (bool) -> output (jsontext) compileJSONMulti = soljson.cwrap('compileJSONMulti', 'string', ['string', 'number']); } - var compileJSONCallback = null; + let compileJSONCallback = null; if ('_compileJSONCallback' in soljson) { // input (jsontext), optimize (bool), callback (ptr) -> output (jsontext) - var compileInternal = soljson.cwrap('compileJSONCallback', 'string', ['string', 'number', 'number']); + const compileInternal = soljson.cwrap('compileJSONCallback', 'string', ['string', 'number', 'number']); compileJSONCallback = function (input, optimize, readCallback) { - return runWithCallbacks(readCallback, compileInternal, [ input, optimize ]); + return runWithCallbacks(readCallback, compileInternal, [input, optimize]); }; } - var compileStandard = null; + let compileStandard = null; if ('_compileStandard' in soljson) { // input (jsontext), callback (ptr) -> output (jsontext) - var compileStandardInternal = soljson.cwrap('compileStandard', 'string', ['string', 'number']); + const compileStandardInternal = soljson.cwrap('compileStandard', 'string', ['string', 'number']); compileStandard = function (input, readCallback) { - return runWithCallbacks(readCallback, compileStandardInternal, [ input ]); + return runWithCallbacks(readCallback, compileStandardInternal, [input]); }; } if ('_solidity_compile' in soljson) { - var solidityCompile; + let solidityCompile; if (isVersion6) { // input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext) solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number', 'number']); @@ -204,12 +204,12 @@ function setupMethods (soljson) { solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number']); } compileStandard = function (input, callbacks) { - return runWithCallbacks(callbacks, solidityCompile, [ input ]); + return runWithCallbacks(callbacks, solidityCompile, [input]); }; } // Expects a Standard JSON I/O but supports old compilers - var compileStandardWrapper = function (input, readCallback) { + const compileStandardWrapper = function (input, readCallback) { if (compileStandard !== null) { return compileStandard(input, readCallback); } @@ -218,11 +218,11 @@ function setupMethods (soljson) { return JSON.stringify({ errors: [ { - 'type': 'JSONError', - 'component': 'solcjs', - 'severity': 'error', - 'message': message, - 'formattedMessage': 'Error: ' + message + type: 'JSONError', + component: 'solcjs', + severity: 'error', + message: message, + formattedMessage: 'Error: ' + message } ] }); @@ -234,24 +234,24 @@ function setupMethods (soljson) { return formatFatalError('Invalid JSON supplied: ' + e.message); } - if (input['language'] !== 'Solidity') { + if (input.language !== 'Solidity') { return formatFatalError('Only "Solidity" is supported as a language.'); } // NOTE: this is deliberately `== null` - if (input['sources'] == null || input['sources'].length === 0) { + if (input.sources == null || input.sources.length === 0) { return formatFatalError('No input sources specified.'); } function isOptimizerEnabled (input) { - return input['settings'] && input['settings']['optimizer'] && input['settings']['optimizer']['enabled']; + return input.settings && input.settings.optimizer && input.settings.optimizer.enabled; } function translateSources (input) { - var sources = {}; - for (var source in input['sources']) { - if (input['sources'][source]['content'] !== null) { - sources[source] = input['sources'][source]['content']; + const sources = {}; + for (const source in input.sources) { + if (input.sources[source].content !== null) { + sources[source] = input.sources[source].content; } else { // force failure return null; @@ -261,8 +261,8 @@ function setupMethods (soljson) { } function librariesSupplied (input) { - if (input['settings']) { - return input['settings']['libraries']; + if (input.settings) { + return input.settings.libraries; } } @@ -279,21 +279,21 @@ function setupMethods (soljson) { return JSON.stringify(output); } - var sources = translateSources(input); + const sources = translateSources(input); if (sources === null || Object.keys(sources).length === 0) { return formatFatalError('Failed to process sources.'); } // Try linking if libraries were supplied - var libraries = librariesSupplied(input); + const libraries = librariesSupplied(input); // Try to wrap around old versions if (compileJSONCallback !== null) { - return translateOutput(compileJSONCallback(JSON.stringify({ 'sources': sources }), isOptimizerEnabled(input), readCallback), libraries); + return translateOutput(compileJSONCallback(JSON.stringify({ sources: sources }), isOptimizerEnabled(input), readCallback), libraries); } if (compileJSONMulti !== null) { - return translateOutput(compileJSONMulti(JSON.stringify({ 'sources': sources }), isOptimizerEnabled(input)), libraries); + return translateOutput(compileJSONMulti(JSON.stringify({ sources: sources }), isOptimizerEnabled(input)), libraries); } // Try our luck with an ancient compiler @@ -327,8 +327,8 @@ function setupMethods (soljson) { // Loads the compiler of the given version from the github repository // instead of from the local filesystem. loadRemoteVersion: function (versionString, cb) { - var mem = new MemoryStream(null, {readable: false}); - var url = 'https://binaries.soliditylang.org/bin/soljson-' + versionString + '.js'; + const mem = new MemoryStream(null, { readable: false }); + const url = 'https://binaries.soliditylang.org/bin/soljson-' + versionString + '.js'; https.get(url, function (response) { if (response.statusCode !== 200) { cb(new Error('Error retrieving binary: ' + response.statusMessage)); From 2b3372312d2f86cdb9c2640a7182e546aca0ab94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 22 Dec 2021 22:41:06 +0100 Subject: [PATCH 113/153] Reimplement the functionality of require-from-string and drop the dependency --- package.json | 1 - wrapper.js | 13 +++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c89bdd62..2d99ca1a 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "follow-redirects": "^1.12.1", "js-sha3": "0.8.0", "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", "semver": "^5.5.0", "tmp": "0.0.33" }, diff --git a/wrapper.js b/wrapper.js index 516f8479..b71ffd59 100755 --- a/wrapper.js +++ b/wrapper.js @@ -1,6 +1,6 @@ const assert = require('assert'); const translate = require('./translate.js'); -const requireFromString = require('require-from-string'); +const Module = require('module'); const https = require('follow-redirects').https; const MemoryStream = require('memorystream'); const semver = require('semver'); @@ -335,7 +335,16 @@ function setupMethods (soljson) { } else { response.pipe(mem); response.on('end', function () { - cb(null, setupMethods(requireFromString(mem.toString(), 'soljson-' + versionString + '.js'))); + // Based on the require-from-string package. + const soljson = new Module(); + soljson._compile(mem.toString(), 'soljson-' + versionString + '.js'); + if (module.parent && module.parent.children) { + // Make sure the module is plugged into the hierarchy correctly to have parent + // properly garbage collected. + module.parent.children.splice(module.parent.children.indexOf(soljson), 1); + } + + cb(null, setupMethods(soljson.exports)); }); } }).on('error', function (error) { From 497722a07bbb92474e699f24d9042858b70d2162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 22 Dec 2021 22:41:48 +0100 Subject: [PATCH 114/153] Switch from require('module') to module.constructor to make it work with Webpack --- wrapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper.js b/wrapper.js index b71ffd59..f043abeb 100755 --- a/wrapper.js +++ b/wrapper.js @@ -1,6 +1,6 @@ const assert = require('assert'); const translate = require('./translate.js'); -const Module = require('module'); +const Module = module.constructor; const https = require('follow-redirects').https; const MemoryStream = require('memorystream'); const semver = require('semver'); From b71813ed4325f5a39785dfe1a7519c98a91432e6 Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Mon, 24 Jan 2022 08:51:39 +0000 Subject: [PATCH 115/153] fix: os.platform is a function call not a property --- solcjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solcjs b/solcjs index bd34b0d1..93c1e891 100755 --- a/solcjs +++ b/solcjs @@ -78,7 +78,7 @@ function readFileCallback(sourcePath) { } function withUnixPathSeparators(filePath) { - if (os.platform !== 'win32') + if (os.platform() !== 'win32') // On UNIX-like systems forward slashes in paths are just a part of the file name. return filePath; From 941097deb6cbd2253ad022f60d49b4b58ad03c0d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 23 Jan 2022 18:45:42 +0100 Subject: [PATCH 116/153] Use binaries.soliditylang.org link consistently --- README.md | 6 +++--- downloadCurrentVersion.js | 4 ++-- test/compiler.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c877e2c0..673c40b3 100644 --- a/README.md +++ b/README.md @@ -290,11 +290,11 @@ Add the version of `solc` you want to use into `index.html`: ```html ``` -(Alternatively use `https://solc-bin.ethereum.org/bin/soljson-latest.js` to get the latests version.) +(Alternatively use `https://binaries.soliditylang.org/bin/soljson-latest.js` to get the latests version.) This will load `solc` into the global variable `window.Module`. Then use this inside Javascript as: @@ -315,7 +315,7 @@ Alternatively, to iterate the releases, one can load `list.js` from `solc-bin`: ```html ``` diff --git a/downloadCurrentVersion.js b/downloadCurrentVersion.js index 4aa36cdb..f373e376 100755 --- a/downloadCurrentVersion.js +++ b/downloadCurrentVersion.js @@ -13,7 +13,7 @@ function getVersionList (cb) { console.log('Retrieving available version list...'); const mem = new MemoryStream(null, { readable: false }); - https.get('https://solc-bin.ethereum.org/bin/list.json', function (response) { + https.get('https://binaries.soliditylang.org/bin/list.json', function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); process.exit(1); @@ -40,7 +40,7 @@ function downloadBinary (outputName, version, expectedHash) { }); const file = fs.createWriteStream(outputName, { encoding: 'binary' }); - https.get('https://solc-bin.ethereum.org/bin/' + version, function (response) { + https.get('https://binaries.soliditylang.org/bin/' + version, function (response) { if (response.statusCode !== 200) { console.log('Error downloading file: ' + response.statusCode); process.exit(1); diff --git a/test/compiler.js b/test/compiler.js index a8adf5a3..eafccb6f 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -887,7 +887,7 @@ if (!noRemoteVersions) { ]; for (let version in versions) { version = versions[version]; - execSync(`curl -L -o /tmp/${version}.js https://solc-bin.ethereum.org/bin/soljson-${version}.js`); + execSync(`curl -L -o /tmp/${version}.js https://binaries.soliditylang.org/bin/soljson-${version}.js`); const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); runTests(newSolc, version); } From 814f08a96471f5654bd9e50f8440f5c5dfe5c67f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 24 Jan 2022 11:40:22 +0100 Subject: [PATCH 117/153] Rename solcjs to solc.js internally Mostly in order to include it in linting Co-authored-by: Stephen Lineker-Miller --- package.json | 4 ++-- solcjs => solc.js | 0 test/cli.js | 42 +++++++++++++++++++++--------------------- 3 files changed, 23 insertions(+), 23 deletions(-) rename solcjs => solc.js (100%) diff --git a/package.json b/package.json index 2d99ca1a..8e0e62ce 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Solidity compiler", "main": "index.js", "bin": { - "solcjs": "solcjs" + "solcjs": "solc.js" }, "scripts": { "lint": "eslint .", @@ -34,7 +34,7 @@ "linker.js", "smtchecker.js", "smtsolver.js", - "solcjs", + "solc.js", "soljson.js", "translate.js", "wrapper.js" diff --git a/solcjs b/solc.js similarity index 100% rename from solcjs rename to solc.js diff --git a/test/cli.js b/test/cli.js index 7eb90107..9d1bbbdb 100644 --- a/test/cli.js +++ b/test/cli.js @@ -5,72 +5,72 @@ const pkg = require('../package.json'); tape('CLI', function (t) { t.test('--version', function (st) { - const spt = spawn(st, './solcjs --version'); + const spt = spawn(st, './solc.js --version'); spt.stdout.match(RegExp(pkg.version + '(-[^a-zA-A0-9.+]+)?(\\+[^a-zA-Z0-9.-]+)?')); spt.stderr.empty(); spt.end(); }); t.test('no parameters', function (st) { - const spt = spawn(st, './solcjs'); + const spt = spawn(st, './solc.js'); spt.stderr.match(/^Must provide a file/); spt.end(); }); t.test('no mode specified', function (st) { - const spt = spawn(st, './solcjs test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js test/resources/fixtureSmoke.sol'); spt.stderr.match(/^Invalid option selected/); spt.end(); }); t.test('--bin', function (st) { - const spt = spawn(st, './solcjs --bin test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js --bin test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --optimize', function (st) { - const spt = spawn(st, './solcjs --bin --optimize test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js --bin --optimize test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --optimize-runs 666', function (st) { - const spt = spawn(st, './solcjs --bin --optimize-runs 666 test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js --bin --optimize-runs 666 test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --optimize-runs not-a-number', function (st) { - const spt = spawn(st, './solcjs --bin --optimize-runs not-a-number test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js --bin --optimize-runs not-a-number test/resources/fixtureSmoke.sol'); spt.stderr.match(/^error: option '--optimize-runs ' argument 'not-a-number' is invalid/); spt.end(); }); t.test('invalid file specified', function (st) { - const spt = spawn(st, './solcjs --bin test/fileNotFound.sol'); + const spt = spawn(st, './solc.js --bin test/fileNotFound.sol'); spt.stderr.match(/^Error reading /); spt.end(); }); t.test('incorrect source source', function (st) { - const spt = spawn(st, './solcjs --bin test/resources/fixtureIncorrectSource.sol'); + const spt = spawn(st, './solc.js --bin test/resources/fixtureIncorrectSource.sol'); spt.stderr.match(/SyntaxError: Invalid pragma "contract"/); spt.end(); }); t.test('--abi', function (st) { - const spt = spawn(st, './solcjs --abi test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js --abi test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); }); t.test('--bin --abi', function (st) { - const spt = spawn(st, './solcjs --bin --abi test/resources/fixtureSmoke.sol'); + const spt = spawn(st, './solc.js --bin --abi test/resources/fixtureSmoke.sol'); spt.stderr.empty(); spt.succeeds(); spt.end(); @@ -79,7 +79,7 @@ tape('CLI', function (t) { t.test('no base path', function (st) { const spt = spawn( st, - './solcjs --bin ' + + './solc.js --bin ' + 'test/resources/importA.sol ' + './test/resources//importA.sol ' + path.resolve('test/resources/importA.sol') @@ -95,7 +95,7 @@ tape('CLI', function (t) { // by the import callback when it appends the base path back. const spt = spawn( st, - './solcjs --bin --base-path test/resources ' + + './solc.js --bin --base-path test/resources ' + 'test/resources/importA.sol ' + './test/resources//importA.sol ' + path.resolve('test/resources/importA.sol') @@ -108,7 +108,7 @@ tape('CLI', function (t) { t.test('relative non canonical base path', function (st) { const spt = spawn( st, - './solcjs --bin --base-path ./test/resources ' + + './solc.js --bin --base-path ./test/resources ' + 'test/resources/importA.sol ' + './test/resources//importA.sol ' + path.resolve('test/resources/importA.sol') @@ -121,7 +121,7 @@ tape('CLI', function (t) { t.test('absolute base path', function (st) { const spt = spawn( st, - './solcjs --bin --base-path ' + path.resolve('test/resources') + ' ' + + './solc.js --bin --base-path ' + path.resolve('test/resources') + ' ' + 'test/resources/importA.sol ' + './test/resources//importA.sol ' + path.resolve('test/resources/importA.sol') @@ -134,7 +134,7 @@ tape('CLI', function (t) { t.test('include paths', function (st) { const spt = spawn( st, - './solcjs --bin ' + + './solc.js --bin ' + 'test/resources/importCallback/base/contractB.sol ' + 'test/resources/importCallback/includeA/libY.sol ' + './test/resources/importCallback/includeA//libY.sol ' + @@ -151,7 +151,7 @@ tape('CLI', function (t) { t.test('include paths without base path', function (st) { const spt = spawn( st, - './solcjs --bin ' + + './solc.js --bin ' + 'test/resources/importCallback/contractC.sol ' + '--include-path test/resources/importCallback/includeA' ); @@ -163,7 +163,7 @@ tape('CLI', function (t) { t.test('empty include paths', function (st) { const spt = spawn( st, - './solcjs --bin ' + + './solc.js --bin ' + 'test/resources/importCallback/contractC.sol ' + '--base-path test/resources/importCallback/base ' + '--include-path=' @@ -189,7 +189,7 @@ tape('CLI', function (t) { } } }; - const spt = spawn(st, './solcjs --standard-json'); + const spt = spawn(st, './solc.js --standard-json'); spt.stdin.setEncoding('utf-8'); spt.stdin.write(JSON.stringify(input)); spt.stdin.end(); @@ -218,7 +218,7 @@ tape('CLI', function (t) { } } }; - const spt = spawn(st, './solcjs --standard-json --base-path test/resources'); + const spt = spawn(st, './solc.js --standard-json --base-path test/resources'); spt.stdin.setEncoding('utf-8'); spt.stdin.write(JSON.stringify(input)); spt.stdin.end(); @@ -244,7 +244,7 @@ tape('CLI', function (t) { }; const spt = spawn( st, - './solcjs --standard-json ' + + './solc.js --standard-json ' + '--base-path test/resources/importCallback/base ' + '--include-path test/resources/importCallback/includeA ' + '--include-path ' + path.resolve('test/resources/importCallback/includeB/') From 5f9f1456254368d5bc818743f3e1509d866218f6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 24 Jan 2022 12:41:38 +0100 Subject: [PATCH 118/153] Apply lint changes to solc.js --- solc.js | 137 +++++++++++++++++++++++------------------------ test/compiler.js | 2 +- 2 files changed, 67 insertions(+), 72 deletions(-) diff --git a/solc.js b/solc.js index 93c1e891..e0ccb390 100755 --- a/solc.js +++ b/solc.js @@ -1,24 +1,24 @@ #!/usr/bin/env node +const commander = require('commander'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const solc = require('./index.js'); +const smtchecker = require('./smtchecker.js'); +const smtsolver = require('./smtsolver.js'); + // hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end -var originalUncaughtExceptionListeners = process.listeners("uncaughtException"); - -var fs = require('fs'); -var os = require('os'); -var path = require('path'); -var solc = require('./index.js'); -var smtchecker = require('./smtchecker.js'); -var smtsolver = require('./smtsolver.js'); +const originalUncaughtExceptionListeners = process.listeners('uncaughtException'); // FIXME: remove annoying exception catcher of Emscripten // see https://github.com/chriseth/browser-solidity/issues/167 process.removeAllListeners('uncaughtException'); -var commander = require('commander'); const program = new commander.Command(); -const commanderParseInt = function(value) { +const commanderParseInt = function (value) { const parsedValue = parseInt(value, 10); if (isNaN(parsedValue)) { - throw new commander.InvalidArgumentError("Not a valid integer."); + throw new commander.InvalidArgumentError('Not a valid integer.'); } return parsedValue; }; @@ -51,46 +51,47 @@ program program.parse(process.argv); const options = program.opts(); -var files = program.args; -var destination = options.outputDir || '.' +const files = program.args; +const destination = options.outputDir || '.'; function abort (msg) { console.error(msg || 'Error occured'); process.exit(1); } -function readFileCallback(sourcePath) { - const prefixes = [options.basePath ? options.basePath : ""].concat( +function readFileCallback (sourcePath) { + const prefixes = [options.basePath ? options.basePath : ''].concat( options.includePath ? options.includePath : [] ); for (const prefix of prefixes) { - const prefixedSourcePath = (prefix ? prefix + '/' : "") + sourcePath; + const prefixedSourcePath = (prefix ? prefix + '/' : '') + sourcePath; if (fs.existsSync(prefixedSourcePath)) { try { - return {'contents': fs.readFileSync(prefixedSourcePath).toString('utf8')} + return { contents: fs.readFileSync(prefixedSourcePath).toString('utf8') }; } catch (e) { - return {error: 'Error reading ' + prefixedSourcePath + ': ' + e}; + return { error: 'Error reading ' + prefixedSourcePath + ': ' + e }; } } } - return {error: 'File not found inside the base path or any of the include paths.'} + return { error: 'File not found inside the base path or any of the include paths.' }; } -function withUnixPathSeparators(filePath) { - if (os.platform() !== 'win32') - // On UNIX-like systems forward slashes in paths are just a part of the file name. +function withUnixPathSeparators (filePath) { + // On UNIX-like systems forward slashes in paths are just a part of the file name. + if (os.platform() !== 'win32') { return filePath; + } - return filePath.replace(/\\/g, "/"); + return filePath.replace(/\\/g, '/'); } -function makeSourcePathRelativeIfPossible(sourcePath) { +function makeSourcePathRelativeIfPossible (sourcePath) { const absoluteBasePath = (options.basePath ? path.resolve(options.basePath) : path.resolve('.')); const absoluteIncludePaths = ( - options.includePath ? - options.includePath.map((prefix) => { return path.resolve(prefix); }) : - [] + options.includePath + ? options.includePath.map((prefix) => { return path.resolve(prefix); }) + : [] ); // Compared to base path stripping logic in solc this is much simpler because path.resolve() @@ -104,58 +105,52 @@ function makeSourcePathRelativeIfPossible(sourcePath) { for (const absolutePrefix of [absoluteBasePath].concat(absoluteIncludePaths)) { const relativeSourcePath = path.relative(absolutePrefix, absoluteSourcePath); - if (!relativeSourcePath.startsWith('../')) - return withUnixPathSeparators(relativeSourcePath); + if (!relativeSourcePath.startsWith('../')) { return withUnixPathSeparators(relativeSourcePath); } } // File is not located inside base path or include paths so use its absolute path. return withUnixPathSeparators(absoluteSourcePath); } -function toFormattedJson(input) { +function toFormattedJson (input) { return JSON.stringify(input, null, program.prettyJson ? 4 : 0); } -function reformatJsonIfRequested(inputJson) { +function reformatJsonIfRequested (inputJson) { return (program.prettyJson ? toFormattedJson(JSON.parse(inputJson)) : inputJson); } -var callbacks = undefined -if (options.basePath || !options.standardJson) - callbacks = {'import': readFileCallback}; +let callbacks; +if (options.basePath || !options.standardJson) { callbacks = { import: readFileCallback }; } if (options.standardJson) { - var input = fs.readFileSync(process.stdin.fd).toString('utf8'); - if (program.verbose) - console.log('>>> Compiling:\n' + reformatJsonIfRequested(input) + "\n") - var output = reformatJsonIfRequested(solc.compile(input, callbacks)); + const input = fs.readFileSync(process.stdin.fd).toString('utf8'); + if (program.verbose) { console.log('>>> Compiling:\n' + reformatJsonIfRequested(input) + '\n'); } + let output = reformatJsonIfRequested(solc.compile(input, callbacks)); try { - var inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver); + const inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver); if (inputJSON) { - if (program.verbose) - console.log('>>> Retrying compilation with SMT:\n' + toFormattedJson(inputJSON) + "\n") + if (program.verbose) { console.log('>>> Retrying compilation with SMT:\n' + toFormattedJson(inputJSON) + '\n'); } output = reformatJsonIfRequested(solc.compile(JSON.stringify(inputJSON), callbacks)); } - } - catch (e) { - var addError = { - component: "general", + } catch (e) { + const addError = { + component: 'general', formattedMessage: e.toString(), message: e.toString(), - type: "Warning" + type: 'Warning' }; - var outputJSON = JSON.parse(output); + const outputJSON = JSON.parse(output); if (!outputJSON.errors) { - outputJSON.errors = [] + outputJSON.errors = []; } outputJSON.errors.push(addError); output = toFormattedJson(outputJSON); } - if (program.verbose) - console.log('>>> Compilation result:') + if (program.verbose) { console.log('>>> Compilation result:'); } console.log(output); process.exit(0); } else if (files.length === 0) { @@ -171,14 +166,15 @@ if (!options.basePath && options.includePath && options.includePath.length > 0) abort('--include-path option requires a non-empty base path.'); } -if (options.includePath) - for (const includePath of options.includePath) - if (!includePath) - abort('Empty values are not allowed in --include-path.'); +if (options.includePath) { + for (const includePath of options.includePath) { + if (!includePath) { abort('Empty values are not allowed in --include-path.'); } + } +} -var sources = {}; +const sources = {}; -for (var i = 0; i < files.length; i++) { +for (let i = 0; i < files.length; i++) { try { sources[makeSourcePathRelativeIfPossible(files[i])] = { content: fs.readFileSync(files[i]).toString() @@ -193,37 +189,36 @@ const cliInput = { settings: { optimizer: { enabled: options.optimize, - runs: options.optimizeRuns, + runs: options.optimizeRuns }, outputSelection: { '*': { - '*': [ 'abi', 'evm.bytecode' ] + '*': ['abi', 'evm.bytecode'] } } }, sources: sources }; -if (program.verbose) - console.log('>>> Compiling:\n' + toFormattedJson(cliInput) + "\n") -var output = JSON.parse(solc.compile(JSON.stringify(cliInput), callbacks)); +if (program.verbose) { console.log('>>> Compiling:\n' + toFormattedJson(cliInput) + '\n'); } +const output = JSON.parse(solc.compile(JSON.stringify(cliInput), callbacks)); let hasError = false; if (!output) { abort('No output from compiler'); -} else if (output['errors']) { - for (var error in output['errors']) { - var message = output['errors'][error] +} else if (output.errors) { + for (const error in output.errors) { + const message = output.errors[error]; if (message.severity === 'warning') { - console.log(message.formattedMessage) + console.log(message.formattedMessage); } else { - console.error(message.formattedMessage) - hasError = true + console.error(message.formattedMessage); + hasError = true; } } } -fs.mkdirSync(destination, {recursive: true}); +fs.mkdirSync(destination, { recursive: true }); function writeFile (file, content) { file = path.join(destination, file); @@ -234,9 +229,9 @@ function writeFile (file, content) { }); } -for (var fileName in output.contracts) { - for (var contractName in output.contracts[fileName]) { - var contractFileName = fileName + ':' + contractName; +for (const fileName in output.contracts) { + for (const contractName in output.contracts[fileName]) { + let contractFileName = fileName + ':' + contractName; contractFileName = contractFileName.replace(/[:./\\]/g, '_'); if (options.bin) { diff --git a/test/compiler.js b/test/compiler.js index a8adf5a3..e457cc40 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -289,7 +289,7 @@ function runTests (solc, versionText) { 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' }; st.throws(function () { - solc.lowlevel.compileCallback(JSON.stringify({ sources: input }), 0, "this isn't a callback"); + solc.lowlevel.compileCallback(JSON.stringify({ sources: input }), 0, 'this isn\'t a callback'); }, /Invalid callback object specified./); st.end(); }); From 776760620bd59fdbc3053aebaa6328c73323567c Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Mon, 24 Jan 2022 13:31:17 +0100 Subject: [PATCH 119/153] README changes --- README.md | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 673c40b3..b1378a57 100644 --- a/README.md +++ b/README.md @@ -144,36 +144,54 @@ for (var contractName in output.contracts['test.sol']) { } ``` -The ``smtSolver`` callback function is used to solve SMT queries generated by +Since version 0.5.1, the ``smtSolver`` callback function is used to solve SMT queries generated by Solidity's SMTChecker. If you have an SMT solver installed locally, it can be used to solve the given queries, where the callback must synchronously return either an error or the result from the solver. A default -``smtSolver`` callback is distributed by ``solc-js``, which relies on either -Z3 or CVC4 being installed locally. +``smtSolver`` callback is included in this package via the module +``smtchecker.js`` which exports the ``smtCallback`` function that takes 1) a +function that takes queries and returns the solving result, and 2) a solver +configuration object. The module ``smtsolver.js`` has a few predefined solver +configurations, and relies on Z3, Eldarica or CVC4 being installed locally. It +exports the list of locally found solvers and a function that invokes a given +solver. + +The API of the SMT callback is **experimental** and can change at any time. +The last change was in version 0.8.11. #### Example usage with smtSolver callback ```javascript var solc = require('solc'); -var smt = require('smtsolver'); +const smtchecker = require('solc/smtchecker'); +const smtsolver = require('solc/smtsolver'); // Note that this example only works via node and not in the browser. var input = { language: 'Solidity', sources: { 'test.sol': { - content: 'pragma experimental SMTChecker; contract C { function f(uint x) public { assert(x > 0); } }' + content: 'contract C { function f(uint x) public { assert(x > 0); } }' + } + }, + settings: { + modelChecker: { + engine: "chc", + solvers: [ "smtlib2" ] } } }; var output = JSON.parse( - solc.compile(JSON.stringify(input), { smtSolver: smt.smtSolver }) + solc.compile( + JSON.stringify(input), + { smtSolver: smtchecker.smtCallback(smtsolver.smtSolver, smtsolver.availableSolvers[0]) } + ) ); ``` The assertion is clearly false, and an ``assertion failure`` warning -should be returned. +should be returned, together with a counterexample. #### Low-level API From 40d563f89302716ddd936216c968abdfc700132a Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Mon, 24 Jan 2022 13:31:42 +0100 Subject: [PATCH 120/153] Fix smtchecker callback tests and add Eldarica --- smtchecker.js | 8 ++-- smtsolver.js | 39 ++++++++++------ solc.js | 12 +++-- test/smtCheckerTests/loop.sol | 8 ++++ test/smtcallback.js | 25 +++++++---- test/smtchecker.js | 85 +++++++++++++++++++++++++++++++++-- 6 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 test/smtCheckerTests/loop.sol diff --git a/smtchecker.js b/smtchecker.js index f1acdb35..084778d1 100644 --- a/smtchecker.js +++ b/smtchecker.js @@ -3,7 +3,7 @@ // The function runs an SMT solver on each query and adjusts the input for // another run. // Returns null if no solving is requested. -function handleSMTQueries (inputJSON, outputJSON, solver) { +function handleSMTQueries (inputJSON, outputJSON, solverFunction, solver) { const auxInputReq = outputJSON.auxiliaryInputRequested; if (!auxInputReq) { return null; @@ -16,7 +16,7 @@ function handleSMTQueries (inputJSON, outputJSON, solver) { const responses = {}; for (const query in queries) { - responses[query] = solver(queries[query]); + responses[query] = solverFunction(queries[query], solver); } // Note: all existing solved queries are replaced. @@ -25,10 +25,10 @@ function handleSMTQueries (inputJSON, outputJSON, solver) { return inputJSON; } -function smtCallback (solver) { +function smtCallback (solverFunction, solver) { return function (query) { try { - const result = solver(query); + const result = solverFunction(query, solver); return { contents: result }; } catch (err) { return { error: err }; diff --git a/smtsolver.js b/smtsolver.js index 9c163fce..70d2e898 100644 --- a/smtsolver.js +++ b/smtsolver.js @@ -3,37 +3,48 @@ const execSync = require('child_process').execSync; const fs = require('fs'); const tmp = require('tmp'); +// Timeout in ms. const timeout = 10000; const potentialSolvers = [ { name: 'z3', + command: 'z3', params: '-smt2 rlimit=20000000 rewriter.pull_cheap_ite=true fp.spacer.q3.use_qgen=true fp.spacer.mbqi=false fp.spacer.ground_pobs=false' }, + { + name: 'Eldarica', + command: 'eld', + params: '-horn -t:' + (timeout / 1000) // Eldarica takes timeout in seconds. + }, { name: 'cvc4', + command: 'cvc4', params: '--lang=smt2 --tlimit=' + timeout } ]; -const solvers = potentialSolvers.filter(solver => commandExistsSync(solver.name)); -function solve (query) { - if (solvers.length === 0) { - throw new Error('No SMT solver available. Assertion checking will not be performed.'); +const solvers = potentialSolvers.filter(solver => commandExistsSync(solver.command)); + +function solve (query, solver) { + if (solver === undefined) { + if (solvers.length === 0) { + throw new Error('No SMT solver available. Assertion checking will not be performed.'); + } else { + solver = solvers[0]; + } } const tmpFile = tmp.fileSync({ postfix: '.smt2' }); fs.writeFileSync(tmpFile.name, query); - // TODO For now only the first SMT solver found is used. - // At some point a computation similar to the one done in - // SMTPortfolio::check should be performed, where the results - // given by different solvers are compared and an error is - // reported if solvers disagree (i.e. SAT vs UNSAT). let solverOutput; try { solverOutput = execSync( - solvers[0].name + ' ' + solvers[0].params + ' ' + tmpFile.name, { - stdio: 'pipe' + solver.command + ' ' + solver.params + ' ' + tmpFile.name, { + encoding: 'utf8', + maxBuffer: 1024 * 1024 * 1024, + stdio: 'pipe', + timeout: timeout // Enforce timeout on the process, since solvers can sometimes go around it. } ).toString(); } catch (e) { @@ -44,7 +55,9 @@ function solve (query) { if ( !solverOutput.startsWith('sat') && !solverOutput.startsWith('unsat') && - !solverOutput.startsWith('unknown') + !solverOutput.startsWith('unknown') && + !solverOutput.startsWith('(error') && // Eldarica reports errors in an sexpr, for example: '(error "Failed to reconstruct array model")' + !solverOutput.startsWith('error') ) { throw new Error('Failed to solve SMT query. ' + e.toString()); } @@ -56,5 +69,5 @@ function solve (query) { module.exports = { smtSolver: solve, - availableSolvers: solvers.length + availableSolvers: solvers }; diff --git a/solc.js b/solc.js index e0ccb390..08f230ae 100755 --- a/solc.js +++ b/solc.js @@ -129,10 +129,14 @@ if (options.standardJson) { let output = reformatJsonIfRequested(solc.compile(input, callbacks)); try { - const inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver); - if (inputJSON) { - if (program.verbose) { console.log('>>> Retrying compilation with SMT:\n' + toFormattedJson(inputJSON) + '\n'); } - output = reformatJsonIfRequested(solc.compile(JSON.stringify(inputJSON), callbacks)); + if (smtsolver.availableSolvers.length === 0) { + console.log('>>> Cannot retry compilation with SMT because there are no SMT solvers available.'); + } else { + const inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output), smtsolver.smtSolver, smtsolver.availableSolvers[0]); + if (inputJSON) { + if (program.verbose) { console.log('>>> Retrying compilation with SMT:\n' + toFormattedJson(inputJSON) + '\n'); } + output = reformatJsonIfRequested(solc.compile(JSON.stringify(inputJSON), callbacks)); + } } } catch (e) { const addError = { diff --git a/test/smtCheckerTests/loop.sol b/test/smtCheckerTests/loop.sol new file mode 100644 index 00000000..24e053d6 --- /dev/null +++ b/test/smtCheckerTests/loop.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint x) public pure { + uint i = 0; + while (i < x) + ++i; + assert(i == x); + } +} diff --git a/test/smtcallback.js b/test/smtcallback.js index 4314fe1a..71c78386 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -119,7 +119,7 @@ tape('SMTCheckerCallback', function (t) { { smtSolver: test.cb } )); const errors = collectErrors(output); - st.ok(expectErrors(errors, test.expectations)); + st.ok(expectErrors(errors, test.expectations, false)); } st.end(); }); @@ -132,8 +132,10 @@ tape('SMTCheckerCallback', function (t) { return; } - if (smtsolver.availableSolvers === 0) { - st.skip('No SMT solver available.'); + // For these tests we actually need z3/Spacer. + const z3HornSolvers = smtsolver.availableSolvers.filter(solver => solver.command === 'z3'); + if (z3HornSolvers.length === 0) { + st.skip('z3/Spacer not available.'); st.end(); return; } @@ -167,9 +169,8 @@ tape('SMTCheckerCallback', function (t) { if (source.includes(option)) { const idx = source.indexOf(option); if (source.indexOf(option, idx + 1) !== -1) { - st.skip('SMTEngine option given multiple times.'); - st.end(); - return; + st.comment('SMTEngine option given multiple times.'); + continue; } const re = new RegExp(option + '(\\w+)'); const m = source.match(re); @@ -211,7 +212,14 @@ tape('SMTCheckerCallback', function (t) { // `pragma experimental SMTChecker;` was deprecated in 0.8.4 if (semver.gt(solc.semver(), '0.8.3')) { const engine = test.engine !== undefined ? test.engine : 'all'; - settings = { modelChecker: { engine: engine } }; + settings = { + modelChecker: { + engine: engine, + solvers: [ + 'smtlib2' + ] + } + }; } const output = JSON.parse(solc.compile( JSON.stringify({ @@ -219,7 +227,8 @@ tape('SMTCheckerCallback', function (t) { sources: test.solidity, settings: settings }), - { smtSolver: smtchecker.smtCallback(smtsolver.smtSolver) } + // This test needs z3 specifically. + { smtSolver: smtchecker.smtCallback(smtsolver.smtSolver, z3HornSolvers[0]) } )); st.ok(output); diff --git a/test/smtchecker.js b/test/smtchecker.js index 70563b21..b2927e48 100644 --- a/test/smtchecker.js +++ b/test/smtchecker.js @@ -1,25 +1,104 @@ const tape = require('tape'); +const semver = require('semver'); +const solc = require('../index.js'); const smtchecker = require('../smtchecker.js'); +const smtsolver = require('../smtsolver.js'); +const preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; +// tape('SMTChecker', function (t) { + // We use null for `solverFunction` and `solver` when calling `handleSMTQueries` + // because these tests do not call a solver. + t.test('smoke test with no axuiliaryInputRequested', function (st) { const input = {}; const output = {}; - st.equal(smtchecker.handleSMTQueries(input, output), null); + st.equal(smtchecker.handleSMTQueries(input, output, null, null), null); st.end(); }); t.test('smoke test with no smtlib2queries', function (st) { const input = {}; const output = { auxiliaryInputRequested: {} }; - st.equal(smtchecker.handleSMTQueries(input, output), null); + st.equal(smtchecker.handleSMTQueries(input, output, null, null), null); st.end(); }); t.test('smoke test with empty smtlib2queries', function (st) { const input = {}; const output = { auxiliaryInputRequested: { smtlib2queries: { } } }; - st.equal(smtchecker.handleSMTQueries(input, output), null); + st.equal(smtchecker.handleSMTQueries(input, output, null, null), null); + st.end(); + }); + + t.test('smtCallback should return type function', (st) => { + const response = smtchecker.smtCallback(() => {}); + st.equal(typeof response, 'function'); + st.end(); + }); + + t.test('smtCallback should error when passed parser fails', (st) => { + const cbFun = smtchecker.smtCallback((content) => { throw new Error(content); }); + const response = cbFun('expected-error-message'); + + st.deepEqual(response, { error: new Error('expected-error-message') }); + st.end(); + }); + + t.test('smtCallback should return content when passed parser does not fail', (st) => { + const cbFun = smtchecker.smtCallback((content) => { return content; }); + const response = cbFun('expected-content-message'); + + st.deepEqual(response, { contents: 'expected-content-message' }); + st.end(); + }); +}); + +tape('SMTCheckerWithSolver', function (t) { + // In these tests we require z3 to actually run the solver. + // This uses the SMT double run mechanism instead of the callback. + + t.test('Simple test with axuiliaryInputRequested', function (st) { + const z3 = smtsolver.availableSolvers.filter(solver => solver.command === 'z3'); + if (z3.length === 0) { + st.skip('Test requires z3.'); + st.end(); + return; + } + + if (semver.lt(solc.semver(), '0.8.7')) { + st.skip('This test requires Solidity 0.8.7 to enable all SMTChecker options.'); + st.end(); + return; + } + + const settings = { + modelChecker: { + engine: 'chc', + solvers: ['smtlib2'] + } + }; + + const source = { a: { content: preamble + '\ncontract C { function f(uint x) public pure { assert(x > 0); } }' } }; + + const input = { + language: 'Solidity', + sources: source, + settings: settings + }; + + const output = JSON.parse(solc.compile(JSON.stringify(input))); + st.ok(output); + + const newInput = smtchecker.handleSMTQueries(input, output, smtsolver.smtSolver, z3[0]); + st.notEqual(newInput, null); + + const newOutput = JSON.parse(solc.compile(JSON.stringify(newInput))); + st.ok(newOutput); + + const smtErrors = newOutput.errors.filter(e => e.errorCode === '6328'); + st.equal(smtErrors.length, 1); + st.end(); }); }); From 9a23da4c3986eeff43c666893344ad3422d2af68 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 24 Jan 2022 22:59:37 +0100 Subject: [PATCH 121/153] Move smtchecker tests to test/resources/smtChecker --- test/{smtCheckerTests => resources/smtChecker}/loop.sol | 0 test/{smtCheckerTests => resources/smtChecker}/smoke.sol | 0 .../smtChecker}/smoke_with_engine.sol | 0 .../smtChecker}/smoke_with_multi_engine.sol | 0 test/smtcallback.js | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename test/{smtCheckerTests => resources/smtChecker}/loop.sol (100%) rename test/{smtCheckerTests => resources/smtChecker}/smoke.sol (100%) rename test/{smtCheckerTests => resources/smtChecker}/smoke_with_engine.sol (100%) rename test/{smtCheckerTests => resources/smtChecker}/smoke_with_multi_engine.sol (100%) diff --git a/test/smtCheckerTests/loop.sol b/test/resources/smtChecker/loop.sol similarity index 100% rename from test/smtCheckerTests/loop.sol rename to test/resources/smtChecker/loop.sol diff --git a/test/smtCheckerTests/smoke.sol b/test/resources/smtChecker/smoke.sol similarity index 100% rename from test/smtCheckerTests/smoke.sol rename to test/resources/smtChecker/smoke.sol diff --git a/test/smtCheckerTests/smoke_with_engine.sol b/test/resources/smtChecker/smoke_with_engine.sol similarity index 100% rename from test/smtCheckerTests/smoke_with_engine.sol rename to test/resources/smtChecker/smoke_with_engine.sol diff --git a/test/smtCheckerTests/smoke_with_multi_engine.sol b/test/resources/smtChecker/smoke_with_multi_engine.sol similarity index 100% rename from test/smtCheckerTests/smoke_with_multi_engine.sol rename to test/resources/smtChecker/smoke_with_multi_engine.sol diff --git a/test/smtcallback.js b/test/smtcallback.js index 71c78386..db21b5ec 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.js @@ -125,7 +125,7 @@ tape('SMTCheckerCallback', function (t) { }); t.test('Solidity smtCheckerTests', function (st) { - const testdir = path.resolve(__dirname, 'smtCheckerTests/'); + const testdir = path.resolve(__dirname, 'resources/smtChecker/'); if (!fs.existsSync(testdir)) { st.skip('SMT checker tests not present.'); st.end(); From 70b489bff26d3934f1ffaf3307d2f4cb78efbed5 Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Tue, 16 Nov 2021 20:31:17 +0000 Subject: [PATCH 122/153] Base support for using Typescript within the project Configuration reference for tsconfig can be located here: https://www.typescriptlang.org/tsconfig * Exclude the build output from source control by adding dist to .gitignore * Rename all project files from .js to .ts * Upgrade require to import and upgrade module.exports to export default * Refactored package.json to reference dist folder When building from typescript, the +x permission is missing, which is required to execute directly. The command line tool general usage and testing require it. The post build step adds this. --- .eslintrc.js | 4 ++ .gitignore | 4 ++ .nycrc | 17 ++++++ abi.js => abi.ts | 6 +- build/clean.js | 8 +++ build/postbuild.js | 4 ++ ...entVersion.js => downloadCurrentVersion.ts | 10 ++-- index.js | 3 - index.ts | 5 ++ linker.js => linker.ts | 10 ++-- package.json | 55 +++++++++++-------- smtchecker.js => smtchecker.ts | 10 ++-- smtsolver.js => smtsolver.ts | 10 ++-- solc.js => solc.ts | 16 +++--- test/{abi.js => abi.ts} | 4 +- test/{cli.js => cli.ts} | 8 +-- test/{compiler.js => compiler.ts} | 27 ++++----- test/index.js | 13 ----- test/index.ts | 13 +++++ test/{linker.js => linker.ts} | 4 +- test/{smtcallback.js => smtcallback.ts} | 16 +++--- test/{smtchecker.js => smtchecker.ts} | 10 ++-- test/{translate.js => translate.ts} | 9 +-- translate.js => translate.ts | 16 +++--- tsconfig.json | 30 ++++++++++ verifyVersion.js => verifyVersion.ts | 8 ++- wrapper.js => wrapper.ts | 17 +++--- 27 files changed, 211 insertions(+), 126 deletions(-) create mode 100644 .nycrc rename abi.js => abi.ts (95%) create mode 100644 build/clean.js create mode 100644 build/postbuild.js rename downloadCurrentVersion.js => downloadCurrentVersion.ts (91%) delete mode 100644 index.js create mode 100644 index.ts rename linker.js => linker.ts (94%) rename smtchecker.js => smtchecker.ts (83%) rename smtsolver.js => smtsolver.ts (91%) rename solc.js => solc.ts (96%) rename test/{abi.js => abi.ts} (98%) rename test/{cli.js => cli.ts} (98%) rename test/{compiler.js => compiler.ts} (98%) delete mode 100644 test/index.js create mode 100644 test/index.ts rename test/{linker.js => linker.ts} (99%) rename test/{smtcallback.js => smtcallback.ts} (96%) rename test/{smtchecker.js => smtchecker.ts} (94%) rename test/{translate.js => translate.ts} (94%) rename translate.js => translate.ts (94%) create mode 100644 tsconfig.json rename verifyVersion.js => verifyVersion.ts (63%) rename wrapper.js => wrapper.ts (97%) diff --git a/.eslintrc.js b/.eslintrc.js index 710c4aaa..f8e27e4e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,10 @@ module.exports = { extends: [ 'standard' ], + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint' + ], parserOptions: { ecmaVersion: 12, sourceType: 'module' diff --git a/.gitignore b/.gitignore index 08761698..416b19a5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ bin out *.bin *.abi + +dist/** + +.nyc_output diff --git a/.nycrc b/.nycrc new file mode 100644 index 00000000..2edd3399 --- /dev/null +++ b/.nycrc @@ -0,0 +1,17 @@ +{ + "exclude": [ + "coverage", + "dist/soljson.js", + "**/test/**" + ], + "extensions": [ + ".js" + ], + "report-dir": "./coverage", + "reporter": [ + "lcov", + "html", + "text-summary" + ], + "temp-directory": "./coverage/.nyc_output" +} diff --git a/abi.js b/abi.ts similarity index 95% rename from abi.js rename to abi.ts index df5dde2b..aa0ae977 100644 --- a/abi.js +++ b/abi.ts @@ -1,4 +1,4 @@ -const semver = require('semver'); +import * as semver from 'semver'; function update (compilerVersion, abi) { let hasConstructor = false; @@ -58,6 +58,6 @@ function update (compilerVersion, abi) { return abi; } -module.exports = { - update: update +export default { + update }; diff --git a/build/clean.js b/build/clean.js new file mode 100644 index 00000000..84e91fa4 --- /dev/null +++ b/build/clean.js @@ -0,0 +1,8 @@ +const fs = require('fs'); +const path = require('path'); + +const distFolder = path.join(__dirname, 'dist'); + +if (fs.existsSync(distFolder)) { + fs.rmdirSync(distFolder); +} diff --git a/build/postbuild.js b/build/postbuild.js new file mode 100644 index 00000000..7ea03dd3 --- /dev/null +++ b/build/postbuild.js @@ -0,0 +1,4 @@ +const fs = require('fs'); +const path = require('path'); + +fs.chmodSync(path.join(__dirname, '../dist', 'solc.js'), '755'); diff --git a/downloadCurrentVersion.js b/downloadCurrentVersion.ts similarity index 91% rename from downloadCurrentVersion.js rename to downloadCurrentVersion.ts index f373e376..f38aff8a 100755 --- a/downloadCurrentVersion.js +++ b/downloadCurrentVersion.ts @@ -3,11 +3,11 @@ // This is used to download the correct binary version // as part of the prepublish step. -const pkg = require('./package.json'); -const fs = require('fs'); -const https = require('follow-redirects').https; -const MemoryStream = require('memorystream'); -const keccak256 = require('js-sha3').keccak256; +import * as pkg from './package.json'; +import * as fs from 'fs'; +import { https } from 'follow-redirects'; +import * as MemoryStream from 'memorystream'; +import { keccak256 } from 'js-sha3'; function getVersionList (cb) { console.log('Retrieving available version list...'); diff --git a/index.js b/index.js deleted file mode 100644 index 78026179..00000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const wrapper = require('./wrapper.js'); - -module.exports = wrapper(require('./soljson.js')); diff --git a/index.ts b/index.ts new file mode 100644 index 00000000..0297cdf2 --- /dev/null +++ b/index.ts @@ -0,0 +1,5 @@ +import wrapper from './wrapper'; + +const soljson = require('./soljson.js'); + +export default wrapper(soljson); diff --git a/linker.js b/linker.ts similarity index 94% rename from linker.js rename to linker.ts index 8345105d..59199276 100644 --- a/linker.js +++ b/linker.ts @@ -1,5 +1,5 @@ -const assert = require('assert'); -const keccak256 = require('js-sha3').keccak256; +import * as assert from 'assert'; +import { keccak256 } from 'js-sha3'; function libraryHashPlaceholder (input) { return '$' + keccak256(input).slice(0, 34) + '$'; @@ -88,7 +88,7 @@ const findLinkReferences = function (bytecode) { return linkReferences; }; -module.exports = { - linkBytecode: linkBytecode, - findLinkReferences: findLinkReferences +export default { + linkBytecode, + findLinkReferences }; diff --git a/package.json b/package.json index 8e0e62ce..c0d3c8e7 100644 --- a/package.json +++ b/package.json @@ -2,19 +2,22 @@ "name": "solc", "version": "0.8.11", "description": "Solidity compiler", - "main": "index.js", + "main": "dist/index.js", "bin": { - "solcjs": "solc.js" + "solcjs": "dist/solc.js" }, "scripts": { - "lint": "eslint .", - "lint:fix": "eslint --fix .", - "updateBinary": "node downloadCurrentVersion.js && node verifyVersion.js", - "prepublishOnly": "npm run updateBinary", - "pretest": "npm run lint", - "test": "tape ./test/index.js", - "coverage": "node ./node_modules/nyc/bin/nyc.js --reporter=lcov --reporter=text-summary ./node_modules/tape/bin/tape ./test/index.js", - "coveralls": "npm run coverage && node ./node_modules/coveralls/bin/coveralls.js =10.0.0" }, "files": [ - "abi.js", - "index.js", - "linker.js", - "smtchecker.js", - "smtsolver.js", - "solc.js", - "soljson.js", - "translate.js", - "wrapper.js" + "dist/abi.js", + "dist/index.js", + "dist/linker.js", + "dist/smtchecker.js", + "dist/smtsolver.js", + "dist/solc.js", + "dist/soljson.js", + "dist/translate.js", + "dist/wrapper.js" ], "author": "chriseth", "license": "MIT", @@ -55,19 +58,27 @@ "tmp": "0.0.33" }, "devDependencies": { + "@types/node": "^16.11.7", + "@types/semver": "^7.3.9", + "@types/tape": "^4.13.2", + "@typescript-eslint/eslint-plugin": "^5.8.0", + "@typescript-eslint/parser": "^5.8.0", "coveralls": "^3.0.0", "eslint": "^7.32.0", "eslint-config-standard": "^16.0.3", "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.1", - "nyc": "^14.1.0", + "nyc": "^15.1.0", "tape": "^4.11.0", - "tape-spawn": "^1.4.2" + "tape-spawn": "^1.4.2", + "ts-node": "^10.4.0", + "typescript": "^4.5.4" }, "nyc": { "exclude": [ - "soljson.js" + "soljson.js", + "dist" ] } } diff --git a/smtchecker.js b/smtchecker.ts similarity index 83% rename from smtchecker.js rename to smtchecker.ts index 084778d1..1dc4a910 100644 --- a/smtchecker.js +++ b/smtchecker.ts @@ -3,7 +3,7 @@ // The function runs an SMT solver on each query and adjusts the input for // another run. // Returns null if no solving is requested. -function handleSMTQueries (inputJSON, outputJSON, solverFunction, solver) { +function handleSMTQueries (inputJSON: any, outputJSON: any, solverFunction: any, solver?: any) { const auxInputReq = outputJSON.auxiliaryInputRequested; if (!auxInputReq) { return null; @@ -25,7 +25,7 @@ function handleSMTQueries (inputJSON, outputJSON, solverFunction, solver) { return inputJSON; } -function smtCallback (solverFunction, solver) { +function smtCallback (solverFunction, solver?: any) { return function (query) { try { const result = solverFunction(query, solver); @@ -36,7 +36,7 @@ function smtCallback (solverFunction, solver) { }; } -module.exports = { - handleSMTQueries: handleSMTQueries, - smtCallback: smtCallback +export default { + handleSMTQueries, + smtCallback }; diff --git a/smtsolver.js b/smtsolver.ts similarity index 91% rename from smtsolver.js rename to smtsolver.ts index 70d2e898..da817837 100644 --- a/smtsolver.js +++ b/smtsolver.ts @@ -1,7 +1,7 @@ -const commandExistsSync = require('command-exists').sync; -const execSync = require('child_process').execSync; -const fs = require('fs'); -const tmp = require('tmp'); +import { sync as commandExistsSync } from 'command-exists'; +import { execSync } from 'child_process'; +import * as fs from 'fs'; +import tmp from 'tmp'; // Timeout in ms. const timeout = 10000; @@ -67,7 +67,7 @@ function solve (query, solver) { return solverOutput; } -module.exports = { +export default { smtSolver: solve, availableSolvers: solvers }; diff --git a/solc.js b/solc.ts similarity index 96% rename from solc.js rename to solc.ts index 08f230ae..9f565883 100755 --- a/solc.js +++ b/solc.ts @@ -1,12 +1,12 @@ #!/usr/bin/env node -const commander = require('commander'); -const fs = require('fs'); -const os = require('os'); -const path = require('path'); -const solc = require('./index.js'); -const smtchecker = require('./smtchecker.js'); -const smtsolver = require('./smtsolver.js'); +import * as commander from 'commander'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import solc from './index'; +import smtchecker from './smtchecker'; +import smtsolver from './smtsolver'; // hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end const originalUncaughtExceptionListeners = process.listeners('uncaughtException'); @@ -14,7 +14,7 @@ const originalUncaughtExceptionListeners = process.listeners('uncaughtException' // see https://github.com/chriseth/browser-solidity/issues/167 process.removeAllListeners('uncaughtException'); -const program = new commander.Command(); +const program: any = new commander.Command(); const commanderParseInt = function (value) { const parsedValue = parseInt(value, 10); if (isNaN(parsedValue)) { diff --git a/test/abi.js b/test/abi.ts similarity index 98% rename from test/abi.js rename to test/abi.ts index 63cd7fa7..98acacbb 100644 --- a/test/abi.js +++ b/test/abi.ts @@ -1,5 +1,5 @@ -const tape = require('tape'); -const abi = require('../abi.js'); +import * as tape from 'tape'; +import abi from '../abi'; tape('ABI translator', function (t) { t.test('Empty ABI', function (st) { diff --git a/test/cli.js b/test/cli.ts similarity index 98% rename from test/cli.js rename to test/cli.ts index 9d1bbbdb..afa63e4a 100644 --- a/test/cli.js +++ b/test/cli.ts @@ -1,7 +1,7 @@ -const tape = require('tape'); -const spawn = require('tape-spawn'); -const path = require('path'); -const pkg = require('../package.json'); +import * as tape from 'tape'; +import * as spawn from 'tape-spawn'; +import * as path from 'path'; +import * as pkg from '../package.json'; tape('CLI', function (t) { t.test('--version', function (st) { diff --git a/test/compiler.js b/test/compiler.ts similarity index 98% rename from test/compiler.js rename to test/compiler.ts index 8b7ce4cf..55e72f7a 100644 --- a/test/compiler.js +++ b/test/compiler.ts @@ -1,9 +1,10 @@ -const assert = require('assert'); -const tape = require('tape'); -const semver = require('semver'); -const solc = require('../index.js'); -const linker = require('../linker.js'); -const execSync = require('child_process').execSync; +import * as assert from 'assert'; +import * as tape from 'tape'; +import * as semver from 'semver'; +import solc from '../'; +import linker from '../linker'; +import { execSync } from 'child_process'; +import wrapper from '../wrapper'; const noRemoteVersions = (process.argv.indexOf('--no-remote-versions') >= 0); @@ -68,10 +69,10 @@ function runTests (solc, versionText) { } } - function expectError (output, errorType, message) { + function expectError (output: any, errorType: any, message: any) { if (output.errors) { - for (let error in output.errors) { - error = output.errors[error]; + for (const errorIndex in output.errors) { + const error = output.errors[errorIndex]; if (error.type === errorType) { if (message) { if (error.message.match(message) !== null) { @@ -86,10 +87,10 @@ function runTests (solc, versionText) { return false; } - function expectNoError (output) { + function expectNoError (output: any) { if (output.errors) { - for (let error in output.errors) { - error = output.errors[error]; + for (const errorIndex in output.errors) { + const error = output.errors[errorIndex]; if (error.severity === 'error') { return false; } @@ -888,7 +889,7 @@ if (!noRemoteVersions) { for (let version in versions) { version = versions[version]; execSync(`curl -L -o /tmp/${version}.js https://binaries.soliditylang.org/bin/soljson-${version}.js`); - const newSolc = require('../wrapper.js')(require(`/tmp/${version}.js`)); + const newSolc = wrapper(require(`/tmp/${version}.js`)); runTests(newSolc, version); } } diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 10e97cc6..00000000 --- a/test/index.js +++ /dev/null @@ -1,13 +0,0 @@ -const semver = require('semver'); - -require('./linker.js'); -require('./translate.js'); -require('./compiler.js'); -require('./smtcallback.js'); -require('./smtchecker.js'); -require('./abi.js'); - -// The CLI doesn't support Node 4 -if (semver.gte(process.version, '5.0.0')) { - require('./cli.js'); -} diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 00000000..9815fa16 --- /dev/null +++ b/test/index.ts @@ -0,0 +1,13 @@ +import * as semver from 'semver'; + +import('./linker'); +import('./translate'); +import('./compiler'); +import('./smtcallback'); +import('./smtchecker'); +import('./abi'); + +// The CLI doesn't support Node 4 +if (semver.gte(process.version, '5.0.0')) { + import('./cli'); +} diff --git a/test/linker.js b/test/linker.ts similarity index 99% rename from test/linker.js rename to test/linker.ts index e7893742..e9f2ba33 100644 --- a/test/linker.js +++ b/test/linker.ts @@ -1,5 +1,5 @@ -const tape = require('tape'); -const linker = require('../linker.js'); +import * as tape from 'tape'; +import linker from '../linker'; tape('Link references', function (t) { t.test('Empty bytecode', function (st) { diff --git a/test/smtcallback.js b/test/smtcallback.ts similarity index 96% rename from test/smtcallback.js rename to test/smtcallback.ts index db21b5ec..f5a157ec 100644 --- a/test/smtcallback.js +++ b/test/smtcallback.ts @@ -1,11 +1,11 @@ -const assert = require('assert'); -const tape = require('tape'); -const fs = require('fs'); -const path = require('path'); -const semver = require('semver'); -const solc = require('../index.js'); -const smtchecker = require('../smtchecker.js'); -const smtsolver = require('../smtsolver.js'); +import * as assert from 'assert'; +import * as tape from 'tape'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as semver from 'semver'; +import solc from '../'; +import smtchecker from '../smtchecker'; +import smtsolver from '../smtsolver'; const preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; diff --git a/test/smtchecker.js b/test/smtchecker.ts similarity index 94% rename from test/smtchecker.js rename to test/smtchecker.ts index b2927e48..8621bc5a 100644 --- a/test/smtchecker.js +++ b/test/smtchecker.ts @@ -1,8 +1,8 @@ -const tape = require('tape'); -const semver = require('semver'); -const solc = require('../index.js'); -const smtchecker = require('../smtchecker.js'); -const smtsolver = require('../smtsolver.js'); +import * as tape from 'tape'; +import * as semver from 'semver'; +import solc from '../'; +import smtchecker from '../smtchecker'; +import smtsolver from '../smtsolver'; const preamble = 'pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n'; // diff --git a/test/translate.js b/test/translate.ts similarity index 94% rename from test/translate.js rename to test/translate.ts index cf321bde..8859df4f 100644 --- a/test/translate.js +++ b/test/translate.ts @@ -1,7 +1,8 @@ -const fs = require('fs'); -const path = require('path'); -const tape = require('tape'); -const translate = require('../translate.js'); +import * as fs from 'fs'; +import * as path from 'path'; +import * as tape from 'tape'; +import translate from '../translate'; + const versionToSemver = translate.versionToSemver; tape('Version string to Semver translator', function (t) { diff --git a/translate.js b/translate.ts similarity index 94% rename from translate.js rename to translate.ts index 9ee560ba..187a6619 100644 --- a/translate.js +++ b/translate.ts @@ -1,4 +1,4 @@ -const linker = require('./linker.js'); +import linker from './linker'; /// Translate old style version numbers to semver. /// Old style: 0.3.6-3fc68da5/Release-Emscripten/clang @@ -28,7 +28,7 @@ function versionToSemver (version) { function translateErrors (ret, errors) { for (const error in errors) { let type = 'error'; - let extractType = /^(.*):(\d+):(\d+):(.*):/; + let extractType: any = /^(.*):(\d+):(\d+):(.*):/; extractType = extractType.exec(errors[error]); if (extractType) { type = extractType[4].trim(); @@ -64,7 +64,7 @@ function translateGasEstimates (gasEstimates) { } function translateJsonCompilerOutput (output, libraries) { - const ret = {}; + const ret: any = {}; ret.errors = []; let errors; @@ -93,7 +93,7 @@ function translateJsonCompilerOutput (output, libraries) { const contractInput = output.contracts[contract]; const gasEstimates = contractInput.gasEstimates; - const translatedGasEstimates = {}; + const translatedGasEstimates: any = {}; if (gasEstimates.creation) { translatedGasEstimates.creation = { @@ -193,8 +193,8 @@ function prettyPrintLegacyAssemblyJSON (assembly, source) { return formatAssemblyText(assembly, '', source); } -module.exports = { - versionToSemver: versionToSemver, - translateJsonCompilerOutput: translateJsonCompilerOutput, - prettyPrintLegacyAssemblyJSON: prettyPrintLegacyAssemblyJSON +export default { + versionToSemver, + translateJsonCompilerOutput, + prettyPrintLegacyAssemblyJSON }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..88ea21fd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + // Configuration reference: https://www.typescriptlang.org/tsconfig + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "resolveJsonModule": true, + "outDir": "./dist", + "forceConsistentCasingInFileNames": true, + // Allow JS must be included to ensure that the built binary is included + // in the output. This could be copied directly in the future if required. + "allowJs": true, + // TODO: + // In order to gracefully move our project to TypeScript without having + // TS immediately yell at you, we'll disable strict mode for now. + "strict": false, + "noImplicitAny": false + }, + "include": [ + "**/*.js", + "**/*.ts", + "**/*.json" + ], + "exclude": [ + "coverage", + "dist" + ], + "ts-node": { + "transpileOnly": true + } +} diff --git a/verifyVersion.js b/verifyVersion.ts similarity index 63% rename from verifyVersion.js rename to verifyVersion.ts index 497b3f39..2dc258a4 100755 --- a/verifyVersion.js +++ b/verifyVersion.ts @@ -1,9 +1,11 @@ #!/usr/bin/env node -const semver = require('semver'); +import * as semver from 'semver'; -const packageVersion = require('./package.json').version; -const solcVersion = require('./index.js').version(); +import { version as packageVersion } from './package.json'; +import solc from './'; + +const solcVersion = (solc as any).version(); console.log('solcVersion: ' + solcVersion); console.log('packageVersion: ' + packageVersion); diff --git a/wrapper.js b/wrapper.ts similarity index 97% rename from wrapper.js rename to wrapper.ts index f043abeb..7b79540d 100755 --- a/wrapper.js +++ b/wrapper.ts @@ -1,9 +1,10 @@ -const assert = require('assert'); -const translate = require('./translate.js'); -const Module = module.constructor; -const https = require('follow-redirects').https; -const MemoryStream = require('memorystream'); -const semver = require('semver'); +import translate from './translate'; +import { https } from 'follow-redirects'; +import * as MemoryStream from 'memorystream'; +import * as assert from 'assert'; +import * as semver from 'semver'; + +const Module = module.constructor as any; function setupMethods (soljson) { let version; @@ -209,7 +210,7 @@ function setupMethods (soljson) { } // Expects a Standard JSON I/O but supports old compilers - const compileStandardWrapper = function (input, readCallback) { + const compileStandardWrapper = function (input, readCallback?: any) { if (compileStandard !== null) { return compileStandard(input, readCallback); } @@ -356,4 +357,4 @@ function setupMethods (soljson) { }; } -module.exports = setupMethods; +export default setupMethods; From a6128363b955645587e551e242839d7a0e8b1cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Jan 2022 17:38:20 +0100 Subject: [PATCH 123/153] Change tmp import in smtsolver.ts to the correct form --- smtsolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smtsolver.ts b/smtsolver.ts index da817837..1a2c1854 100644 --- a/smtsolver.ts +++ b/smtsolver.ts @@ -1,7 +1,7 @@ import { sync as commandExistsSync } from 'command-exists'; import { execSync } from 'child_process'; import * as fs from 'fs'; -import tmp from 'tmp'; +import * as tmp from 'tmp'; // Timeout in ms. const timeout = 10000; From 8405162e2bb66ed2467ddc85cf65aded20044faa Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Tue, 25 Jan 2022 22:01:29 +0000 Subject: [PATCH 124/153] fix: export all properties under the root instead of default By exporting under the root, javascript importing modules will continue to use it as expected without first having to index `.default.` --- index.ts | 23 ++++++++++++++++++++++- solc.ts | 2 +- test/compiler.ts | 2 +- test/smtcallback.ts | 2 +- test/smtchecker.ts | 2 +- verifyVersion.ts | 2 +- 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/index.ts b/index.ts index 0297cdf2..08b69910 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,26 @@ import wrapper from './wrapper'; const soljson = require('./soljson.js'); +const wrapped = wrapper(soljson); -export default wrapper(soljson); +const { + version, + semver, + license, + lowlevel, + features, + compile, + loadRemoteVersion, + setupMethods +} = wrapped; + +export { + version, + semver, + license, + lowlevel, + features, + compile, + loadRemoteVersion, + setupMethods +}; diff --git a/solc.ts b/solc.ts index 9f565883..2a6261af 100755 --- a/solc.ts +++ b/solc.ts @@ -4,7 +4,7 @@ import * as commander from 'commander'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import solc from './index'; +import * as solc from './index'; import smtchecker from './smtchecker'; import smtsolver from './smtsolver'; diff --git a/test/compiler.ts b/test/compiler.ts index 55e72f7a..612fa102 100644 --- a/test/compiler.ts +++ b/test/compiler.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import * as tape from 'tape'; import * as semver from 'semver'; -import solc from '../'; +import * as solc from '../'; import linker from '../linker'; import { execSync } from 'child_process'; import wrapper from '../wrapper'; diff --git a/test/smtcallback.ts b/test/smtcallback.ts index f5a157ec..8dc716ec 100644 --- a/test/smtcallback.ts +++ b/test/smtcallback.ts @@ -3,7 +3,7 @@ import * as tape from 'tape'; import * as fs from 'fs'; import * as path from 'path'; import * as semver from 'semver'; -import solc from '../'; +import * as solc from '../'; import smtchecker from '../smtchecker'; import smtsolver from '../smtsolver'; diff --git a/test/smtchecker.ts b/test/smtchecker.ts index 8621bc5a..26044ce4 100644 --- a/test/smtchecker.ts +++ b/test/smtchecker.ts @@ -1,6 +1,6 @@ import * as tape from 'tape'; import * as semver from 'semver'; -import solc from '../'; +import * as solc from '../'; import smtchecker from '../smtchecker'; import smtsolver from '../smtsolver'; diff --git a/verifyVersion.ts b/verifyVersion.ts index 2dc258a4..4a0e1e3d 100755 --- a/verifyVersion.ts +++ b/verifyVersion.ts @@ -3,7 +3,7 @@ import * as semver from 'semver'; import { version as packageVersion } from './package.json'; -import solc from './'; +import * as solc from './'; const solcVersion = (solc as any).version(); From 25c98cbcc563b5e18c0b75ae7a460ed874f8b606 Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Wed, 26 Jan 2022 17:20:18 +0000 Subject: [PATCH 125/153] fix: use esModuleInterop to resolve commonjs default require By doing so allows the commonjs default imports to work as expected not blocking backwards compatibility. reference: https://www.typescriptlang.org/tsconfig#esModuleInterop --- abi.ts | 2 +- downloadCurrentVersion.ts | 4 ++-- index.ts | 24 +----------------------- linker.ts | 4 ++-- smtchecker.ts | 2 +- smtsolver.ts | 2 +- solc.ts | 2 +- test/abi.ts | 2 +- test/cli.ts | 6 +++--- test/compiler.ts | 6 +++--- test/linker.ts | 2 +- test/smtcallback.ts | 6 +++--- test/smtchecker.ts | 4 ++-- test/translate.ts | 2 +- translate.ts | 2 +- tsconfig.json | 4 ++++ verifyVersion.ts | 4 ++-- wrapper.ts | 6 +++--- 18 files changed, 33 insertions(+), 51 deletions(-) diff --git a/abi.ts b/abi.ts index aa0ae977..ebe6a8ce 100644 --- a/abi.ts +++ b/abi.ts @@ -58,6 +58,6 @@ function update (compilerVersion, abi) { return abi; } -export default { +export = { update }; diff --git a/downloadCurrentVersion.ts b/downloadCurrentVersion.ts index f38aff8a..6b524a49 100755 --- a/downloadCurrentVersion.ts +++ b/downloadCurrentVersion.ts @@ -3,11 +3,11 @@ // This is used to download the correct binary version // as part of the prepublish step. -import * as pkg from './package.json'; import * as fs from 'fs'; import { https } from 'follow-redirects'; -import * as MemoryStream from 'memorystream'; +import MemoryStream from 'memorystream'; import { keccak256 } from 'js-sha3'; +const pkg = require('./package.json'); function getVersionList (cb) { console.log('Retrieving available version list...'); diff --git a/index.ts b/index.ts index 08b69910..80eb230a 100644 --- a/index.ts +++ b/index.ts @@ -1,26 +1,4 @@ import wrapper from './wrapper'; const soljson = require('./soljson.js'); -const wrapped = wrapper(soljson); - -const { - version, - semver, - license, - lowlevel, - features, - compile, - loadRemoteVersion, - setupMethods -} = wrapped; - -export { - version, - semver, - license, - lowlevel, - features, - compile, - loadRemoteVersion, - setupMethods -}; +export = wrapper(soljson); diff --git a/linker.ts b/linker.ts index 59199276..8dd1199d 100644 --- a/linker.ts +++ b/linker.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert'; +import assert from 'assert'; import { keccak256 } from 'js-sha3'; function libraryHashPlaceholder (input) { @@ -88,7 +88,7 @@ const findLinkReferences = function (bytecode) { return linkReferences; }; -export default { +export = { linkBytecode, findLinkReferences }; diff --git a/smtchecker.ts b/smtchecker.ts index 1dc4a910..78e64248 100644 --- a/smtchecker.ts +++ b/smtchecker.ts @@ -36,7 +36,7 @@ function smtCallback (solverFunction, solver?: any) { }; } -export default { +export = { handleSMTQueries, smtCallback }; diff --git a/smtsolver.ts b/smtsolver.ts index 1a2c1854..9bd73b36 100644 --- a/smtsolver.ts +++ b/smtsolver.ts @@ -67,7 +67,7 @@ function solve (query, solver) { return solverOutput; } -export default { +export = { smtSolver: solve, availableSolvers: solvers }; diff --git a/solc.ts b/solc.ts index 2a6261af..9f565883 100755 --- a/solc.ts +++ b/solc.ts @@ -4,7 +4,7 @@ import * as commander from 'commander'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import * as solc from './index'; +import solc from './index'; import smtchecker from './smtchecker'; import smtsolver from './smtsolver'; diff --git a/test/abi.ts b/test/abi.ts index 98acacbb..6328c904 100644 --- a/test/abi.ts +++ b/test/abi.ts @@ -1,4 +1,4 @@ -import * as tape from 'tape'; +import tape from 'tape'; import abi from '../abi'; tape('ABI translator', function (t) { diff --git a/test/cli.ts b/test/cli.ts index afa63e4a..0417c0a0 100644 --- a/test/cli.ts +++ b/test/cli.ts @@ -1,7 +1,7 @@ -import * as tape from 'tape'; -import * as spawn from 'tape-spawn'; +import tape from 'tape'; +import spawn from 'tape-spawn'; import * as path from 'path'; -import * as pkg from '../package.json'; +const pkg = require('../package.json'); tape('CLI', function (t) { t.test('--version', function (st) { diff --git a/test/compiler.ts b/test/compiler.ts index 612fa102..b60d06a3 100644 --- a/test/compiler.ts +++ b/test/compiler.ts @@ -1,7 +1,7 @@ -import * as assert from 'assert'; -import * as tape from 'tape'; +import assert from 'assert'; +import tape from 'tape'; import * as semver from 'semver'; -import * as solc from '../'; +import solc from '../'; import linker from '../linker'; import { execSync } from 'child_process'; import wrapper from '../wrapper'; diff --git a/test/linker.ts b/test/linker.ts index e9f2ba33..588b9992 100644 --- a/test/linker.ts +++ b/test/linker.ts @@ -1,4 +1,4 @@ -import * as tape from 'tape'; +import tape from 'tape'; import linker from '../linker'; tape('Link references', function (t) { diff --git a/test/smtcallback.ts b/test/smtcallback.ts index 8dc716ec..726cc87e 100644 --- a/test/smtcallback.ts +++ b/test/smtcallback.ts @@ -1,9 +1,9 @@ -import * as assert from 'assert'; -import * as tape from 'tape'; +import assert from 'assert'; +import tape from 'tape'; import * as fs from 'fs'; import * as path from 'path'; import * as semver from 'semver'; -import * as solc from '../'; +import solc from '../'; import smtchecker from '../smtchecker'; import smtsolver from '../smtsolver'; diff --git a/test/smtchecker.ts b/test/smtchecker.ts index 26044ce4..70193339 100644 --- a/test/smtchecker.ts +++ b/test/smtchecker.ts @@ -1,6 +1,6 @@ -import * as tape from 'tape'; +import tape from 'tape'; import * as semver from 'semver'; -import * as solc from '../'; +import solc from '../'; import smtchecker from '../smtchecker'; import smtsolver from '../smtsolver'; diff --git a/test/translate.ts b/test/translate.ts index 8859df4f..ecd15231 100644 --- a/test/translate.ts +++ b/test/translate.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as tape from 'tape'; +import tape from 'tape'; import translate from '../translate'; const versionToSemver = translate.versionToSemver; diff --git a/translate.ts b/translate.ts index 187a6619..ca0c39f7 100644 --- a/translate.ts +++ b/translate.ts @@ -193,7 +193,7 @@ function prettyPrintLegacyAssemblyJSON (assembly, source) { return formatAssemblyText(assembly, '', source); } -export default { +export = { versionToSemver, translateJsonCompilerOutput, prettyPrintLegacyAssemblyJSON diff --git a/tsconfig.json b/tsconfig.json index 88ea21fd..a4420494 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,10 @@ "target": "esnext", "module": "commonjs", "resolveJsonModule": true, + // This is needed for backwards-compatibility, to keep imports of the form `wrapper = require('solc/wrapper)` + // working like they did before the TypeScript migration. + // TODO: Drop it in the next breaking release. + "esModuleInterop": true, "outDir": "./dist", "forceConsistentCasingInFileNames": true, // Allow JS must be included to ensure that the built binary is included diff --git a/verifyVersion.ts b/verifyVersion.ts index 4a0e1e3d..863e6bee 100755 --- a/verifyVersion.ts +++ b/verifyVersion.ts @@ -1,9 +1,9 @@ #!/usr/bin/env node import * as semver from 'semver'; +import solc from './'; -import { version as packageVersion } from './package.json'; -import * as solc from './'; +const { version: packageVersion } = require('./package.json'); const solcVersion = (solc as any).version(); diff --git a/wrapper.ts b/wrapper.ts index 7b79540d..36c18d74 100755 --- a/wrapper.ts +++ b/wrapper.ts @@ -1,7 +1,7 @@ import translate from './translate'; import { https } from 'follow-redirects'; -import * as MemoryStream from 'memorystream'; -import * as assert from 'assert'; +import MemoryStream from 'memorystream'; +import assert from 'assert'; import * as semver from 'semver'; const Module = module.constructor as any; @@ -357,4 +357,4 @@ function setupMethods (soljson) { }; } -export default setupMethods; +export = setupMethods; From 30a75e4371be58af2b35247e223d7b3caec769ea Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Wed, 26 Jan 2022 20:27:02 +0000 Subject: [PATCH 126/153] fix: use npm run publish to release the dist folder directly --- build/pack-publish-block.js | 9 +++++++++ package.json | 27 +++++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 build/pack-publish-block.js diff --git a/build/pack-publish-block.js b/build/pack-publish-block.js new file mode 100644 index 00000000..8ce0f6e4 --- /dev/null +++ b/build/pack-publish-block.js @@ -0,0 +1,9 @@ +// This is meant to run in a hook before npm pack. +// Reporting an error from the hook interrupts the command. +if (process.env.BYPASS_SAFETY_CHECK === 'false' || process.env.BYPASS_SAFETY_CHECK === undefined) { + console.error('Run `npm run build:tarball` or `npm run publish:tarball` to pack or publish the package'); + process.exit(1); +} else if (process.env.BYPASS_SAFETY_CHECK !== 'true') { + console.error('Invalid value of the BYPASS_SAFETY_CHECK variable. Must be "true", "false" or unset.'); + process.exit(1); +} diff --git a/package.json b/package.json index c0d3c8e7..700b1c7f 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "solc", "version": "0.8.11", "description": "Solidity compiler", - "main": "dist/index.js", + "main": "index.js", "bin": { - "solcjs": "dist/solc.js" + "solcjs": "solc.js" }, "scripts": { "build": "tsc", @@ -12,7 +12,10 @@ "lint": "eslint --ext .js,.ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "updateBinary": "node build/clean.js && ts-node ./downloadCurrentVersion.ts && ts-node ./verifyVersion.ts", - "prepublishOnly": "npm run updateBinary npm && npm run build", + "prepack": "node build/pack-publish-block.js", + "build:tarball": "npm run updateBinary && npm run build && BYPASS_SAFETY_CHECK=true npm pack ./dist", + "publish:tarball": "tarball=$(npm run --silent tarballName) && ls \"$tarball\" && BYPASS_SAFETY_CHECK=true npm publish \"$tarball\"", + "tarballName": "jq --raw-output '.name + \"-\" + .version + \".tgz\"' package.json", "copyTestFiles": "cp -r ./test/resources ./dist/test/", "pretest": "npm run lint && npm run build && npm run copyTestFiles", "test": "cd dist && tape ./test/index.js", @@ -32,15 +35,15 @@ "node": ">=10.0.0" }, "files": [ - "dist/abi.js", - "dist/index.js", - "dist/linker.js", - "dist/smtchecker.js", - "dist/smtsolver.js", - "dist/solc.js", - "dist/soljson.js", - "dist/translate.js", - "dist/wrapper.js" + "abi.js", + "index.js", + "linker.js", + "smtchecker.js", + "smtsolver.js", + "solc.js", + "soljson.js", + "translate.js", + "wrapper.js" ], "author": "chriseth", "license": "MIT", From 0b512164779f4042d01c89ecbac5c56023b50f2c Mon Sep 17 00:00:00 2001 From: adi <50960175+adi611@users.noreply.github.com> Date: Tue, 25 Jan 2022 21:37:26 +0530 Subject: [PATCH 127/153] fix: create temporary directory instead of /tmp Create temporary directory fix-parentheses require to import add typescript support for tmp add typescript support for tmp compiler.ts add typescript support for tmp compiler.ts v-2 Change tmp import in smtsolver.ts to the correct form implement the suggestions --- package.json | 1 + test/compiler.ts | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c0d3c8e7..8044f0f6 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ }, "devDependencies": { "@types/node": "^16.11.7", + "@types/tmp": "^0.2.3", "@types/semver": "^7.3.9", "@types/tape": "^4.13.2", "@typescript-eslint/eslint-plugin": "^5.8.0", diff --git a/test/compiler.ts b/test/compiler.ts index b60d06a3..e05185c1 100644 --- a/test/compiler.ts +++ b/test/compiler.ts @@ -1,6 +1,7 @@ import assert from 'assert'; import tape from 'tape'; import * as semver from 'semver'; +import * as tmp from 'tmp'; import solc from '../'; import linker from '../linker'; import { execSync } from 'child_process'; @@ -888,8 +889,10 @@ if (!noRemoteVersions) { ]; for (let version in versions) { version = versions[version]; - execSync(`curl -L -o /tmp/${version}.js https://binaries.soliditylang.org/bin/soljson-${version}.js`); - const newSolc = wrapper(require(`/tmp/${version}.js`)); + // NOTE: The temporary directory will be removed on process exit. + const tempDir = tmp.dirSync({ unsafeCleanup: true, prefix: 'solc-js-compiler-test-' }).name; + execSync(`curl -L -o ${tempDir}/${version}.js https://binaries.soliditylang.org/bin/soljson-${version}.js`); + const newSolc = wrapper(require(`${tempDir}/${version}.js`)); runTests(newSolc, version); } } From 00c27509257572d28360b00448bfc621f36114e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Jan 2022 20:56:15 +0100 Subject: [PATCH 128/153] CI: Reusable command for cloning and installing dependencies --- .circleci/config.yml | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23b36898..8ca29322 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,6 +10,34 @@ workflows: - node-v17 version: 2.1 + +commands: + install-dependencies: + parameters: + cache-id: + type: string + path: + type: string + default: . + package-manager: + type: string + default: npm + dependency-file: + type: string + default: package.json + steps: + - restore_cache: + key: <>-dependency-cache-v2-{{ .Environment.CIRCLE_JOB }}-{{ checksum "<>/<>" }} + - run: + name: "<> install in <>" + command: | + cd "<>" + [[ -e node_modules/ ]] || <> install + - save_cache: + key: <>-dependency-cache-v2-{{ .Environment.CIRCLE_JOB }}-{{ checksum "<>/<>" }} + paths: + - "<>/node_modules/" + jobs: node-base: &node-base working_directory: ~/solc-js @@ -24,8 +52,8 @@ jobs: name: Versions command: npm version - checkout - - restore_cache: - key: dependency-cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package.json" }} + - install-dependencies: + cache-id: solc-js - run: name: install-npm command: npm install @@ -41,10 +69,6 @@ jobs: - run: name: coveralls command: npm run coveralls - - save_cache: - key: dependency-cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package.json" }} - paths: - - ./node_modules node-v10: <<: *node-base From 5e4be6188d651ea90ecbd78dc0d11178efe73b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Jan 2022 22:20:07 +0100 Subject: [PATCH 129/153] CI: Reusable step for printing npm version --- .circleci/config.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ca29322..f7c72f92 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,12 @@ workflows: version: 2.1 commands: + show-npm-version: + steps: + - run: + name: Versions + command: npm version + install-dependencies: parameters: cache-id: @@ -48,9 +54,7 @@ jobs: type: boolean default: false steps: - - run: - name: Versions - command: npm version + - show-npm-version - checkout - install-dependencies: cache-id: solc-js From 31b4139e9a3c8f6c9990595f50254aa5c2556f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Jan 2022 07:02:38 +0100 Subject: [PATCH 130/153] CI jobs for testing solc-js with Truffle and Hardhat --- .circleci/config.yml | 207 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7c72f92..39f35428 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,10 @@ workflows: - node-v16: run_coveralls: true - node-v17 + - hardhat-core-default-solc + - hardhat-core-latest-solc + - hardhat-sample-project + - truffle-sample-project version: 2.1 @@ -44,6 +48,96 @@ commands: paths: - "<>/node_modules/" + install-truffle-dependencies: + steps: + - run: + name: Store current Truffle commit ID in a variable + command: | + cd truffle/ + echo "export _TRUFFLE_COMMIT_ID=$(git rev-parse --verify HEAD)" >> $BASH_ENV + - restore_cache: + key: truffle-dependency-cache-{{ checksum "truffle/yarn.lock" }}-{{ .Environment._TRUFFLE_COMMIT_ID }} + - run: + name: yarn install in truffle + command: | + cd truffle/ + [[ -e node_modules/ ]] || yarn install + - save_cache: + key: truffle-dependency-cache-{{ checksum "truffle/yarn.lock" }}-{{ .Environment._TRUFFLE_COMMIT_ID }} + paths: + - truffle/ + + inject-solc-js-tarball: + description: "Recursively finds and replaces all instances of solc-js module installed in node_modules/ with the one from a tarball." + parameters: + path: + type: string + default: . + tarball-path: + type: string + default: solc-js.tgz + install-command: + type: string + default: npm install + steps: + - run: + name: Inject solc-js from the tarball into dependencies at <> + command: | + [[ -f "<>" ]] + absolute_tarball_path=$(realpath "<>") + for solc_module in $(find "<>" -type d -path "*/node_modules/solc"); do + pushd "${solc_module}/../.." + <> "$absolute_tarball_path" --ignore-workspace-root-check + popd + done + + provision-and-package-solcjs: + description: "Creates a package out of latest solc-js to test its installation as a dependency." + steps: + - checkout: + path: solc-js/ + - install-dependencies: + cache-id: solc-js + path: solc-js + - run: + name: Package solc-js + command: | + cd solc-js/ + npm run build:tarball + mv "$(npm run --silent tarballName)" ../solc-js.tgz + + provision-hardhat-with-packaged-solcjs: + description: "Clones Hardhat repository and configures it to use a local clone of solc-js." + steps: + - run: git clone --depth 1 "https://github.com/nomiclabs/hardhat" hardhat/ + - install-dependencies: + cache-id: hardhat + path: hardhat + package-manager: yarn + dependency-file: yarn.lock + - inject-solc-js-tarball: + path: hardhat/ + install-command: yarn add + + provision-truffle-with-packaged-solcjs: + description: "Clones Truffle repository and configures it to use a local clone of solc-js." + steps: + - run: git clone --depth 1 "https://github.com/trufflesuite/truffle" truffle/ + - install-truffle-dependencies + - inject-solc-js-tarball: + path: truffle/node_modules/ + install-command: yarn add + - run: + name: Neutralize any copies of solc-js outside of node_modules/ + command: | + # NOTE: Injecting solc-js into node_modules/ dirs located under truffle/packages/ causes + # an error 'Tarball is not in network and can not be located in cache'. These are not + # supposed to be used but let's remove them just in case. + find truffle/ \ + -path "*/solc/wrapper.js" \ + -not -path "truffle/node_modules/*" \ + -printf "%h\n" | xargs --verbose rm -r + jobs: node-base: &node-base working_directory: ~/solc-js @@ -74,6 +168,119 @@ jobs: name: coveralls command: npm run coveralls + hardhat-core-default-solc: + docker: + - image: circleci/node:16 + steps: + - show-npm-version + - provision-and-package-solcjs + - provision-hardhat-with-packaged-solcjs + - run: + name: Run hardhat-core test suite with its default solc + command: | + cd hardhat/packages/hardhat-core + yarn test + + hardhat-core-latest-solc: + docker: + - image: circleci/node:16 + steps: + - show-npm-version + - provision-and-package-solcjs + - provision-hardhat-with-packaged-solcjs + - run: + name: Run hardhat-core test suite with latest solc + command: | + HARDHAT_TESTS_SOLC_PATH="${PWD}/solc-js/soljson.js" + HARDHAT_TESTS_SOLC_VERSION=$(jq --raw-output .version solc-js/package.json) + export HARDHAT_TESTS_SOLC_PATH HARDHAT_TESTS_SOLC_VERSION + + cd hardhat/packages/hardhat-core + yarn test + + hardhat-sample-project: + docker: + - image: circleci/node:16 + steps: + - show-npm-version + - provision-and-package-solcjs + - run: git clone --depth 1 "https://github.com/nomiclabs/hardhat-hackathon-boilerplate" boilerplate/ + - run: + # Leaving package-lock.json causes a weird error in arborist when npm is used again after + # `npm install`: 'The "from" argument must be of type string. Received undefined' + name: Neutralize package-lock.json + command: rm boilerplate/package-lock.json + - install-dependencies: + cache-id: hardhat-hackathon-boilerplate + path: boilerplate + - run: + name: Update to the latest Hardhat release + command: | + # We can just use a release here because injection does not require rebuilding it. + cd boilerplate/ + npm update hardhat + - inject-solc-js-tarball: + path: boilerplate/ + - run: + name: Configure the boilerplate project to force Hardhat not to use a native binary + command: | + solc_version=$(jq --raw-output .version solc-js/package.json) + + cd boilerplate/ + + sed -i 's|pragma solidity [^;]\+;|pragma solidity *;|g' contracts/Token.sol + + { + echo "const {TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD} = require('hardhat/builtin-tasks/task-names');" + echo "const assert = require('assert');" + echo + echo "subtask(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args, hre, runSuper) => {" + echo " assert(args.solcVersion == '${solc_version}', 'Unexpected solc version: ' + args.solcVersion);" + echo " return {" + echo " compilerPath: '$(realpath "../solc-js/soljson.js")'," + echo " isSolcJs: true," + echo " version: args.solcVersion," + echo " longVersion: args.solcVersion" + echo " };" + echo "})" + echo "module.exports = {solidity: '${solc_version}'};" + } >> hardhat.config.js + - run: + name: Build and test the boilerplate project with local Hardhat + command: | + cd boilerplate/ + npm run test + + truffle-sample-project: + docker: + - image: circleci/node:12 + steps: + - show-npm-version + - provision-and-package-solcjs + - provision-truffle-with-packaged-solcjs + - run: + name: Unbox MetaCoin + command: | + mkdir metacoin/ + cd metacoin/ + node ../truffle/node_modules/.bin/truffle unbox metacoin + - run: + name: Strip version pragmas + command: sed -i 's|pragma solidity [^;]\+;|pragma solidity *;|g' $(find metacoin/{contracts,test}/ -name "*.sol") + - run: + name: Build and test the sample project with local Truffle and its default solc + command: | + cd metacoin/ + node ../truffle/node_modules/.bin/truffle test + - run: + name: Build and test the sample project with local Truffle and latest solc + command: | + cd metacoin/ + # `truffle test` compiles the project but artifacts go into /tmp/ + ! [[ -e build/ ]] || false + echo "module.exports['compilers'] = {solc: {version: '$(realpath node_modules/solc/)'}}" > truffle-config.js + node ../truffle/node_modules/.bin/truffle test + node-v10: <<: *node-base docker: From 57f0473af058a55a78fbea17ddedb98d1f12f3e2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Feb 2022 15:21:19 +0100 Subject: [PATCH 131/153] Set version to 0.8.12. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b712e4d6..4b8c444a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.11", + "version": "0.8.12", "description": "Solidity compiler", "main": "index.js", "bin": { From b8575ca21d0ca549b85781f6026e7718550ef4c2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Mar 2022 16:34:30 +0100 Subject: [PATCH 132/153] Version 0.8.13. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b8c444a..06abc52d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.12", + "version": "0.8.13", "description": "Solidity compiler", "main": "index.js", "bin": { From 8d515a9163947c263f6a28b8a7343d7b4a670e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 18 Mar 2022 19:01:16 +0100 Subject: [PATCH 133/153] Add `yarn build` to hardhat-core-default-solc job to work around breakage in recently added tests for --parallel --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 39f35428..3ead46cd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -179,6 +179,9 @@ jobs: name: Run hardhat-core test suite with its default solc command: | cd hardhat/packages/hardhat-core + # TODO: yarn build should not be needed to run these tests. Remove it. + # See https://github.com/NomicFoundation/hardhat/issues/2486 for details. + yarn build yarn test hardhat-core-latest-solc: From 51c954865c5263445313b54c47fda7816bbad3f2 Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Tue, 15 Feb 2022 14:09:22 +0000 Subject: [PATCH 134/153] refactor: linker.ts --- common/helpers.ts | 20 ++++++ linker.ts | 162 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 144 insertions(+), 38 deletions(-) create mode 100644 common/helpers.ts diff --git a/common/helpers.ts b/common/helpers.ts new file mode 100644 index 00000000..c21a91c6 --- /dev/null +++ b/common/helpers.ts @@ -0,0 +1,20 @@ +/** + * Returns true if and only if the value is null or undefined. + * + * @param value + */ +export function isNil (value: any): boolean { + // Uses == over === which compares both null and undefined. + return value == null; +} + +/** + * Returns true if and only if the value is an object and not an array. + * + * @param value + */ +export function isObject (value: any): boolean { + // typeof [] will result in an 'object' so this additionally uses Array.isArray + // to confirm it's just an object. + return typeof value === 'object' && !Array.isArray(value); +} diff --git a/linker.ts b/linker.ts index 8dd1199d..2955b66f 100644 --- a/linker.ts +++ b/linker.ts @@ -1,65 +1,150 @@ import assert from 'assert'; import { keccak256 } from 'js-sha3'; +import { isNil, isObject } from './common/helpers'; -function libraryHashPlaceholder (input) { - return '$' + keccak256(input).slice(0, 34) + '$'; +/** + * Generates a new-style library placeholder from a fully-qualified library name. + * + * Newer versions of the compiler use hashed names instead of just truncating the name + * before putting it in a placeholder. + * + * @param fullyQualifiedLibraryName Fully qualified library name. + */ +function libraryHashPlaceholder (fullyQualifiedLibraryName) { + return `$${keccak256(fullyQualifiedLibraryName).slice(0, 34)}$`; } -const linkBytecode = function (bytecode, libraries) { +/** + * Finds all placeholders corresponding to the specified library label and replaces them + * with a concrete address. Works with both hex-encoded and binary bytecode as long as + * the address is in the same format. + * + * @param bytecode Bytecode string. + * + * @param label Library label, either old- or new-style. Must exactly match the part between `__` markers in the + * placeholders. Will be padded with `_` characters if too short or truncated if too long. + * + * @param address Address to replace placeholders with. Must be the right length. + * It will **not** be padded with zeros if too short. + */ +function replacePlaceholder (bytecode, label, address) { + // truncate to 36 characters + const truncatedName = label.slice(0, 36); + const libLabel = `__${truncatedName.padEnd(36, '_')}__`; + + while (bytecode.indexOf(libLabel) >= 0) { + bytecode = bytecode.replace(libLabel, address); + } + + return bytecode; +} + +/** + * Finds and all library placeholders in the provided bytecode and replaces them with actual addresses. + * Supports both old- and new-style placeholders (even both in the same file). + * See [Library Linking](https://docs.soliditylang.org/en/latest/using-the-compiler.html#library-linking) + * for a full explanation of the linking process. + * + * Example of a legacy placeholder: `__lib.sol:L_____________________________` + * Example of a new-style placeholder: `__$cb901161e812ceb78cfe30ca65050c4337$__` + * + * @param bytecode Hex-encoded bytecode string. All 40-byte substrings starting and ending with + * `__` will be interpreted as placeholders. + * + * @param libraries Mapping between fully qualified library names and the hex-encoded + * addresses they should be replaced with. Addresses shorter than 40 characters are automatically padded with zeros. + * + * @returns bytecode Hex-encoded bytecode string with placeholders replaced with addresses. + * Note that some placeholders may remain in the bytecode if `libraries` does not provide addresses for all of them. + */ +function linkBytecode (bytecode, libraries) { assert(typeof bytecode === 'string'); assert(typeof libraries === 'object'); + // NOTE: for backwards compatibility support old compiler which didn't use file names const librariesComplete = {}; - for (const libraryName in libraries) { - if (typeof libraries[libraryName] === 'object') { - // API compatible with the standard JSON i/o - for (const lib in libraries[libraryName]) { - librariesComplete[lib] = libraries[libraryName][lib]; - librariesComplete[libraryName + ':' + lib] = libraries[libraryName][lib]; - } - } else { - // backwards compatible API for early solc-js versions - const parsed = libraryName.match(/^([^:]+):(.+)$/); - if (parsed) { - librariesComplete[parsed[2]] = libraries[libraryName]; + + for (const [fullyQualifiedLibraryName, libraryObjectOrAddress] of Object.entries(libraries)) { + if (isNil(libraryObjectOrAddress)) { + throw new Error(`No address provided for library ${fullyQualifiedLibraryName}`); + } + + // API compatible with the standard JSON i/o + // {"lib.sol": {"L": "0x..."}} + if (isObject(libraryObjectOrAddress)) { + for (const [unqualifiedLibraryName, address] of Object.entries(libraryObjectOrAddress)) { + librariesComplete[unqualifiedLibraryName] = address; + librariesComplete[`${fullyQualifiedLibraryName}:${unqualifiedLibraryName}`] = address; } - librariesComplete[libraryName] = libraries[libraryName]; + + continue; + } + + // backwards compatible API for early solc-js versions + const parsed = fullyQualifiedLibraryName.match(/^(?[^:]+):(?.+)$/); + const libraryAddress = libraryObjectOrAddress as string; + + if (!isNil(parsed)) { + const { unqualifiedLibraryName } = parsed.groups; + librariesComplete[unqualifiedLibraryName] = libraryAddress; } + + librariesComplete[fullyQualifiedLibraryName] = libraryAddress; } for (const libraryName in librariesComplete) { let hexAddress = librariesComplete[libraryName]; - if (hexAddress.slice(0, 2) !== '0x' || hexAddress.length > 42) { - throw new Error('Invalid address specified for ' + libraryName); + + if (!hexAddress.startsWith('0x') || hexAddress.length > 42) { + throw new Error(`Invalid address specified for ${libraryName}`); } + // remove 0x prefix - hexAddress = hexAddress.slice(2); - hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress; - - // Support old (library name) and new (hash of library name) - // placeholders. - const replace = function (name) { - // truncate to 37 characters - const truncatedName = name.slice(0, 36); - const libLabel = '__' + truncatedName + Array(37 - truncatedName.length).join('_') + '__'; - while (bytecode.indexOf(libLabel) >= 0) { - bytecode = bytecode.replace(libLabel, hexAddress); - } - }; + hexAddress = hexAddress.slice(2).padStart(40, '0'); - replace(libraryName); - replace(libraryHashPlaceholder(libraryName)); + bytecode = replacePlaceholder(bytecode, libraryName, hexAddress); + bytecode = replacePlaceholder(bytecode, libraryHashPlaceholder(libraryName), hexAddress); } return bytecode; -}; +} -const findLinkReferences = function (bytecode) { +/** + * Finds locations of all library address placeholders in the hex-encoded bytecode. + * Returns information in a format matching `evm.bytecode.linkReferences` output + * in Standard JSON. + * + * See [Library Linking](https://docs.soliditylang.org/en/latest/using-the-compiler.html#library-linking) + * for a full explanation of library placeholders and linking process. + * + * WARNING: The output matches `evm.bytecode.linkReferences` exactly only in + * case of old-style placeholders created from fully qualified library names + * of no more than 36 characters, and even then only if the name does not start + * or end with an underscore. This is different from + * `evm.bytecode.linkReferences`, which uses fully qualified library names. + * This is a limitation of the placeholder format - the fully qualified names + * are not preserved in the compiled bytecode and cannot be reconstructed + * without external information. + * + * @param bytecode Hex-encoded bytecode string. + * + * @returns linkReferences A mapping between library labels and their locations + * in the bytecode. In case of old-style placeholders the label is a fully + * qualified library name truncated to 36 characters. For new-style placeholders + * it's the first 34 characters of the hex-encoded hash of the fully qualified + * library name, with a leading and trailing $ character added. Note that the + * offsets and lengths refer to the *binary* (not hex-encoded) bytecode, just + * like in `evm.bytecode.linkReferences`. + */ +function findLinkReferences (bytecode) { assert(typeof bytecode === 'string'); + // find 40 bytes in the pattern of __...<36 digits>...__ // e.g. __Lib.sol:L_____________________________ const linkReferences = {}; + let offset = 0; + while (true) { const found = bytecode.match(/__(.{36})__/); if (!found) { @@ -67,6 +152,7 @@ const findLinkReferences = function (bytecode) { } const start = found.index; + // trim trailing underscores // NOTE: this has no way of knowing if the trailing underscore was part of the name const libraryName = found[1].replace(/_+$/gm, ''); @@ -75,18 +161,18 @@ const findLinkReferences = function (bytecode) { linkReferences[libraryName] = []; } + // offsets are in bytes in binary representation (and not hex) linkReferences[libraryName].push({ - // offsets are in bytes in binary representation (and not hex) start: (offset + start) / 2, length: 20 }); offset += start + 20; - bytecode = bytecode.slice(start + 20); } + return linkReferences; -}; +} export = { linkBytecode, From 6409860c35216451fdad4f83770bb24c9324d08d Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Tue, 15 Feb 2022 14:03:21 +0000 Subject: [PATCH 135/153] types: include basic types for linker.ts --- common/types.ts | 26 ++++++++++++++++++++++++++ linker.ts | 9 +++++---- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 common/types.ts diff --git a/common/types.ts b/common/types.ts new file mode 100644 index 00000000..254484a1 --- /dev/null +++ b/common/types.ts @@ -0,0 +1,26 @@ +/** + * A mapping between libraries and the addresses to which they were deployed. + * + * Containing support for two level configuration, These two level + * configurations can be seen below. + * + * { + * "lib.sol:L1": "0x...", + * "lib.sol:L2": "0x...", + * "lib.sol": {"L3": "0x..."} + * } + */ +export interface LibraryAddresses { + [qualifiedNameOrSourceUnit: string]: string | { [unqualifiedLibraryName: string]: string }; +} + +/** + * A mapping between libraries and lists of placeholder instances present in their hex-encoded bytecode. + * For each placeholder its length and the position of the first character is stored. + * + * Each start and length entry will always directly refer to the position in + * binary and not hex-encoded bytecode. + */ +export interface LinkReferences { + [libraryLabel: string]: Array<{ start: number, length: number }>; +} diff --git a/linker.ts b/linker.ts index 2955b66f..619cae57 100644 --- a/linker.ts +++ b/linker.ts @@ -1,6 +1,7 @@ import assert from 'assert'; import { keccak256 } from 'js-sha3'; import { isNil, isObject } from './common/helpers'; +import { LibraryAddresses, LinkReferences } from './common/types'; /** * Generates a new-style library placeholder from a fully-qualified library name. @@ -57,12 +58,12 @@ function replacePlaceholder (bytecode, label, address) { * @returns bytecode Hex-encoded bytecode string with placeholders replaced with addresses. * Note that some placeholders may remain in the bytecode if `libraries` does not provide addresses for all of them. */ -function linkBytecode (bytecode, libraries) { +function linkBytecode (bytecode: string, libraries: LibraryAddresses): string { assert(typeof bytecode === 'string'); assert(typeof libraries === 'object'); // NOTE: for backwards compatibility support old compiler which didn't use file names - const librariesComplete = {}; + const librariesComplete: { [fullyQualifiedLibraryName: string]: string } = {}; for (const [fullyQualifiedLibraryName, libraryObjectOrAddress] of Object.entries(libraries)) { if (isNil(libraryObjectOrAddress)) { @@ -136,12 +137,12 @@ function linkBytecode (bytecode, libraries) { * offsets and lengths refer to the *binary* (not hex-encoded) bytecode, just * like in `evm.bytecode.linkReferences`. */ -function findLinkReferences (bytecode) { +function findLinkReferences (bytecode: string): LinkReferences { assert(typeof bytecode === 'string'); // find 40 bytes in the pattern of __...<36 digits>...__ // e.g. __Lib.sol:L_____________________________ - const linkReferences = {}; + const linkReferences: LinkReferences = {}; let offset = 0; From 302b51e859a4e78aa755db7b5dfc9c1f38108ce8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 May 2022 16:16:36 +0200 Subject: [PATCH 136/153] Set version to 0.8.14. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 06abc52d..0e34503e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.13", + "version": "0.8.14", "description": "Solidity compiler", "main": "index.js", "bin": { From d261104b70c92e203bd113dbc854d689959f28af Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 17 May 2022 18:08:49 +0200 Subject: [PATCH 137/153] Add common files to package.json. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e34503e..94c69358 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "solc.js", "soljson.js", "translate.js", - "wrapper.js" + "wrapper.js", + "common/helpers.js" ], "author": "chriseth", "license": "MIT", From f83b049d4c4992b0f4728bae0f64ff084e54febb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 18 May 2022 13:13:09 +0200 Subject: [PATCH 138/153] CI: Use latest npm in all jobs --- .circleci/config.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ead46cd..1a1d3ff4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,6 +22,13 @@ commands: name: Versions command: npm version + update-npm: + steps: + - run: + name: Update globally available npm to the latest version + # Note: We need npm >= 8.3 which supports 'overrides' in package.json + command: sudo npm install npm --global + install-dependencies: parameters: cache-id: @@ -148,6 +155,7 @@ jobs: type: boolean default: false steps: + # We want the default npm here. Older one might not work with older node.js - show-npm-version - checkout - install-dependencies: @@ -172,6 +180,7 @@ jobs: docker: - image: circleci/node:16 steps: + - update-npm - show-npm-version - provision-and-package-solcjs - provision-hardhat-with-packaged-solcjs @@ -188,6 +197,7 @@ jobs: docker: - image: circleci/node:16 steps: + - update-npm - show-npm-version - provision-and-package-solcjs - provision-hardhat-with-packaged-solcjs @@ -205,6 +215,7 @@ jobs: docker: - image: circleci/node:16 steps: + - update-npm - show-npm-version - provision-and-package-solcjs - run: git clone --depth 1 "https://github.com/nomiclabs/hardhat-hackathon-boilerplate" boilerplate/ @@ -258,6 +269,7 @@ jobs: docker: - image: circleci/node:12 steps: + - update-npm - show-npm-version - provision-and-package-solcjs - provision-truffle-with-packaged-solcjs From af9237021e4e44d340f76d39ea778dc4d540112e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 18 May 2022 11:37:13 +0200 Subject: [PATCH 139/153] CI: Fix solc-js injection and make it more robust --- .circleci/config.yml | 61 +++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a1d3ff4..b6ba4584 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -83,20 +83,34 @@ commands: tarball-path: type: string default: solc-js.tgz - install-command: - type: string - default: npm install + package-manager: + type: enum + enum: ["npm", "yarn"] + default: npm steps: - run: - name: Inject solc-js from the tarball into dependencies at <> + name: "Sanity check: tarball exists and the target dir contains a JS project" command: | [[ -f "<>" ]] + [[ -f "<>/package.json" ]] + - run: + name: Inject solc-js from the tarball into dependencies at <> + command: | absolute_tarball_path=$(realpath "<>") - for solc_module in $(find "<>" -type d -path "*/node_modules/solc"); do - pushd "${solc_module}/../.." - <> "$absolute_tarball_path" --ignore-workspace-root-check - popd - done + cd "<>" + mv package.json original-package.json + # NOTE: The 'overrides' feature requires npm >= 8.3. Yarn requires `resolutions` instead. + jq ". + {overrides: {solc: \"${absolute_tarball_path}\"}} + {resolutions: {solc: \"${absolute_tarball_path}\"}}" original-package.json > package.json + "<>" install + - run: + name: "Sanity check: all transitive dependencies successfully replaced with the tarball" + command: | + solc_version=$(jq --raw-output .version solc-js/package.json) + cd "<>" + if "<>" list --pattern solc | grep 'solc@' | grep -v "solc@${solc_version}"; then + echo "Another version of solc-js is still present in the dependency tree." + exit 1 + fi provision-and-package-solcjs: description: "Creates a package out of latest solc-js to test its installation as a dependency." @@ -124,7 +138,7 @@ commands: dependency-file: yarn.lock - inject-solc-js-tarball: path: hardhat/ - install-command: yarn add + package-manager: yarn provision-truffle-with-packaged-solcjs: description: "Clones Truffle repository and configures it to use a local clone of solc-js." @@ -132,18 +146,8 @@ commands: - run: git clone --depth 1 "https://github.com/trufflesuite/truffle" truffle/ - install-truffle-dependencies - inject-solc-js-tarball: - path: truffle/node_modules/ - install-command: yarn add - - run: - name: Neutralize any copies of solc-js outside of node_modules/ - command: | - # NOTE: Injecting solc-js into node_modules/ dirs located under truffle/packages/ causes - # an error 'Tarball is not in network and can not be located in cache'. These are not - # supposed to be used but let's remove them just in case. - find truffle/ \ - -path "*/solc/wrapper.js" \ - -not -path "truffle/node_modules/*" \ - -printf "%h\n" | xargs --verbose rm -r + path: truffle/ + package-manager: yarn jobs: node-base: &node-base @@ -185,7 +189,18 @@ jobs: - provision-and-package-solcjs - provision-hardhat-with-packaged-solcjs - run: - name: Run hardhat-core test suite with its default solc + name: Restore the default solc binary expected by Hardhat + command: | + # Hardhat downloader tests are hard-coded to expect the version that comes with the solc-js. + # We forced latest solc-js but we still want the default binary with it. + hardhat_default_solc_version=$(jq --raw-output '.dependencies.solc' hardhat/packages/hardhat-core/package.json) + mkdir hardhat-default-solc/ + pushd hardhat-default-solc/ + npm install "solc@${hardhat_default_solc_version}" + popd + ln -sf ../../../hardhat-default-solc/node_modules/solc/soljson.js hardhat/node_modules/solc/soljson.js + - run: + name: Run hardhat-core test suite with its default solc binary command: | cd hardhat/packages/hardhat-core # TODO: yarn build should not be needed to run these tests. Remove it. From 5636d586ca4be412089688f4978e9eee6a2bac64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 18 May 2022 17:12:36 +0200 Subject: [PATCH 140/153] CI: Add a smoke test for the CLI script --- .circleci/config.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b6ba4584..24457c8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,7 @@ workflows: - hardhat-core-latest-solc - hardhat-sample-project - truffle-sample-project + - cli-smoke-test version: 2.1 @@ -311,6 +312,35 @@ jobs: echo "module.exports['compilers'] = {solc: {version: '$(realpath node_modules/solc/)'}}" > truffle-config.js node ../truffle/node_modules/.bin/truffle test + cli-smoke-test: + docker: + - image: circleci/node:17 + steps: + - update-npm + - show-npm-version + - provision-and-package-solcjs + - run: + name: "CLI smoke test (repository)" + command: | + cd solc-js + dist/solc.js --version + + echo "contract C {}" > C.sol + dist/solc.js C.sol --bin + [[ -f C_sol_C.bin ]] + - run: + name: "CLI smoke test (package)" + command: | + mkdir package/ + cd package/ + npm install ../solc-js.tgz + + npx solcjs --version + + echo "contract C {}" > C.sol + npx solcjs C.sol --bin + [[ -f C_sol_C.bin ]] + node-v10: <<: *node-base docker: From ea3f446de4e822f9397c9a8e5c23e9ec829a12d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Jan 2022 07:02:59 +0100 Subject: [PATCH 141/153] CI job for running the solc-js test script from the main repo --- .circleci/config.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 24457c8d..75e04a9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,6 +13,7 @@ workflows: - hardhat-sample-project - truffle-sample-project - cli-smoke-test + - solidity-solcjs-ext-test version: 2.1 @@ -341,6 +342,17 @@ jobs: npx solcjs C.sol --bin [[ -f C_sol_C.bin ]] + solidity-solcjs-ext-test: + docker: + - image: circleci/node + steps: + - show-npm-version + - checkout: + path: solc-js/ + - run: git clone --depth 1 "https://github.com/ethereum/solidity" solidity/ + - run: cd solidity/ && curl "https://binaries.soliditylang.org/bin/soljson-nightly.js" --location --output soljson.js + - run: cd solidity/ && test/externalTests/solc-js/solc-js.sh "$(realpath soljson.js)" "$(scripts/get_version.sh)" "$(realpath ../solc-js/)" + node-v10: <<: *node-base docker: From 9a16f3da413e789b83537c731b5c9a207ee9e0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 19 May 2022 21:25:55 +0200 Subject: [PATCH 142/153] Fix CLI `--version` test to use version from the binary not `package.json` --- test/cli.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/cli.ts b/test/cli.ts index 0417c0a0..ab86e3f5 100644 --- a/test/cli.ts +++ b/test/cli.ts @@ -1,12 +1,13 @@ import tape from 'tape'; import spawn from 'tape-spawn'; import * as path from 'path'; -const pkg = require('../package.json'); +import solc from '../'; tape('CLI', function (t) { t.test('--version', function (st) { const spt = spawn(st, './solc.js --version'); - spt.stdout.match(RegExp(pkg.version + '(-[^a-zA-A0-9.+]+)?(\\+[^a-zA-Z0-9.-]+)?')); + spt.stdout.match(solc.version() + '\n'); + spt.stdout.match(/^\s*[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?\+commit\.[0-9a-f]+([a-zA-Z0-9.-]+)?\s*$/); spt.stderr.empty(); spt.end(); }); From 99b621d913b48045c95519044b32cf20abc9bc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 15 Jun 2022 18:18:29 +0200 Subject: [PATCH 143/153] Set version to 0.8.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94c69358..8f67becc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.14", + "version": "0.8.15", "description": "Solidity compiler", "main": "index.js", "bin": { From d34493d7451f1ba7203e221b06c33e844c2ee2b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 14 Jul 2022 19:51:00 +0200 Subject: [PATCH 144/153] CI: Truffle now requires node.js >= 14 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 75e04a9f..b1158746 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -284,7 +284,7 @@ jobs: truffle-sample-project: docker: - - image: circleci/node:12 + - image: circleci/node:16 steps: - update-npm - show-npm-version From d16c8acdc998460b4ed7ea332cc775925dd2ec34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 3 Aug 2022 22:43:56 +0200 Subject: [PATCH 145/153] CI: Update truffle-sample-project job to match Truffle's upstream changes --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b1158746..fa53ed5c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -309,8 +309,8 @@ jobs: command: | cd metacoin/ # `truffle test` compiles the project but artifacts go into /tmp/ - ! [[ -e build/ ]] || false - echo "module.exports['compilers'] = {solc: {version: '$(realpath node_modules/solc/)'}}" > truffle-config.js + ! [[ -e build/ ]] + echo "module.exports['compilers'] = {solc: {version: '$(realpath ../truffle/node_modules/solc/)'}}" >> truffle-config.js node ../truffle/node_modules/.bin/truffle test cli-smoke-test: From 9afecab104dbb25970a6846429f16466ffddea13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Jun 2022 20:26:08 +0200 Subject: [PATCH 146/153] CI: Switch from deprecated circleci/ images to cimg/ --- .circleci/config.yml | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fa53ed5c..fc7feada 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,7 +29,7 @@ commands: - run: name: Update globally available npm to the latest version # Note: We need npm >= 8.3 which supports 'overrides' in package.json - command: sudo npm install npm --global + command: npm install npm --global install-dependencies: parameters: @@ -155,7 +155,7 @@ jobs: node-base: &node-base working_directory: ~/solc-js docker: - - image: circleci/node + - image: cimg/node:current parameters: run_coveralls: type: boolean @@ -184,9 +184,8 @@ jobs: hardhat-core-default-solc: docker: - - image: circleci/node:16 + - image: cimg/node:16.15 steps: - - update-npm - show-npm-version - provision-and-package-solcjs - provision-hardhat-with-packaged-solcjs @@ -212,9 +211,8 @@ jobs: hardhat-core-latest-solc: docker: - - image: circleci/node:16 + - image: cimg/node:16.15 steps: - - update-npm - show-npm-version - provision-and-package-solcjs - provision-hardhat-with-packaged-solcjs @@ -230,9 +228,8 @@ jobs: hardhat-sample-project: docker: - - image: circleci/node:16 + - image: cimg/node:16.15 steps: - - update-npm - show-npm-version - provision-and-package-solcjs - run: git clone --depth 1 "https://github.com/nomiclabs/hardhat-hackathon-boilerplate" boilerplate/ @@ -284,11 +281,13 @@ jobs: truffle-sample-project: docker: - - image: circleci/node:16 + - image: cimg/node:16.15 steps: - update-npm - show-npm-version - provision-and-package-solcjs + - run: sudo apt update + - run: sudo apt install python3 python-is-python3 --assume-yes --no-install-recommends - provision-truffle-with-packaged-solcjs - run: name: Unbox MetaCoin @@ -315,9 +314,8 @@ jobs: cli-smoke-test: docker: - - image: circleci/node:17 + - image: cimg/node:17.9 steps: - - update-npm - show-npm-version - provision-and-package-solcjs - run: @@ -356,20 +354,20 @@ jobs: node-v10: <<: *node-base docker: - - image: circleci/node:10 + - image: cimg/node:10.24 node-v12: <<: *node-base docker: - - image: circleci/node:12 + - image: cimg/node:12.22 node-v14: <<: *node-base docker: - - image: circleci/node:14 + - image: cimg/node:14.19 node-v16: <<: *node-base docker: - - image: circleci/node:16 + - image: cimg/node:16.15 node-v17: <<: *node-base docker: - - image: circleci/node:17 + - image: cimg/node:17.9 From f0ef2f50cf3d46975da40a8f3e1e2449d68b394c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Jun 2022 20:37:01 +0200 Subject: [PATCH 147/153] CI: Switch from node.js 17 to 18 (LTS) --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fc7feada..fad0c1e3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ workflows: - node-v14 - node-v16: run_coveralls: true - - node-v17 + - node-v18 - hardhat-core-default-solc - hardhat-core-latest-solc - hardhat-sample-project @@ -314,7 +314,7 @@ jobs: cli-smoke-test: docker: - - image: cimg/node:17.9 + - image: cimg/node:current steps: - show-npm-version - provision-and-package-solcjs @@ -367,7 +367,7 @@ jobs: <<: *node-base docker: - image: cimg/node:16.15 - node-v17: + node-v18: <<: *node-base docker: - - image: cimg/node:17.9 + - image: cimg/node:18.3 From ffa4ef445ad5258e20284169a6c4ba7b75e3b246 Mon Sep 17 00:00:00 2001 From: Derrick Persson Date: Thu, 7 Jul 2022 23:24:50 -0700 Subject: [PATCH 148/153] Adding command to copy meta files into dist dir --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f67becc..21c34274 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "scripts": { "build": "tsc", - "postbuild": "node build/postbuild.js", + "postbuild": "node build/postbuild.js && cp README.md LICENSE dist/", "lint": "eslint --ext .js,.ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "updateBinary": "node build/clean.js && ts-node ./downloadCurrentVersion.ts && ts-node ./verifyVersion.ts", From f27ef6e5477ef2ccb15009b16cc23a90d223a115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 8 Aug 2022 18:31:51 +0200 Subject: [PATCH 149/153] Set version to 0.8.16 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21c34274..45c203d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.15", + "version": "0.8.16", "description": "Solidity compiler", "main": "index.js", "bin": { From 888c978aaf46598000714423c30c3d7c72ae9a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 8 Sep 2022 20:21:21 +0200 Subject: [PATCH 150/153] CI: Switch to medium+ resource class for hardhat-core-default-solc --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index fad0c1e3..7acfa445 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -183,6 +183,8 @@ jobs: command: npm run coveralls hardhat-core-default-solc: + # Runs out of memory on 'medium'. + resource_class: medium+ docker: - image: cimg/node:16.15 steps: From bb4987aa995a5d5ee3a329c26bc54877fc73bffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 8 Sep 2022 16:53:40 +0200 Subject: [PATCH 151/153] Set version to 0.8.17 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 45c203d7..cedaf277 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solc", - "version": "0.8.16", + "version": "0.8.17", "description": "Solidity compiler", "main": "index.js", "bin": { From 864af418384e81dd76bdcf85715a047b8d21c19d Mon Sep 17 00:00:00 2001 From: Stephen Lineker-Miller Date: Sat, 2 Apr 2022 21:13:23 +0100 Subject: [PATCH 152/153] Refactor wrapper.ts to improve maintainability by splitting and documenting --- bindings/compile.ts | 232 +++++++++++++++++++++++ bindings/core.ts | 161 ++++++++++++++++ bindings/helpers.ts | 36 ++++ bindings/index.ts | 15 ++ formatters.ts | 13 ++ package.json | 15 +- wrapper.ts | 447 +++++++++++++------------------------------- 7 files changed, 589 insertions(+), 330 deletions(-) create mode 100644 bindings/compile.ts create mode 100644 bindings/core.ts create mode 100644 bindings/helpers.ts create mode 100644 bindings/index.ts create mode 100644 formatters.ts diff --git a/bindings/compile.ts b/bindings/compile.ts new file mode 100644 index 00000000..5f3a2d54 --- /dev/null +++ b/bindings/compile.ts @@ -0,0 +1,232 @@ +import assert from 'assert'; + +import { isNil } from '../common/helpers'; +import { bindSolcMethod } from './helpers'; + +export function setupCompile (solJson, core) { + return { + compileJson: bindCompileJson(solJson), + compileJsonCallback: bindCompileJsonCallback(solJson, core), + compileJsonMulti: bindCompileJsonMulti(solJson), + compileStandard: bindCompileStandard(solJson, core) + }; +} + +/********************** + * COMPILE + **********************/ + +/** + * Returns a binding to the solidity compileJSON method. + * input (text), optimize (bool) -> output (jsontext) + * + * @param solJson The Emscripten compiled Solidity object. + */ +function bindCompileJson (solJson) { + return bindSolcMethod( + solJson, + 'compileJSON', + 'string', + ['string', 'number'], + null + ); +} + +/** + * Returns a binding to the solidity compileJSONMulti method. + * input (jsontext), optimize (bool) -> output (jsontext) + * + * @param solJson The Emscripten compiled Solidity object. + */ +function bindCompileJsonMulti (solJson) { + return bindSolcMethod( + solJson, + 'compileJSONMulti', + 'string', + ['string', 'number'], + null + ); +} + +/** + * Returns a binding to the solidity compileJSONCallback method. + * input (jsontext), optimize (bool), callback (ptr) -> output (jsontext) + * + * @param solJson The Emscripten compiled Solidity object. + * @param coreBindings The core bound Solidity methods. + */ +function bindCompileJsonCallback (solJson, coreBindings) { + const compileInternal = bindSolcMethod( + solJson, + 'compileJSONCallback', + 'string', + ['string', 'number', 'number'], + null + ); + + if (isNil(compileInternal)) return null; + + return function (input, optimize, readCallback) { + return runWithCallbacks(solJson, coreBindings, readCallback, compileInternal, [input, optimize]); + }; +} + +/** + * Returns a binding to the solidity solidity_compile method with a fallback to + * compileStandard. + * input (jsontext), callback (optional >= v6 only - ptr) -> output (jsontext) + * + * @param solJson The Emscripten compiled Solidity object. + * @param coreBindings The core bound Solidity methods. + */ +function bindCompileStandard (solJson, coreBindings) { + let boundFunctionStandard: any = null; + let boundFunctionSolidity: any = null; + + // input (jsontext), callback (ptr) -> output (jsontext) + const compileInternal = bindSolcMethod( + solJson, + 'compileStandard', + 'string', + ['string', 'number'], + null + ); + + if (coreBindings.isVersion6OrNewer) { + // input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext) + boundFunctionSolidity = bindSolcMethod( + solJson, + 'solidity_compile', + 'string', + ['string', 'number', 'number'], + null + ); + } else { + // input (jsontext), callback (ptr) -> output (jsontext) + boundFunctionSolidity = bindSolcMethod( + solJson, + 'solidity_compile', + 'string', + ['string', 'number'], + null + ); + } + + if (!isNil(compileInternal)) { + boundFunctionStandard = function (input, readCallback) { + return runWithCallbacks(solJson, coreBindings, readCallback, compileInternal, [input]); + }; + } + + if (!isNil(boundFunctionSolidity)) { + boundFunctionStandard = function (input, callbacks) { + return runWithCallbacks(solJson, coreBindings, callbacks, boundFunctionSolidity, [input]); + }; + } + + return boundFunctionStandard; +} + +/********************** + * CALL BACKS + **********************/ + +function wrapCallback (coreBindings, callback) { + assert(typeof callback === 'function', 'Invalid callback specified.'); + + return function (data, contents, error) { + const result = callback(coreBindings.copyFromCString(data)); + if (typeof result.contents === 'string') { + coreBindings.copyToCString(result.contents, contents); + } + if (typeof result.error === 'string') { + coreBindings.copyToCString(result.error, error); + } + }; +} + +function wrapCallbackWithKind (coreBindings, callback) { + assert(typeof callback === 'function', 'Invalid callback specified.'); + + return function (context, kind, data, contents, error) { + // Must be a null pointer. + assert(context === 0, 'Callback context must be null.'); + const result = callback(coreBindings.copyFromCString(kind), coreBindings.copyFromCString(data)); + if (typeof result.contents === 'string') { + coreBindings.copyToCString(result.contents, contents); + } + if (typeof result.error === 'string') { + coreBindings.copyToCString(result.error, error); + } + }; +} + +// calls compile() with args || cb +function runWithCallbacks (solJson, coreBindings, callbacks, compile, args) { + if (callbacks) { + assert(typeof callbacks === 'object', 'Invalid callback object specified.'); + } else { + callbacks = {}; + } + + let readCallback = callbacks.import; + if (readCallback === undefined) { + readCallback = function (data) { + return { + error: 'File import callback not supported' + }; + }; + } + + let singleCallback; + if (coreBindings.isVersion6OrNewer) { + // After 0.6.x multiple kind of callbacks are supported. + let smtSolverCallback = callbacks.smtSolver; + if (smtSolverCallback === undefined) { + smtSolverCallback = function (data) { + return { + error: 'SMT solver callback not supported' + }; + }; + } + + singleCallback = function (kind, data) { + if (kind === 'source') { + return readCallback(data); + } else if (kind === 'smt-query') { + return smtSolverCallback(data); + } else { + assert(false, 'Invalid callback kind specified.'); + } + }; + + singleCallback = wrapCallbackWithKind(coreBindings, singleCallback); + } else { + // Old Solidity version only supported imports. + singleCallback = wrapCallback(coreBindings, readCallback); + } + + const cb = coreBindings.addFunction(singleCallback, 'viiiii'); + let output; + try { + args.push(cb); + if (coreBindings.isVersion6OrNewer) { + // Callback context. + args.push(null); + } + + output = compile(...args); + } finally { + coreBindings.removeFunction(cb); + } + + if (coreBindings.reset) { + // Explicitly free memory. + // + // NOTE: cwrap() of "compile" will copy the returned pointer into a + // Javascript string and it is not possible to call free() on it. + // reset() however will clear up all allocations. + coreBindings.reset(); + } + return output; +} diff --git a/bindings/core.ts b/bindings/core.ts new file mode 100644 index 00000000..674fb20e --- /dev/null +++ b/bindings/core.ts @@ -0,0 +1,161 @@ +import { bindSolcMethod, bindSolcMethodWithFallbackFunc } from './helpers'; +import translate from '../translate'; +import * as semver from 'semver'; +import { isNil } from '../common/helpers'; + +export function setupCore (solJson) { + const core = { + alloc: bindAlloc(solJson), + license: bindLicense(solJson), + version: bindVersion(solJson), + reset: bindReset(solJson) + }; + + const helpers = { + addFunction: unboundAddFunction.bind(this, solJson), + removeFunction: unboundRemoveFunction.bind(this, solJson), + + copyFromCString: unboundCopyFromCString.bind(this, solJson), + copyToCString: unboundCopyToCString.bind(this, solJson, core.alloc), + + // @ts-ignore + versionToSemver: versionToSemver(core.version()) + }; + + return { + ...core, + ...helpers, + + isVersion6OrNewer: semver.gt(helpers.versionToSemver(), '0.5.99') + }; +} + +/********************** + * Core Functions + **********************/ + +/** + * Returns a binding to the solidity_alloc function. + * + * @param solJson The Emscripten compiled Solidity object. + */ +function bindAlloc (solJson) { + const allocBinding = bindSolcMethod( + solJson, + 'solidity_alloc', + 'number', + ['number'], + null + ); + + // the fallback malloc is not a cwrap function and should just be returned + // directly in-case the alloc binding could not happen. + if (isNil(allocBinding)) { + return solJson._malloc; + } + + return allocBinding; +} + +/** + * Returns a binding to the solidity_version method. + * + * @param solJson The Emscripten compiled Solidity object. + */ +function bindVersion (solJson) { + return bindSolcMethodWithFallbackFunc( + solJson, + 'solidity_version', + 'string', + [], + 'version' + ); +} + +function versionToSemver (version) { + return translate.versionToSemver.bind(this, version); +} + +/** + * Returns a binding to the solidity_license method. + * + * If the current solJson version < 0.4.14 then this will bind an empty function. + * + * @param solJson The Emscripten compiled Solidity object. + */ +function bindLicense (solJson) { + return bindSolcMethodWithFallbackFunc( + solJson, + 'solidity_license', + 'string', + [], + 'license', + () => { + } + ); +} + +/** + * Returns a binding to the solidity_reset method. + * + * @param solJson The Emscripten compiled Solidity object. + */ +function bindReset (solJson) { + return bindSolcMethod( + solJson, + 'solidity_reset', + null, + [], + null + ); +} + +/********************** + * Helpers Functions + **********************/ + +/** + * Copy to a C string. + * + * Allocates memory using solc's allocator. + * + * Before 0.6.0: + * Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. + * See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 + * + * After 0.6.0: + * The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end. + * + * @param solJson The Emscripten compiled Solidity object. + * @param alloc The memory allocation function. + * @param str The source string being copied to a C string. + * @param ptr The pointer location where the C string will be set. + */ +function unboundCopyToCString (solJson, alloc, str, ptr) { + const length = solJson.lengthBytesUTF8(str); + + const buffer = alloc(length + 1); + + solJson.stringToUTF8(str, buffer, length + 1); + solJson.setValue(ptr, buffer, '*'); +} + +/** + * Wrapper over Emscripten's C String copying function (which can be different + * on different versions). + * + * @param solJson The Emscripten compiled Solidity object. + * @param ptr The pointer location where the C string will be referenced. + */ +function unboundCopyFromCString (solJson, ptr) { + const copyFromCString = solJson.UTF8ToString || solJson.Pointer_stringify; + return copyFromCString(ptr); +} + +function unboundAddFunction (solJson, func, signature?) { + return (solJson.addFunction || solJson.Runtime.addFunction)(func, signature); +} + +function unboundRemoveFunction (solJson, ptr) { + return (solJson.removeFunction || solJson.Runtime.removeFunction)(ptr); +} diff --git a/bindings/helpers.ts b/bindings/helpers.ts new file mode 100644 index 00000000..b3e6ade9 --- /dev/null +++ b/bindings/helpers.ts @@ -0,0 +1,36 @@ +import { isNil } from '../common/helpers'; + +export function bindSolcMethod (solJson, method, returnType, args, defaultValue) { + if (isNil(solJson[`_${method}`]) && defaultValue !== undefined) { + return defaultValue; + } + + return solJson.cwrap(method, returnType, args); +} + +export function bindSolcMethodWithFallbackFunc (solJson, method, returnType, args, fallbackMethod, finalFallback = undefined) { + const methodFunc = bindSolcMethod(solJson, method, returnType, args, null); + + if (!isNil(methodFunc)) { + return methodFunc; + } + + return bindSolcMethod(solJson, fallbackMethod, returnType, args, finalFallback); +} + +export function getSupportedMethods (solJson) { + return { + licenseSupported: anyMethodExists(solJson, 'solidity_license'), + versionSupported: anyMethodExists(solJson, 'solidity_version'), + allocSupported: anyMethodExists(solJson, 'solidity_alloc'), + resetSupported: anyMethodExists(solJson, 'solidity_reset'), + compileJsonSupported: anyMethodExists(solJson, 'compileJSON'), + compileJsonMultiSupported: anyMethodExists(solJson, 'compileJSONMulti'), + compileJsonCallbackSuppported: anyMethodExists(solJson, 'compileJSONCallback'), + compileJsonStandardSupported: anyMethodExists(solJson, 'compileStandard', 'solidity_compile') + }; +} + +function anyMethodExists (solJson, ...names) { + return names.some(name => !isNil(solJson[`_${name}`])); +} diff --git a/bindings/index.ts b/bindings/index.ts new file mode 100644 index 00000000..63d4c074 --- /dev/null +++ b/bindings/index.ts @@ -0,0 +1,15 @@ +import { setupCore } from './core'; +import { getSupportedMethods } from './helpers'; +import { setupCompile } from './compile'; + +export default function setupBindings (solJson) { + const coreBindings = setupCore(solJson); + const compileBindings = setupCompile(solJson, coreBindings); + const methodFlags = getSupportedMethods(solJson); + + return { + methodFlags, + coreBindings, + compileBindings + }; +} diff --git a/formatters.ts b/formatters.ts new file mode 100644 index 00000000..4ad82028 --- /dev/null +++ b/formatters.ts @@ -0,0 +1,13 @@ +export function formatFatalError (message) { + return JSON.stringify({ + errors: [ + { + type: 'JSONError', + component: 'solcjs', + severity: 'error', + message: message, + formattedMessage: 'Error: ' + message + } + ] + }); +} diff --git a/package.json b/package.json index cedaf277..8a85ef3f 100644 --- a/package.json +++ b/package.json @@ -35,16 +35,9 @@ "node": ">=10.0.0" }, "files": [ - "abi.js", - "index.js", - "linker.js", - "smtchecker.js", - "smtsolver.js", - "solc.js", - "soljson.js", - "translate.js", - "wrapper.js", - "common/helpers.js" + "common/*.js", + "bindings/*.js", + "*.js" ], "author": "chriseth", "license": "MIT", @@ -63,9 +56,9 @@ }, "devDependencies": { "@types/node": "^16.11.7", - "@types/tmp": "^0.2.3", "@types/semver": "^7.3.9", "@types/tape": "^4.13.2", + "@types/tmp": "^0.2.3", "@typescript-eslint/eslint-plugin": "^5.8.0", "@typescript-eslint/parser": "^5.8.0", "coveralls": "^3.0.0", diff --git a/wrapper.ts b/wrapper.ts index 36c18d74..e9bf471e 100755 --- a/wrapper.ts +++ b/wrapper.ts @@ -1,360 +1,169 @@ -import translate from './translate'; -import { https } from 'follow-redirects'; import MemoryStream from 'memorystream'; -import assert from 'assert'; -import * as semver from 'semver'; - -const Module = module.constructor as any; - -function setupMethods (soljson) { - let version; - if ('_solidity_version' in soljson) { - version = soljson.cwrap('solidity_version', 'string', []); - } else { - version = soljson.cwrap('version', 'string', []); - } - - const versionToSemver = function () { - return translate.versionToSemver(version()); - }; - - const isVersion6 = semver.gt(versionToSemver(), '0.5.99'); - - let license; - if ('_solidity_license' in soljson) { - license = soljson.cwrap('solidity_license', 'string', []); - } else if ('_license' in soljson) { - license = soljson.cwrap('license', 'string', []); - } else { - // pre 0.4.14 - license = function () { - // return undefined - }; - } - - let alloc; - if ('_solidity_alloc' in soljson) { - alloc = soljson.cwrap('solidity_alloc', 'number', ['number']); - } else { - alloc = soljson._malloc; - assert(alloc, 'Expected malloc to be present.'); - } +import { https } from 'follow-redirects'; - let reset; - if ('_solidity_reset' in soljson) { - reset = soljson.cwrap('solidity_reset', null, []); - } +import { formatFatalError } from './formatters'; +import { isNil } from './common/helpers'; +import setupBindings from './bindings'; +import translate from './translate'; - const copyToCString = function (str, ptr) { - const length = soljson.lengthBytesUTF8(str); - // This is allocating memory using solc's allocator. - // - // Before 0.6.0: - // Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. - // See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 - // - // After 0.6.0: - // The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end. - const buffer = alloc(length + 1); - soljson.stringToUTF8(str, buffer, length + 1); - soljson.setValue(ptr, buffer, '*'); - }; +const Module = module.constructor as any; - // This is to support multiple versions of Emscripten. - // Take a single `ptr` and returns a `str`. - const copyFromCString = soljson.UTF8ToString || soljson.Pointer_stringify; +function wrapper (soljson) { + const { + coreBindings, + compileBindings, + methodFlags + } = setupBindings(soljson); - const wrapCallback = function (callback) { - assert(typeof callback === 'function', 'Invalid callback specified.'); - return function (data, contents, error) { - const result = callback(copyFromCString(data)); - if (typeof result.contents === 'string') { - copyToCString(result.contents, contents); - } - if (typeof result.error === 'string') { - copyToCString(result.error, error); - } - }; + return { + version: coreBindings.version, + semver: coreBindings.versionToSemver, + license: coreBindings.license, + lowlevel: { + compileSingle: compileBindings.compileJson, + compileMulti: compileBindings.compileJsonMulti, + compileCallback: compileBindings.compileJsonCallback, + compileStandard: compileBindings.compileStandard + }, + features: { + legacySingleInput: methodFlags.compileJsonStandardSupported, + multipleInputs: methodFlags.compileJsonMultiSupported || methodFlags.compileJsonStandardSupported, + importCallback: methodFlags.compileJsonCallbackSuppported || methodFlags.compileJsonStandardSupported, + nativeStandardJSON: methodFlags.compileJsonStandardSupported + }, + compile: compileStandardWrapper.bind(this, compileBindings), + // Loads the compiler of the given version from the github repository + // instead of from the local filesystem. + loadRemoteVersion, + // Use this if you want to add wrapper functions around the pure module. + setupMethods: wrapper }; +} - const wrapCallbackWithKind = function (callback) { - assert(typeof callback === 'function', 'Invalid callback specified.'); - return function (context, kind, data, contents, error) { - // Must be a null pointer. - assert(context === 0, 'Callback context must be null.'); - const result = callback(copyFromCString(kind), copyFromCString(data)); - if (typeof result.contents === 'string') { - copyToCString(result.contents, contents); - } - if (typeof result.error === 'string') { - copyToCString(result.error, error); - } - }; - }; +function loadRemoteVersion (versionString, callback) { + const memoryStream = new MemoryStream(null, { readable: false }); + const url = `https://binaries.soliditylang.org/bin/soljson-${versionString}.js`; - // This calls compile() with args || cb - const runWithCallbacks = function (callbacks, compile, args) { - if (callbacks) { - assert(typeof callbacks === 'object', 'Invalid callback object specified.'); + https.get(url, response => { + if (response.statusCode !== 200) { + callback(new Error(`Error retrieving binary: ${response.statusMessage}`)); } else { - callbacks = {}; - } - - let readCallback = callbacks.import; - if (readCallback === undefined) { - readCallback = function (data) { - return { - error: 'File import callback not supported' - }; - }; - } - - let singleCallback; - if (isVersion6) { - // After 0.6.x multiple kind of callbacks are supported. - let smtSolverCallback = callbacks.smtSolver; - if (smtSolverCallback === undefined) { - smtSolverCallback = function (data) { - return { - error: 'SMT solver callback not supported' - }; - }; - } - - singleCallback = function (kind, data) { - if (kind === 'source') { - return readCallback(data); - } else if (kind === 'smt-query') { - return smtSolverCallback(data); - } else { - assert(false, 'Invalid callback kind specified.'); + response.pipe(memoryStream); + response.on('end', () => { + // Based on the require-from-string package. + const soljson = new Module(); + soljson._compile(memoryStream.toString(), `soljson-${versionString}.js`); + + if (module.parent && module.parent.children) { + // Make sure the module is plugged into the hierarchy correctly to have parent + // properly garbage collected. + module.parent.children.splice(module.parent.children.indexOf(soljson), 1); } - }; - - singleCallback = wrapCallbackWithKind(singleCallback); - } else { - // Old Solidity version only supported imports. - singleCallback = wrapCallback(readCallback); - } - - // This is to support multiple versions of Emscripten. - const addFunction = soljson.addFunction || soljson.Runtime.addFunction; - const removeFunction = soljson.removeFunction || soljson.Runtime.removeFunction; - const cb = addFunction(singleCallback, 'viiiii'); - let output; - try { - args.push(cb); - if (isVersion6) { - // Callback context. - args.push(null); - } - output = compile.apply(undefined, args); - } catch (e) { - removeFunction(cb); - throw e; - } - removeFunction(cb); - if (reset) { - // Explicitly free memory. - // - // NOTE: cwrap() of "compile" will copy the returned pointer into a - // Javascript string and it is not possible to call free() on it. - // reset() however will clear up all allocations. - reset(); + callback(null, wrapper(soljson.exports)); + }); } - return output; - }; + }).on('error', function (error) { + callback(error); + }); +} - let compileJSON = null; - if ('_compileJSON' in soljson) { - // input (text), optimize (bool) -> output (jsontext) - compileJSON = soljson.cwrap('compileJSON', 'string', ['string', 'number']); +// Expects a Standard JSON I/O but supports old compilers +function compileStandardWrapper (compile, inputRaw, readCallback) { + if (!isNil(compile.compileStandard)) { + return compile.compileStandard(inputRaw, readCallback); } - let compileJSONMulti = null; - if ('_compileJSONMulti' in soljson) { - // input (jsontext), optimize (bool) -> output (jsontext) - compileJSONMulti = soljson.cwrap('compileJSONMulti', 'string', ['string', 'number']); - } + let input: { language: string, sources: any[], settings: any }; - let compileJSONCallback = null; - if ('_compileJSONCallback' in soljson) { - // input (jsontext), optimize (bool), callback (ptr) -> output (jsontext) - const compileInternal = soljson.cwrap('compileJSONCallback', 'string', ['string', 'number', 'number']); - compileJSONCallback = function (input, optimize, readCallback) { - return runWithCallbacks(readCallback, compileInternal, [input, optimize]); - }; + try { + input = JSON.parse(inputRaw); + } catch (e) { + return formatFatalError(`Invalid JSON supplied: ${e.message}`); } - let compileStandard = null; - if ('_compileStandard' in soljson) { - // input (jsontext), callback (ptr) -> output (jsontext) - const compileStandardInternal = soljson.cwrap('compileStandard', 'string', ['string', 'number']); - compileStandard = function (input, readCallback) { - return runWithCallbacks(readCallback, compileStandardInternal, [input]); - }; + if (input.language !== 'Solidity') { + return formatFatalError('Only "Solidity" is supported as a language.'); } - if ('_solidity_compile' in soljson) { - let solidityCompile; - if (isVersion6) { - // input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext) - solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number', 'number']); - } else { - // input (jsontext), callback (ptr) -> output (jsontext) - solidityCompile = soljson.cwrap('solidity_compile', 'string', ['string', 'number']); - } - compileStandard = function (input, callbacks) { - return runWithCallbacks(callbacks, solidityCompile, [input]); - }; + + // NOTE: this is deliberately `== null` + if (isNil(input.sources) || input.sources.length === 0) { + return formatFatalError('No input sources specified.'); } - // Expects a Standard JSON I/O but supports old compilers - const compileStandardWrapper = function (input, readCallback?: any) { - if (compileStandard !== null) { - return compileStandard(input, readCallback); - } + const sources = translateSources(input); + const optimize = isOptimizerEnabled(input); + const libraries = librariesSupplied(input); - function formatFatalError (message) { - return JSON.stringify({ - errors: [ - { - type: 'JSONError', - component: 'solcjs', - severity: 'error', - message: message, - formattedMessage: 'Error: ' + message - } - ] - }); - } + if (isNil(sources) || Object.keys(sources).length === 0) { + return formatFatalError('Failed to process sources.'); + } - try { - input = JSON.parse(input); - } catch (e) { - return formatFatalError('Invalid JSON supplied: ' + e.message); - } + // Try to wrap around old versions + if (!isNil(compile.compileJsonCallback)) { + const inputJson = JSON.stringify({ sources: sources }); + const output = compile.compileJsonCallback(inputJson, optimize, readCallback); + return translateOutput(output, libraries); + } - if (input.language !== 'Solidity') { - return formatFatalError('Only "Solidity" is supported as a language.'); - } + if (!isNil(compile.compileJsonMulti)) { + const output = compile.compileJsonMulti(JSON.stringify({ sources: sources }), optimize); + return translateOutput(output, libraries); + } - // NOTE: this is deliberately `== null` - if (input.sources == null || input.sources.length === 0) { - return formatFatalError('No input sources specified.'); + // Try our luck with an ancient compiler + if (!isNil(compile.compileJson)) { + if (Object.keys(sources).length > 1) { + return formatFatalError('Multiple sources provided, but compiler only supports single input.'); } - function isOptimizerEnabled (input) { - return input.settings && input.settings.optimizer && input.settings.optimizer.enabled; - } + const input = sources[Object.keys(sources)[0]]; + const output = compile.compileJson(input, optimize); + return translateOutput(output, libraries); + } - function translateSources (input) { - const sources = {}; - for (const source in input.sources) { - if (input.sources[source].content !== null) { - sources[source] = input.sources[source].content; - } else { - // force failure - return null; - } - } - return sources; - } + return formatFatalError('Compiler does not support any known interface.'); +} - function librariesSupplied (input) { - if (input.settings) { - return input.settings.libraries; - } - } +function isOptimizerEnabled (input) { + return input.settings && input.settings.optimizer && input.settings.optimizer.enabled; +} - function translateOutput (output, libraries) { - try { - output = JSON.parse(output); - } catch (e) { - return formatFatalError('Compiler returned invalid JSON: ' + e.message); - } - output = translate.translateJsonCompilerOutput(output, libraries); - if (output == null) { - return formatFatalError('Failed to process output.'); - } - return JSON.stringify(output); - } +function translateSources (input) { + const sources = {}; - const sources = translateSources(input); - if (sources === null || Object.keys(sources).length === 0) { - return formatFatalError('Failed to process sources.'); + for (const source in input.sources) { + if (input.sources[source].content !== null) { + sources[source] = input.sources[source].content; + } else { + // force failure + return null; } + } - // Try linking if libraries were supplied - const libraries = librariesSupplied(input); + return sources; +} - // Try to wrap around old versions - if (compileJSONCallback !== null) { - return translateOutput(compileJSONCallback(JSON.stringify({ sources: sources }), isOptimizerEnabled(input), readCallback), libraries); - } +function librariesSupplied (input) { + if (!isNil(input.settings)) return input.settings.libraries; +} - if (compileJSONMulti !== null) { - return translateOutput(compileJSONMulti(JSON.stringify({ sources: sources }), isOptimizerEnabled(input)), libraries); - } +function translateOutput (outputRaw, libraries) { + let parsedOutput; - // Try our luck with an ancient compiler - if (compileJSON !== null) { - if (Object.keys(sources).length !== 1) { - return formatFatalError('Multiple sources provided, but compiler only supports single input.'); - } - return translateOutput(compileJSON(sources[Object.keys(sources)[0]], isOptimizerEnabled(input)), libraries); - } + try { + parsedOutput = JSON.parse(outputRaw); + } catch (e) { + return formatFatalError(`Compiler returned invalid JSON: ${e.message}`); + } - return formatFatalError('Compiler does not support any known interface.'); - }; + const output = translate.translateJsonCompilerOutput(parsedOutput, libraries); - return { - version: version, - semver: versionToSemver, - license: license, - lowlevel: { - compileSingle: compileJSON, - compileMulti: compileJSONMulti, - compileCallback: compileJSONCallback, - compileStandard: compileStandard - }, - features: { - legacySingleInput: compileJSON !== null, - multipleInputs: compileJSONMulti !== null || compileStandard !== null, - importCallback: compileJSONCallback !== null || compileStandard !== null, - nativeStandardJSON: compileStandard !== null - }, - compile: compileStandardWrapper, - // Loads the compiler of the given version from the github repository - // instead of from the local filesystem. - loadRemoteVersion: function (versionString, cb) { - const mem = new MemoryStream(null, { readable: false }); - const url = 'https://binaries.soliditylang.org/bin/soljson-' + versionString + '.js'; - https.get(url, function (response) { - if (response.statusCode !== 200) { - cb(new Error('Error retrieving binary: ' + response.statusMessage)); - } else { - response.pipe(mem); - response.on('end', function () { - // Based on the require-from-string package. - const soljson = new Module(); - soljson._compile(mem.toString(), 'soljson-' + versionString + '.js'); - if (module.parent && module.parent.children) { - // Make sure the module is plugged into the hierarchy correctly to have parent - // properly garbage collected. - module.parent.children.splice(module.parent.children.indexOf(soljson), 1); - } + if (isNil(output)) { + return formatFatalError('Failed to process output.'); + } - cb(null, setupMethods(soljson.exports)); - }); - } - }).on('error', function (error) { - cb(error); - }); - }, - // Use this if you want to add wrapper functions around the pure module. - setupMethods: setupMethods - }; + return JSON.stringify(output); } -export = setupMethods; +export = wrapper; From 86749f5f4347e7dabb4113e6bfc7564809d7bca9 Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Sat, 24 Sep 2022 17:46:04 +0200 Subject: [PATCH 153/153] Add CI job to check coding style --- .circleci/config.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7acfa445..a5265445 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,6 +2,7 @@ workflows: version: 2.1 node-multi-build: jobs: + - check-coding-style - node-v10 - node-v12 - node-v14 @@ -17,6 +18,9 @@ workflows: version: 2.1 +orbs: + shellcheck: circleci/shellcheck@volatile + commands: show-npm-version: steps: @@ -182,6 +186,24 @@ jobs: name: coveralls command: npm run coveralls + check-coding-style: + docker: + - image: cimg/node:current + steps: + - show-npm-version + - checkout + - shellcheck/install + - install-dependencies: + cache-id: solc-js + - run: + name: Check for javascript/typescript coding style + command: npm run lint + - shellcheck/check: + ignore-dirs: | + ./.git + ./node_modules + ./dist + hardhat-core-default-solc: # Runs out of memory on 'medium'. resource_class: medium+