Skip to content

Commit 1ec36ca

Browse files
committed
Modify preprocessor for nested conditionals, test w/ oraclize
1 parent de233a9 commit 1ec36ca

File tree

8 files changed

+138
-39
lines changed

8 files changed

+138
-39
lines changed

lib/preprocessor.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ const SolExplore = require('sol-explore');
22
const SolidityParser = require('solidity-parser-antlr');
33

44
const crRegex = /[\r\n ]+$/g;
5+
const OPEN = '{';
6+
const CLOSE = '}';
7+
58
/**
69
* Splices enclosing brackets into `contract` around `expression`;
710
* @param {String} contract solidity source
811
* @param {Object} node AST node to bracket
912
* @return {String} contract
1013
*/
11-
function blockWrap(contract, expression) {
12-
return contract.slice(0, expression.range[0]) + '{' + contract.slice(expression.range[0], expression.range[1] + 1) + '}' + contract.slice(expression.range[1] + 1);
14+
function insertBrace(contract, item, offset) {
15+
return contract.slice(0,item.pos + offset) + item.type + contract.slice(item.pos + offset)
1316
}
1417

1518
/** Remove 'pure' and 'view' from the function declaration.
@@ -47,24 +50,28 @@ module.exports.run = function r(contract) {
4750

4851
try {
4952
const ast = SolidityParser.parse(contract, { range: true });
50-
blocksToWrap = [];
53+
insertions = [];
5154
viewPureToRemove = [];
5255
SolidityParser.visit(ast, {
5356
IfStatement: function(node) {
5457
if (node.trueBody.type !== 'Block') {
55-
blocksToWrap.push(node.trueBody);
56-
} else if (node.falseBody && node.falseBody.type !== 'Block'){
57-
blocksToWrap.push(node.falseBody);
58+
insertions.push({type: OPEN, pos: node.trueBody.range[0]});
59+
insertions.push({type: CLOSE, pos: node.trueBody.range[1] + 1});
60+
} else if ( node.falseBody && node.falseBody.type !== 'Block' ) {
61+
insertions.push({type: OPEN, pos: node.falseBody.range[0]});
62+
insertions.push({type: CLOSE, pos: node.falseBody.range[1] + 1});
5863
}
5964
},
6065
ForStatement: function(node){
6166
if (node.body.type !== 'Block'){
62-
blocksToWrap.push(node.body);
67+
insertions.push({type: OPEN, pos: node.body.range[0]});
68+
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
6369
}
6470
},
6571
WhileStatement: function(node){
6672
if (node.body.type !== 'Block'){
67-
blocksToWrap.push(node.body);
73+
insertions.push({type: OPEN, pos: node.body.range[0]});
74+
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
6875
}
6976
},
7077
FunctionDefinition: function(node){
@@ -73,12 +80,13 @@ module.exports.run = function r(contract) {
7380
}
7481
}
7582
})
76-
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
83+
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
7784
// character counts remain the same, so we can do this in any order
7885
viewPureToRemove.forEach(node => contract = removePureView(contract, node));
79-
// We apply the blocks we found in reverse order to avoid extra characters messing things up.
80-
blocksToWrap.sort((a,b) => a.range[0] < b.range[0]);
81-
blocksToWrap.forEach(block => contract = blockWrap(contract, block))
86+
// Sort the insertion points.
87+
insertions.sort((a,b) => a.pos - b.pos);
88+
insertions.forEach((item, idx) => contract = insertBrace(contract, item, idx));
89+
8290
} catch (err) {
8391
contract = err;
8492
keepRunning = false;

test/app.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('app', () => {
2828
};
2929

3030
before(done => {
31-
const command = `./node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port ${port}`;
31+
const command = `./node_modules/.bin/testrpc-sc --allowUnlimitedContractSize --gasLimit 0xfffffffffff --port ${port}`;
3232
testrpcProcess = childprocess.exec(command);
3333

3434
testrpcProcess.stdout.on('data', data => {
@@ -194,6 +194,36 @@ describe('app', () => {
194194
}
195195
});
196196

197+
it('large contract w/ many unbracketed statements (Oraclize)', () => {
198+
const trufflejs =
199+
`module.exports = {
200+
networks: {
201+
coverage: {
202+
host: "localhost",
203+
network_id: "*",
204+
port: 8555,
205+
gas: 0xfffffffffff,
206+
gasPrice: 0x01
207+
},
208+
},
209+
compilers: {
210+
solc: {
211+
version: "0.4.24",
212+
}
213+
}
214+
};`;
215+
216+
// Directory should be clean
217+
assert(pathExists('./coverage') === false, 'should start without: coverage');
218+
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
219+
220+
// Run script (exits 0);
221+
mock.install('Oraclize.sol', 'oraclize.js', config, trufflejs, null, true);
222+
shell.exec(script);
223+
assert(shell.error() === null, 'script should not error');
224+
225+
});
226+
197227
it('simple contract: should generate coverage, cleanup & exit(0)', () => {
198228
// Directory should be clean
199229
assert(pathExists('./coverage') === false, 'should start without: coverage');

test/cli/oraclize.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* eslint-env node, mocha */
2+
/* global artifacts, contract, assert */
3+
const usingOraclize = artifacts.require('usingOraclize');
4+
5+
contract('Nothing', () => {
6+
it('nothing', async () => {
7+
const ora = await usingOraclize.new();
8+
await ora.test();
9+
});
10+
});

test/if.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,30 @@ describe('if, else, and else if statements', () => {
186186
}).catch(done);
187187
});
188188

189+
it('should cover an else if statement with an unbracketed alternate', done => {
190+
const contract = util.getCode('if/else-if-without-brackets.sol');
191+
const info = getInstrumentedVersion(contract, filePath);
192+
const coverage = new CoverageMap();
193+
coverage.addContract(info, filePath);
194+
195+
vm.execute(info.contract, 'a', [2]).then(events => {
196+
const mapping = coverage.generate(events, pathPrefix);
197+
assert.deepEqual(mapping[filePath].l, {
198+
5: 1, 6: 0, 8: 0,
199+
});
200+
assert.deepEqual(mapping[filePath].b, {
201+
1: [0, 1], 2: [0, 1]
202+
});
203+
assert.deepEqual(mapping[filePath].s, {
204+
1: 1, 2: 0, 3: 1, 4: 0
205+
});
206+
assert.deepEqual(mapping[filePath].f, {
207+
1: 1,
208+
});
209+
done();
210+
}).catch(done);
211+
});
212+
189213
it('should cover nested if statements with missing else statements', done => {
190214
const contract = util.getCode('if/nested-if-missing-else.sol');
191215
const info = getInstrumentedVersion(contract, filePath);

test/sources/statements/oraclize-plus.sol renamed to test/sources/cli/Oraclize.sol

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
pragma solidity ^0.4.0;
21
// <ORACLIZE_API>
32
/*
43
Copyright (c) 2015-2016 Oraclize SRL
@@ -30,7 +29,7 @@ THE SOFTWARE.
3029
*/
3130

3231
// This api is currently targeted at 0.4.18, please import oraclizeAPI_pre0.4.sol or oraclizeAPI_0.4 where necessary
33-
pragma solidity ^0.4.18;
32+
pragma solidity ^0.4.24;
3433

3534
contract OraclizeI {
3635
address public cbAddress;
@@ -44,7 +43,7 @@ contract OraclizeI {
4443
function getPrice(string _datasource, uint gaslimit) public returns (uint _dsprice);
4544
function setProofType(byte _proofType) external;
4645
function setCustomGasPrice(uint _gasPrice) external;
47-
function randomDS_getSessionPubKeyHash() external constant returns(bytes32);
46+
function randomDS_getSessionPubKeyHash() external view returns(bytes32);
4847
}
4948
contract OraclizeAddrResolverI {
5049
function getAddress() public returns (address _addr);
@@ -525,7 +524,7 @@ contract usingOraclize {
525524
return oraclize.randomDS_getSessionPubKeyHash();
526525
}
527526

528-
function getCodeSize(address _addr) constant internal returns(uint _size) {
527+
function getCodeSize(address _addr) view internal returns(uint _size) {
529528
assembly {
530529
_size := extcodesize(_addr)
531530
}
@@ -551,7 +550,7 @@ contract usingOraclize {
551550
return address(iaddr);
552551
}
553552

554-
function strCompare(string _a, string _b) internal pure returns (int) {
553+
/*function strCompare(string _a, string _b) internal pure returns (int) {
555554
bytes memory a = bytes(_a);
556555
bytes memory b = bytes(_b);
557556
uint minLength = a.length;
@@ -567,7 +566,7 @@ contract usingOraclize {
567566
return 1;
568567
else
569568
return 0;
570-
}
569+
}*/
571570

572571
function indexOf(string _haystack, string _needle) internal pure returns (int) {
573572
bytes memory h = bytes(_haystack);
@@ -638,8 +637,9 @@ contract usingOraclize {
638637
for (uint i=0; i<bresult.length; i++){
639638
if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
640639
if (decimals){
641-
if (_b == 0) break;
642-
else _b--;
640+
if (_b == 0) {
641+
break;
642+
} else _b--;
643643
}
644644
mint *= 10;
645645
mint += uint(bresult[i]) - 48;
@@ -1043,29 +1043,39 @@ contract usingOraclize {
10431043
}
10441044

10451045
function nestingTest(string _a, string _b, string _c, string _d, string _e) {
1046+
uint i;
1047+
uint k;
1048+
1049+
uint[] _ba;
1050+
uint[] _bb;
1051+
uint[] _bc;
1052+
uint[] _bd;
1053+
uint[] _be;
1054+
uint[] babcde;
1055+
10461056
for (i = 0; i < _bb.length; i++)
10471057
for (i = 0; i < _bb.length; i++)
1048-
if (i = 0)
1058+
if (i == 0)
10491059
babcde[k++] = _bb[i];
10501060
for (i = 0; i < _bb.length; i++)
10511061
for (i = 0; i < _bb.length; i++)
1052-
if (i = 0)
1053-
while (i)
1062+
if (i == 0)
1063+
while (true)
10541064
babcde[k++] = _bb[i];
10551065
for (i = 0; i < _bb.length; i++)
10561066
for (i = 0; i < _bb.length; i++)
1057-
if (i = 0)
1067+
if (i == 0)
10581068
babcde[k++] = _bb[i];
10591069
for (i = 0; i < _bb.length; i++)
10601070
for (i = 0; i < _bb.length; i++)
1061-
if (i = 0)
1071+
if (i == 0)
10621072
babcde[k++] = _bb[i];
10631073
for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
1064-
for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
1074+
for (i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
10651075
for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
10661076
for (i = 0; i < _bb.length; i++)
10671077
for (i = 0; i < _bb.length; i++)
1068-
if (i = 0)
1078+
if (i == 0)
10691079
babcde[k++] = _bb[i];
10701080
for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
10711081
for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
@@ -1079,5 +1089,9 @@ contract usingOraclize {
10791089
return;
10801090
}
10811091

1092+
function test() public {
1093+
uint tls = 5;
1094+
}
1095+
10821096
}
10831097
// </ORACLIZE_API>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pragma solidity ^0.5.0;
2+
3+
contract Test {
4+
function a(uint x) public {
5+
if (x == 1) {
6+
revert();
7+
} else if (x == 50)
8+
x = 5;
9+
}
10+
}

test/statements.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,6 @@ describe('generic statements', () => {
5959
util.report(output.errors);
6060
});
6161

62-
it.only('should compile after instrumenting many unbracketed statements', () => {
63-
const contract = util.getCode('statements/oraclize-plus.sol');
64-
const info = getInstrumentedVersion(contract, filePath);
65-
const output = JSON.parse(solc.compile(util.codeToCompilerInput(info.contract)));
66-
util.report(output.errors);
67-
});
68-
6962
it('should NOT pass tests if the contract has a compilation error', () => {
7063
const contract = util.getCode('statements/compilation-error.sol');
7164
const info = getInstrumentedVersion(contract, filePath);

test/util/mockTruffle.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ const defaultTruffleJs = `module.exports = {
2727
* @param {String} contract <contractName.sol> located in /test/sources/cli/
2828
* @param {[type]} test <testName.js> located in /test/cli/
2929
*/
30-
module.exports.install = function install(contract, test, config, _trufflejs, _trufflejsName) {
30+
module.exports.install = function install(
31+
contract,
32+
test,
33+
config,
34+
_trufflejs,
35+
_trufflejsName,
36+
noMigrations
37+
) {
3138
const configjs = `module.exports = ${JSON.stringify(config)}`;
3239
const contractLocation = `./${contract}`;
3340
const trufflejsName = _trufflejsName || 'truffle.js';
@@ -68,9 +75,12 @@ module.exports.install = function install(contract, test, config, _trufflejs, _t
6875
shell.cp(`./test/sources/cli/${contract}`, `./mock/contracts/${contract}`);
6976
}
7077

71-
shell.cp('./test/sources/cli/Migrations.sol', './mock/contracts/Migrations.sol');
72-
fs.writeFileSync('./mock/migrations/1_initial_migration.js', initialMigration);
73-
fs.writeFileSync('./mock/migrations/2_deploy_contracts.js', deployContracts);
78+
if (!noMigrations){
79+
shell.cp('./test/sources/cli/Migrations.sol', './mock/contracts/Migrations.sol');
80+
fs.writeFileSync('./mock/migrations/1_initial_migration.js', initialMigration);
81+
fs.writeFileSync('./mock/migrations/2_deploy_contracts.js', deployContracts);
82+
}
83+
7484
fs.writeFileSync(`./mock/${trufflejsName}`, trufflejs);
7585
fs.writeFileSync('./mock/assets/asset.js', asset);
7686
fs.writeFileSync('./.solcover.js', configjs);

0 commit comments

Comments
 (0)