uport
Alex Rea 8 years ago
parent 165694d3cf
commit b5ca8b3da8
  1. 308
      instrumentSolidity.js
  2. 13
      runCoveredTests.js

@ -1,24 +1,42 @@
var SolidityParser = require("solidity-parser"); // var SolidityParser = require("solidity-parser");
var solparse = require("solparse");
var fs = require('fs');
var path = require("path"); var path = require("path");
module.exports = function(pathToFile, instrument){ module.exports = function(pathToFile, instrument){
var result = SolidityParser.parseFile("./" + pathToFile); // var result = SolidityParser.parseFile("./" + pathToFile);
var contract = fs.readFileSync("./" + pathToFile).toString();
var result = solparse.parse(contract);
var instrumented = ""; var instrumented = "";
const __INDENTATION__ = " "; const __INDENTATION__ = " ";
var parse = {}; var parse = {};
var runnableLines=[]; var runnableLines=[];
var linecount = 1; var linecount = 1;
var fileName = path.basename(pathToFile); var fileName = path.basename(pathToFile);
var dottable = ['msg', 'tx', 'this']; var dottable = ['msg', 'tx', 'this', 'bytes'];
function addInstrumentationEvent(){
runnableLines.push(linecount);
return "Coverage('" + fileName + "'," + linecount + ");\n";
}
parse["AssignmentExpression"] = function (expression){ parse["AssignmentExpression"] = function (expression, instrument){
if (instrument){ retval += addInstrumentationEvent(); }
var retval = ""; var retval = "";
retval += parse[expression.left.type](expression.left); retval += parse[expression.left.type](expression.left, instrument);
retval += expression.operator; retval += expression.operator;
retval += parse[expression.right.type](expression.right); retval += parse[expression.right.type](expression.right, instrument) + ';';
if (dottable.indexOf(expression.right.name)>=0){
dottable.push(expression.left.name);
}
return retval; return retval;
} }
parse["ConditionalExpression"] = function(expression){
return parse[expression.test.left.type](expression.test.left) + expression.test.operator + parse[expression.test.right.type](expression.test.right) + '?' + parse[expression.consequent.type](expression.consequent) + ":" + parse[expression.alternate.type](expression.alternate);
}
parse["Identifier"] = function(expression){ parse["Identifier"] = function(expression){
return expression.name; return expression.name;
} }
@ -28,37 +46,91 @@ module.exports = function(pathToFile, instrument){
} }
parse["Literal"] = function(expression){ parse["Literal"] = function(expression){
if (typeof expression.value==='string' && expression.value.slice(0,2)!=="0x"){
return '"' + expression.value + '"';
}else{
return expression.value; return expression.value;
} }
}
parse["ModifierName"] = function(expression){
var retvalue = expression.name
if (expression.params && expression.params.length>0){
retvalue += '(';
for (x in expression.params){
var param = expression.params[x];
retvalue += param.literal.literal + ', ';
}
retvalue = retvalue.slice(0,-2);
retvalue += ')';
}
return retvalue;
};
parse["Modifiers"] = function(modifiers){ parse["Modifiers"] = function(modifiers){
retvalue = ""; retvalue = "";
var retModifier = null; var retModifier = null;
var constModifier = null;
for (x in modifiers){ for (x in modifiers){
if (modifiers[x].name==='returns'){ if (modifiers[x].name==='returns'){
retModifier = x; retModifier = x;
continue; continue;
}else if (modifiers[x].name==='constant'){
constModifier = x;
}else{ }else{
console.log('unknown modifier: ', modifiers[x]); retvalue+=parse[modifiers[x].type](modifiers[x]);
retvalue += ' ';
} }
} }
if (constModifier) {retvalue += 'constant '};
if (retModifier){ if (retModifier){
retvalue += ' returns (' retvalue += ' returns ';
retvalue += ' (';
for (p in modifiers[retModifier].params){ for (p in modifiers[retModifier].params){
param = modifiers[retModifier].params[p]; param = modifiers[retModifier].params[p];
retvalue+= parse[param.type](param); retvalue+= parse[param.type](param);
retvalue += ', ';
} }
retvalue = retvalue.slice(0,-2);
retvalue +=')'; retvalue +=')';
} }
return retvalue; return retvalue;
} }
parse["ReturnStatement"] = function(expression){ parse["ThisExpression"] = function(expression){
return 'return ' + parse[expression.argument.type](expression.argument) + ';'; return 'this'
}
parse["ReturnStatement"] = function(expression, instrument){
var retval = "";
if (instrument){ retval += addInstrumentationEvent(); }
return retval + 'return ' + parse[expression.argument.type](expression.argument, instrument) + ';';
}
parse["NewExpression"] = function(expression){
var retval = 'new ' + parse[expression.callee.type](expression.callee);
retval += '(';
for (x in expression.arguments){
retval += parse[expression.arguments[x].type](expression.arguments[x]) + ", "
}
if (expression.arguments && expression.arguments.length){
retval = retval.slice(0,-2);
}
retval+=")"
return retval
} }
parse["MemberExpression"] = function (expression){ parse["MemberExpression"] = function (expression){
if (dottable.indexOf(expression.object.name)>-1){ // return contract.slice(expression.start, expression.end);
// var shouldDot = false;
// if (dottable.indexOf(expression.object.name)>=0){shouldDot = true;}
// if (expression.object.callee){
// if (dottable.indexOf(expression.object.callee.name)>=0){shouldDot=true;}
// }
if (!expression.computed){
return parse[expression.object.type](expression.object) + "." + parse[expression.property.type](expression.property); return parse[expression.object.type](expression.object) + "." + parse[expression.property.type](expression.property);
}else{ }else{
return parse[expression.object.type](expression.object) + "[" + parse[expression.property.type](expression.property) + "]"; return parse[expression.object.type](expression.object) + "[" + parse[expression.property.type](expression.property) + "]";
@ -78,13 +150,58 @@ module.exports = function(pathToFile, instrument){
return retval return retval
} }
parse["UnaryExpression"] = function(expression){
if (expression.operator==='delete'){
return expression.operator + ' ' + parse[expression.argument.type](expression.argument);
}
return expression.operator + parse[expression.argument.type](expression.argument);
}
parse["ThrowStatement"] = function(expression, instrument){
var retval = "";
if (instrument){ retval += addInstrumentationEvent(); }
return retval + 'throw'
}
parse["BinaryExpression"] = function(expression){ parse["BinaryExpression"] = function(expression){
return parse[expression.left.type](expression.left) + expression.operator + parse[expression.right.type](expression.right); return '(' + parse[expression.left.type](expression.left) + expression.operator + parse[expression.right.type](expression.right) + ')';
} }
parse["IfStatement"] = function(expression){ parse["IfStatement"] = function(expression, instrument){
var retval = "if ("; var retval = "";
retval += parse[expression.test.type](expression.test) + "){" + parse[expression.consequent.type](expression.consequent) + "}"; if (instrument){ retval += addInstrumentationEvent(); }
retval += "if (";
retval += parse[expression.test.type](expression.test, instrument) + "){"
retval += newLine('{');
retval += parse[expression.consequent.type](expression.consequent, instrument)
retval += newLine(retval.slice(-1));
retval += "}";
if (expression.alternate){
retval += 'else ';
if (expression.alternate.type!=="IfStatement"){
retval += '{';
retval += newLine('{');
}
retval += parse[expression.alternate.type](expression.alternate, instrument)
if (expression.alternate.type!=="IfStatement"){
retval += '}';
retval += newLine('}');
}
}
return retval;
}
parse["SequenceExpression"] = function(expression){
retval = "(";
for (x in expression.expressions){
retval += parse[expression.expressions[x].type](expression.expressions[x]) + ', ';
}
if (expression.expressions && expression.expressions.length>0){
//remove trailing comma and space if needed
retval = retval.slice(0,-2);
}
retval += ')';
return retval; return retval;
} }
@ -93,18 +210,31 @@ module.exports = function(pathToFile, instrument){
return 'import "' + expression.from + '"'; return 'import "' + expression.from + '"';
} }
parse["ExpressionStatement"] = function(content){ parse["DeclarativeExpression"] = function(expression){
if (content.expression.type==="AssignmentExpression"){ return expression.literal.literal + ' ' + (expression.is_public ? "public " : "") + (expression.is_constant ? "constant " : "") + expression.name;
return parse["AssignmentExpression"](content.expression); }
}else if (content.expression.type==="CallExpression"){
return parse["CallExpression"](content.expression); parse["ExpressionStatement"] = function(content, instrument){
}else if(content.expression.literal.literal.type==="MappingExpression"){ var retval = "";
return 'mapping (' + content.expression.literal.literal.from.literal + ' => ' + content.expression.literal.literal.to.literal + ') '+ content.expression.name; if (instrument){ retval += addInstrumentationEvent(); }
if (content.expression.literal && content.expression.literal.literal && content.expression.literal.literal.type==="MappingExpression"){
return retval + 'mapping (' + content.expression.literal.literal.from.literal + ' => ' + content.expression.literal.literal.to.literal + ') '+ content.expression.name;
}else { }else {
console.log(content); return retval + parse[content.expression.type](content.expression);
} }
} }
parse["EnumDeclaration"] = function(expression){
var retvalue = 'enum ' + expression.name + ' {';
dottable.push(expression.name);
for (x in expression.members){
retvalue += expression.members[x] + ', ';
}
retvalue = retvalue.slice(0,-2);
retvalue += '}';
return retvalue;
}
parse["EventDeclaration"]=function(expression){ parse["EventDeclaration"]=function(expression){
var retval = 'event ' + expression.name + '('; var retval = 'event ' + expression.name + '(';
for (x in expression.params){ for (x in expression.params){
@ -119,16 +249,68 @@ module.exports = function(pathToFile, instrument){
return retval return retval
} }
parse["BlockStatement"] = function(expression){ parse["VariableDeclarationTuple"] = function(expression){
if (expression.body.length>1){ var retval = "";
console.log('bigger blockstatement...') if (instrument){ retval += addInstrumentationEvent(); }
retval += "var (";
for (x in expression.declarations){
retval += expression.declarations[x].id.name + ', ';
} }
return parse[expression.body[0].type](expression.body[0]); retval = retval.slice(0,-2);
retval += ") = ";
retval+=parse[expression.init.type](expression.init)
return retval;
}
parse["BlockStatement"] = function(expression, instrument){
var retval = "";
for (var x=0; x < expression.body.length; x++){
retval += parse[expression.body[x].type](expression.body[x], instrument);
retval += newLine(retval.slice(-1));
}
return retval;
}
parse["VariableDeclaration"] = function(expression, instrument){
if (expression.declarations.length>1){
console.log('more than one declaration')
}
retval = "";
if (instrument){ retval += addInstrumentationEvent(); }
return retval + "var " + parse[expression.declarations[0].id.type](expression.declarations[0].id) + " = " + parse[expression.declarations[0].init.type](expression.declarations[0].init);
}
parse["Type"] = function(expression){
return expression.literal;
}
parse["UsingStatement"] = function(expression){
return "using " + expression.library + " for " + parse[expression.for.type](expression.for) + ";";
}
parse["FunctionDeclaration"] = function(expression){
retval = 'function ' + (expression.name ? expression.name : "") + '('
for (x in expression.params){
var param = expression.params[x];
retval += param.literal.literal + ' ' + (param.isIndexed ? 'true' : '') + param.id + ', ';
}
if (expression.params && expression.params.length>0){
retval = retval.slice(0,-2);
}
retval += ')';
retval += parse["Modifiers"](expression.modifiers);
if (expression.body){
retval+='{' + newLine('{');
retval += parse[expression.body.type](expression.body, instrument);
retval+='}' + newLine('}');
}
return retval;
} }
function newLine(lastchar){ function newLine(lastchar){
linecount+=1; linecount+=1;
if (['}','{',';'].indexOf(lastchar)>-1){ if (['}','{',';','_','\n'].indexOf(lastchar)>-1){
return '\n'; return '\n';
}else{ }else{
return ';\n'; return ';\n';
@ -141,16 +323,55 @@ module.exports = function(pathToFile, instrument){
function printBody(content, indented, cover){ function printBody(content, indented, cover){
if (content.body){ if (content.body){
if (content.type === "ContractStatement"){ if (content.type === "ContractStatement"){
instrumented += 'contract ' + content.name + ' {' + newLine('{'); instrumented += 'contract ' + content.name
//Inject our coverage event;
if (content.is.length>0){
instrumented += ' is '
for (x in content.is){
instrumented += content.is[x].name + ', '
}
instrumented = instrumented.slice(0,-2);
}
instrumented += ' {' + newLine('{');
//Inject our coverage event if we're covering
if (instrument){
instrumented += "event Coverage(string fileName, uint256 lineNumber);\n"; //We're injecting this, so don't count the newline instrumented += "event Coverage(string fileName, uint256 lineNumber);\n"; //We're injecting this, so don't count the newline
}
for (x in content.body){ for (x in content.body){
printBody(content.body[x], indented+1, cover); printBody(content.body[x], indented+1, cover);
} }
instrumented += '}' + newLine('}'); instrumented += '}' + newLine('}');
}else if (content.type === "FunctionDeclaration"){ }else if (content.type === "FunctionDeclaration"){
instrumented += __INDENTATION__.repeat(indented) + 'function ' + content.name + '(' instrumented += parse[content.type](content);
// instrumented += __INDENTATION__.repeat(indented) + 'function ' + (content.name ? content.name : "") + '('
// for (x in content.params){
// var param = content.params[x];
// instrumented += param.literal.literal + ' ' + (param.isIndexed ? 'true' : '') + param.id + ', ';
// if (param.literal.literal==='address'){
// dottable.push(param.id);
// }
// }
// if (content.params && content.params.length>0){
// instrumented = instrumented.slice(0,-2);
// }
// instrumented += ')';
// instrumented += parse["Modifiers"](content.modifiers);
// instrumented+='{' + newLine('{');
// printBody(content.body, indented+1, instrument);
// instrumented+=__INDENTATION__.repeat(indented) +'}' + newLine('}');
}else if (content.type === "LibraryStatement"){
instrumented += 'library ' + content.name + ' {' + newLine('{');
//Inject our coverage event;
instrumented += "event Coverage(string fileName, uint256 lineNumber);\n"; //We're injecting this, so don't count the newline
for (x in content.body){
printBody(content.body[x], indented+1, cover);
}
instrumented += '}' + newLine('}');
}else if (content.type === "ModifierDeclaration"){
instrumented += 'modifier ' + content.name + '(';
for (x in content.params){ for (x in content.params){
var param = content.params[x]; var param = content.params[x];
instrumented += param.literal.literal + ' ' + (param.isIndexed ? 'true' : '') + param.id + ', '; instrumented += param.literal.literal + ' ' + (param.isIndexed ? 'true' : '') + param.id + ', ';
@ -158,21 +379,24 @@ module.exports = function(pathToFile, instrument){
if (content.params && content.params.length>0){ if (content.params && content.params.length>0){
instrumented = instrumented.slice(0,-2); instrumented = instrumented.slice(0,-2);
} }
instrumented += ')'; instrumented += '){';
instrumented += parse["Modifiers"](content.modifiers); instrumented += newLine(instrumented.slice(-1));
instrumented+='{' + newLine('{'); instrumented += parse[content.body.type](content.body, instrument);
printBody(content.body, indented+1, instrument); instrumented += newLine(instrumented.slice(-1));
instrumented+=__INDENTATION__.repeat(indented) +'}' + newLine('}'); instrumented +='}';
}else if (content.type === "LibraryStatement"){ instrumented += newLine(instrumented.slice(-1));
instrumented += 'library ' + content.name + ' {' + newLine('{');
//Inject our coverage event;
instrumented += "event Coverage(string fileName, uint256 lineNumber);\n"; //We're injecting this, so don't count the newline
}else if (content.type === "BlockStatement"){
instrumented += parse['BlockStatement'](content, cover);
}else if (content.type ==="Program"){
//I don't think we need to do anything here...
for (x in content.body){ for (x in content.body){
printBody(content.body[x], indented+1, cover); printBody(content.body[x], indented, cover);
} }
instrumented += '}' + newLine('}');
}else{ }else{
console.log(content);
process.exit()
for (x in content.body){ for (x in content.body){
printBody(content.body[x], indented, cover); printBody(content.body[x], indented, cover);
} }

@ -14,14 +14,18 @@ shell.mv('./contracts/', './originalContracts');
shell.mkdir('./contracts/'); shell.mkdir('./contracts/');
//For each contract in originalContracts, get the canonical version and the instrumented version //For each contract in originalContracts, get the canonical version and the instrumented version
shell.ls('./originalContracts/*.sol').forEach(function(file) { shell.ls('./originalContracts/*.sol').forEach(function(file) {
if (file !== './originalContracts/Migrations.sol') { if (file !== 'originalContracts/Migrations.sol') {
console.log("=================")
console.log(file);
console.log("=================")
var instrumentedContractInfo = getInstrumentedVersion(file, true); var instrumentedContractInfo = getInstrumentedVersion(file, true);
var canonicalContractInfo = getInstrumentedVersion(file, false); var canonicalContractInfo = getInstrumentedVersion(file, false);
fs.writeFileSync('./canonicalContracts/' + path.basename(file), canonicalContractInfo.contract); fs.writeFileSync('./canonicalContracts/' + path.basename(file), canonicalContractInfo.contract);
fs.writeFileSync('./contracts/' + path.basename(file), instrumentedContractInfo.contract); fs.writeFileSync('./contracts/' + path.basename(file), instrumentedContractInfo.contract);
} }
shell.cp("./originalContracts/Migrations.sol", "./contracts/Migrations.sol");
}); });
shell.cp("./originalContracts/Migrations.sol", "./contracts/Migrations.sol");
shell.cp("./originalContracts/Migrations.sol", "./canonicalContracts/Migrations.sol");
var filter = web3.eth.filter('latest'); var filter = web3.eth.filter('latest');
var res = web3.currentProvider.send({ var res = web3.currentProvider.send({
@ -31,10 +35,7 @@ var res = web3.currentProvider.send({
id: new Date().getTime() id: new Date().getTime()
}); });
var filterid = res.result; var filterid = res.result;
shell.exec("truffle test"); shell.exec("truffle test");
//Again, once that truffle issue gets solved, we don't have to call these again here //Again, once that truffle issue gets solved, we don't have to call these again here
shell.ls('./originalContracts/*.sol').forEach(function(file) { shell.ls('./originalContracts/*.sol').forEach(function(file) {
if (file !== './originalContracts/Migrations.sol') { if (file !== './originalContracts/Migrations.sol') {
@ -68,7 +69,7 @@ for (idx in res.result) {
fs.writeFileSync('./coverage.json', JSON.stringify(coverage)); fs.writeFileSync('./coverage.json', JSON.stringify(coverage));
shell.exec("istanbul report text") shell.exec("istanbul report html")
shell.rm('-rf', './contracts'); shell.rm('-rf', './contracts');
shell.rm('-rf', './canonicalContracts'); shell.rm('-rf', './canonicalContracts');

Loading…
Cancel
Save