Skip to content

Commit 45d0f31

Browse files
authored
Merge pull request #431 from ethereum/master_060
Merge 'master_060' into 'master'
2 parents 4c89410 + 2297b30 commit 45d0f31

File tree

8 files changed

+305
-92
lines changed

8 files changed

+305
-92
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ of them are resolved.
5050

5151
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.
5252

53-
_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.
54-
5553
#### Example usage without the import callback
5654

5755
Example:

smtchecker.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ function handleSMTQueries (inputJSON, outputJSON, solver) {
2525
return inputJSON;
2626
}
2727

28+
function smtCallback (solver) {
29+
return function (query) {
30+
try {
31+
var result = solver(query);
32+
return { contents: result };
33+
} catch (err) {
34+
return { error: err };
35+
}
36+
};
37+
}
38+
2839
module.exports = {
29-
handleSMTQueries: handleSMTQueries
40+
handleSMTQueries: handleSMTQueries,
41+
smtCallback: smtCallback
3042
};

smtsolver.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function solve (query) {
4646
!solverOutput.startsWith('unsat') &&
4747
!solverOutput.startsWith('unknown')
4848
) {
49-
throw new Error('Failed solve SMT query. ' + e.toString());
49+
throw new Error('Failed to solve SMT query. ' + e.toString());
5050
}
5151
}
5252
// Trigger early manual cleanup
@@ -55,5 +55,6 @@ function solve (query) {
5555
}
5656

5757
module.exports = {
58-
smtSolver: solve
58+
smtSolver: solve,
59+
availableSolvers: solvers.length
5960
};

test/compiler.js

Lines changed: 13 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ function runTests (solc, versionText) {
193193
return { error: 'File not found' };
194194
}
195195
}
196-
var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports));
196+
var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports }));
197197
var B = getBytecode(output, 'b.sol', 'B');
198198
st.ok(typeof B === 'string');
199199
st.ok(B.length > 0);
@@ -217,7 +217,7 @@ function runTests (solc, versionText) {
217217
function findImports (path) {
218218
return { error: 'File not found' };
219219
}
220-
var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports));
220+
var output = JSON.parse(solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports }));
221221
st.plan(3);
222222
st.ok('errors' in output);
223223
// Check if the ParserError exists, but allow others too
@@ -248,7 +248,7 @@ function runTests (solc, versionText) {
248248
throw new Error('Could not implement this interface properly...');
249249
}
250250
st.throws(function () {
251-
solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, findImports);
251+
solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, { import: findImports });
252252
}, /^Error: Could not implement this interface properly.../);
253253
st.end();
254254
});
@@ -266,7 +266,7 @@ function runTests (solc, versionText) {
266266
};
267267
st.throws(function () {
268268
solc.lowlevel.compileCallback(JSON.stringify({sources: input}), 0, "this isn't a callback");
269-
}, /Invalid callback specified./);
269+
}, /Invalid callback object specified./);
270270
st.end();
271271
});
272272

@@ -418,7 +418,7 @@ function runTests (solc, versionText) {
418418
}
419419
}
420420

421-
var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), findImports));
421+
var output = JSON.parse(solc.lowlevel.compileStandard(JSON.stringify(input), { import: findImports }));
422422
st.ok(bytecodeExists(output, 'a.sol', 'A'));
423423
st.ok(bytecodeExists(output, 'b.sol', 'B'));
424424
st.end();
@@ -513,6 +513,12 @@ function runTests (solc, versionText) {
513513
}
514514

515515
var isVersion6 = semver.gt(solc.semver(), '0.5.99');
516+
var source;
517+
if (isVersion6) {
518+
source = 'abstract contract C { function f() public virtual; }';
519+
} else {
520+
source = 'contract C { function f() public; }';
521+
}
516522

517523
var input = {
518524
'language': 'Solidity',
@@ -525,7 +531,7 @@ function runTests (solc, versionText) {
525531
},
526532
'sources': {
527533
'c.sol': {
528-
'content': (isVersion6 ? 'abstract ' : '') + 'contract C { function f() public; }'
534+
'content': source
529535
}
530536
}
531537
};
@@ -570,50 +576,6 @@ function runTests (solc, versionText) {
570576
}
571577
}
572578

573-
var output = JSON.parse(solc.compile(JSON.stringify(input), findImports));
574-
st.ok(expectNoError(output));
575-
var A = getBytecodeStandard(output, 'a.sol', 'A');
576-
st.ok(typeof A === 'string');
577-
st.ok(A.length > 0);
578-
var B = getBytecodeStandard(output, 'b.sol', 'B');
579-
st.ok(typeof B === 'string');
580-
st.ok(B.length > 0);
581-
st.ok(Object.keys(linker.findLinkReferences(B)).length === 0);
582-
st.end();
583-
});
584-
585-
t.test('compiling standard JSON (with imports + new callback API)', function (st) {
586-
// <0.2.1 doesn't have this
587-
if (!solc.features.importCallback) {
588-
st.skip('Not supported by solc');
589-
st.end();
590-
return;
591-
}
592-
593-
var input = {
594-
'language': 'Solidity',
595-
'settings': {
596-
'outputSelection': {
597-
'*': {
598-
'*': [ 'evm.bytecode' ]
599-
}
600-
}
601-
},
602-
'sources': {
603-
'b.sol': {
604-
'content': 'import "a.sol"; contract B is A { function g() public { f(); } }'
605-
}
606-
}
607-
};
608-
609-
function findImports (path) {
610-
if (path === 'a.sol') {
611-
return { contents: 'contract A { function f() public returns (uint) { return 7; } }' };
612-
} else {
613-
return { error: 'File not found' };
614-
}
615-
}
616-
617579
var output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports }));
618580
st.ok(expectNoError(output));
619581
var A = getBytecodeStandard(output, 'a.sol', 'A');
@@ -622,6 +584,7 @@ function runTests (solc, versionText) {
622584
var B = getBytecodeStandard(output, 'b.sol', 'B');
623585
st.ok(typeof B === 'string');
624586
st.ok(B.length > 0);
587+
st.ok(Object.keys(linker.findLinkReferences(B)).length === 0);
625588
st.end();
626589
});
627590

@@ -821,14 +784,6 @@ function runTests (solc, versionText) {
821784
});
822785
});
823786
});
824-
825-
tape('API backwards compatibility', function (t) {
826-
t.test('compileStandard and compileStandardWrapper exists', function (st) {
827-
st.equal(solc.compile, solc.compileStandard);
828-
st.equal(solc.compile, solc.compileStandardWrapper);
829-
st.end();
830-
});
831-
});
832787
}
833788
}
834789

test/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const semver = require('semver');
33
require('./linker.js');
44
require('./translate.js');
55
require('./compiler.js');
6+
require('./smtcallback.js');
67
require('./smtchecker.js');
78
require('./abi.js');
89

test/smtCheckerTests/smoke.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pragma experimental SMTChecker;
2+
contract C {
3+
function f() public pure {
4+
}
5+
}

test/smtcallback.js

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
const tape = require('tape');
2+
const fs = require('fs');
3+
const path = require('path');
4+
const semver = require('semver');
5+
const solc = require('../index.js');
6+
const smtchecker = require('../smtchecker.js');
7+
const smtsolver = require('../smtsolver.js');
8+
9+
var pragmaSol = 'pragma solidity >=0.0;\n';
10+
var pragmaSMT = 'pragma experimental SMTChecker;\n';
11+
12+
function collectErrors (solOutput) {
13+
if (solOutput === undefined) {
14+
return [];
15+
}
16+
17+
var errors = [];
18+
for (var i in solOutput.errors) {
19+
var error = solOutput.errors[i];
20+
if (error.message.includes('This is a pre-release compiler version')) {
21+
continue;
22+
}
23+
errors.push(error.message);
24+
}
25+
return errors;
26+
}
27+
28+
function expectErrors (errors, expectations) {
29+
if (errors.length !== expectations.length) {
30+
return false;
31+
}
32+
33+
for (var i in errors) {
34+
if (!errors[i].includes(expectations[i])) {
35+
return false;
36+
}
37+
}
38+
39+
return true;
40+
}
41+
42+
tape('SMTCheckerCallback', function (t) {
43+
t.test('Interface via callback', function (st) {
44+
if (!semver.gt(solc.semver(), '0.5.99')) {
45+
st.skip('SMT callback not implemented by this compiler version.');
46+
st.end();
47+
return;
48+
}
49+
50+
var satCallback = function (query) {
51+
return { contents: 'sat\n' };
52+
};
53+
var unsatCallback = function (query) {
54+
return { contents: 'unsat\n' };
55+
};
56+
var errorCallback = function (query) {
57+
return { error: 'Fake SMT solver error.' };
58+
};
59+
60+
var input = { 'a': { content: pragmaSol + pragmaSMT + 'contract C { function f(uint x) public pure { assert(x > 0); } }' } };
61+
var inputJSON = JSON.stringify({
62+
language: 'Solidity',
63+
sources: input
64+
});
65+
var tests = [
66+
{ cb: satCallback, expectations: ['Assertion violation happens here'] },
67+
{ cb: unsatCallback, expectations: [] },
68+
{ cb: errorCallback, expectations: ['BMC analysis was not possible'] }
69+
];
70+
for (var i in tests) {
71+
var test = tests[i];
72+
var output = JSON.parse(solc.compile(
73+
inputJSON,
74+
{ smtSolver: test.cb }
75+
));
76+
var errors = collectErrors(output);
77+
st.ok(expectErrors(errors, test.expectations));
78+
}
79+
st.end();
80+
});
81+
82+
t.test('Solidity smtCheckerTests', function (st) {
83+
var testdir = path.resolve(__dirname, 'smtCheckerTests/');
84+
if (!fs.existsSync(testdir)) {
85+
st.skip('SMT checker tests not present.');
86+
st.end();
87+
return;
88+
}
89+
90+
if (smtsolver.availableSolvers === 0) {
91+
st.skip('No SMT solver available.');
92+
st.end();
93+
return;
94+
}
95+
96+
var sources = [];
97+
98+
// BFS to get all test files
99+
var dirs = [testdir];
100+
var i;
101+
while (dirs.length > 0) {
102+
var dir = dirs.shift();
103+
var files = fs.readdirSync(dir);
104+
for (i in files) {
105+
var file = path.join(dir, files[i]);
106+
if (fs.statSync(file).isDirectory()) {
107+
dirs.push(file);
108+
} else {
109+
sources.push(file);
110+
}
111+
}
112+
}
113+
114+
// Read tests and collect expectations
115+
var tests = [];
116+
for (i in sources) {
117+
st.comment('Collecting ' + sources[i] + '...');
118+
var source = fs.readFileSync(sources[i], 'utf8');
119+
var expected = [];
120+
var delimiter = '// ----';
121+
if (source.includes(delimiter)) {
122+
expected = source.substring(source.indexOf('// ----') + 8, source.length).split('\n');
123+
// Sometimes the last expectation line ends with a '\n'
124+
if (expected.length > 0 && expected[expected.length - 1] === '') {
125+
expected.pop();
126+
}
127+
}
128+
tests[sources[i]] = {
129+
expectations: expected,
130+
solidity: { test: { content: pragmaSol + source } }
131+
};
132+
}
133+
134+
// Run all tests
135+
for (i in tests) {
136+
var test = tests[i];
137+
var output = JSON.parse(solc.compile(
138+
JSON.stringify({
139+
language: 'Solidity',
140+
sources: test.solidity
141+
}),
142+
{ smtSolver: smtchecker.smtCallback(smtsolver.smtSolver) }
143+
));
144+
st.ok(output);
145+
146+
// Collect obtained error messages
147+
test.errors = collectErrors(output);
148+
149+
// These are errors in the SMTLib2Interface encoding.
150+
if (test.errors.length > 0 && test.errors[test.errors.length - 1].includes('BMC analysis was not possible')) {
151+
continue;
152+
}
153+
154+
// These are due to CHC not being supported via SMTLib2Interface yet.
155+
if (test.expectations.length !== test.errors.length) {
156+
continue;
157+
}
158+
159+
// Compare expected vs obtained errors
160+
st.ok(expectErrors(test.expectations, test.errors));
161+
}
162+
163+
st.end();
164+
});
165+
});

0 commit comments

Comments
 (0)