Support multi-contract files w/ inheritance for solc 0.6.x (#511)

pull/512/head
cgewecke 5 years ago committed by GitHub
parent 1b379c2924
commit 1b8b73086c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      lib/injector.js
  2. 6
      lib/parse.js
  3. 2
      lib/registrar.js
  4. 2
      package.json
  5. 7
      test/integration/projects/solc-6/contracts/ContractA.sol
  6. 2
      test/units/buidler/standard.js

@ -12,23 +12,23 @@ class Injector {
} }
} }
_getInjectable(fileName, hash, type){ _getInjectable(id, hash, type){
return `${this._getMethodIdentifier(fileName)}(${hash}); /* ${type} */ \n`; return `${this._getMethodIdentifier(id)}(${hash}); /* ${type} */ \n`;
} }
_getHash(fileName) { _getHash(id) {
this.hashCounter++; this.hashCounter++;
return web3Utils.keccak256(`${fileName}:${this.hashCounter}`); return web3Utils.keccak256(`${id}:${this.hashCounter}`);
} }
_getMethodIdentifier(fileName){ _getMethodIdentifier(id){
return `coverage_${web3Utils.keccak256(fileName).slice(0,10)}` return `coverage_${web3Utils.keccak256(id).slice(0,10)}`
} }
_getInjectionComponents(contract, injectionPoint, fileName, type){ _getInjectionComponents(contract, injectionPoint, id, type){
const { start, end } = this._split(contract, injectionPoint); const { start, end } = this._split(contract, injectionPoint);
const hash = this._getHash(fileName) const hash = this._getHash(id)
const injectable = this._getInjectable(fileName, hash, type); const injectable = this._getInjectable(id, hash, type);
return { return {
start: start, start: start,
@ -41,25 +41,26 @@ class Injector {
/** /**
* Generates a solidity statement injection. Declared once per fn. * Generates a solidity statement injection. Declared once per fn.
* Definition is the same for every fn in file. * Definition is the same for every fn in file.
* @param {String} fileName * @param {String} id
* @return {String} ex: bytes32[1] memory _sc_82e0891 * @return {String} ex: bytes32[1] memory _sc_82e0891
*/ */
_getHashMethodDefinition(fileName){ _getHashMethodDefinition(id, contract){
const hash = web3Utils.keccak256(fileName).slice(0,10); const hash = web3Utils.keccak256(id).slice(0,10);
const method = this._getMethodIdentifier(fileName); const method = this._getMethodIdentifier(id);
return `\nfunction ${method}(bytes32 c__${hash}) public pure {}\n`; return `\nfunction ${method}(bytes32 c__${hash}) public pure {}\n`;
} }
injectLine(contract, fileName, injectionPoint, injection, instrumentation){ injectLine(contract, fileName, injectionPoint, injection, instrumentation){
const type = 'line'; const type = 'line';
const { start, end } = this._split(contract, injectionPoint); const { start, end } = this._split(contract, injectionPoint);
const id = `${fileName}:${injection.contractName}`;
const newLines = start.match(/\n/g); const newLines = start.match(/\n/g);
const linecount = ( newLines || []).length + 1; const linecount = ( newLines || []).length + 1;
contract.runnableLines.push(linecount); contract.runnableLines.push(linecount);
const hash = this._getHash(fileName) const hash = this._getHash(id)
const injectable = this._getInjectable(fileName, hash, type); const injectable = this._getInjectable(id, hash, type);
instrumentation[hash] = { instrumentation[hash] = {
id: linecount, id: linecount,
@ -73,13 +74,14 @@ class Injector {
injectStatement(contract, fileName, injectionPoint, injection, instrumentation) { injectStatement(contract, fileName, injectionPoint, injection, instrumentation) {
const type = 'statement'; const type = 'statement';
const id = `${fileName}:${injection.contractName}`;
const { const {
start, start,
end, end,
hash, hash,
injectable injectable
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); } = this._getInjectionComponents(contract, injectionPoint, id, type);
instrumentation[hash] = { instrumentation[hash] = {
id: injection.statementId, id: injection.statementId,
@ -93,13 +95,14 @@ class Injector {
injectFunction(contract, fileName, injectionPoint, injection, instrumentation){ injectFunction(contract, fileName, injectionPoint, injection, instrumentation){
const type = 'function'; const type = 'function';
const id = `${fileName}:${injection.contractName}`;
const { const {
start, start,
end, end,
hash, hash,
injectable injectable
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); } = this._getInjectionComponents(contract, injectionPoint, id, type);
instrumentation[hash] = { instrumentation[hash] = {
id: injection.fnId, id: injection.fnId,
@ -113,13 +116,14 @@ class Injector {
injectBranch(contract, fileName, injectionPoint, injection, instrumentation){ injectBranch(contract, fileName, injectionPoint, injection, instrumentation){
const type = 'branch'; const type = 'branch';
const id = `${fileName}:${injection.contractName}`;
const { const {
start, start,
end, end,
hash, hash,
injectable injectable
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); } = this._getInjectionComponents(contract, injectionPoint, id, type);
instrumentation[hash] = { instrumentation[hash] = {
id: injection.branchId, id: injection.branchId,
@ -134,13 +138,14 @@ class Injector {
injectEmptyBranch(contract, fileName, injectionPoint, injection, instrumentation) { injectEmptyBranch(contract, fileName, injectionPoint, injection, instrumentation) {
const type = 'branch'; const type = 'branch';
const id = `${fileName}:${injection.contractName}`;
const { const {
start, start,
end, end,
hash, hash,
injectable injectable
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); } = this._getInjectionComponents(contract, injectionPoint, id, type);
instrumentation[hash] = { instrumentation[hash] = {
id: injection.branchId, id: injection.branchId,
@ -155,13 +160,14 @@ class Injector {
injectAssertPre(contract, fileName, injectionPoint, injection, instrumentation) { injectAssertPre(contract, fileName, injectionPoint, injection, instrumentation) {
const type = 'assertPre'; const type = 'assertPre';
const id = `${fileName}:${injection.contractName}`;
const { const {
start, start,
end, end,
hash, hash,
injectable injectable
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); } = this._getInjectionComponents(contract, injectionPoint, id, type);
instrumentation[hash] = { instrumentation[hash] = {
id: injection.branchId, id: injection.branchId,
@ -175,13 +181,14 @@ class Injector {
injectAssertPost(contract, fileName, injectionPoint, injection, instrumentation) { injectAssertPost(contract, fileName, injectionPoint, injection, instrumentation) {
const type = 'assertPost'; const type = 'assertPost';
const id = `${fileName}:${injection.contractName}`;
const { const {
start, start,
end, end,
hash, hash,
injectable injectable
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); } = this._getInjectionComponents(contract, injectionPoint, id, type);
instrumentation[hash] = { instrumentation[hash] = {
id: injection.branchId, id: injection.branchId,
@ -196,7 +203,8 @@ class Injector {
injectHashMethod(contract, fileName, injectionPoint, injection, instrumentation){ injectHashMethod(contract, fileName, injectionPoint, injection, instrumentation){
const start = contract.instrumented.slice(0, injectionPoint); const start = contract.instrumented.slice(0, injectionPoint);
const end = contract.instrumented.slice(injectionPoint); const end = contract.instrumented.slice(injectionPoint);
contract.instrumented = `${start}${this._getHashMethodDefinition(fileName)}${end}`; const id = `${fileName}:${injection.contractName}`;
contract.instrumented = `${start}${this._getHashMethodDefinition(id)}${end}`;
} }
}; };

@ -72,9 +72,11 @@ parse.ContractOrLibraryStatement = function(contract, expression) {
const end = contract.instrumented.slice(start).indexOf('{') + 1; const end = contract.instrumented.slice(start).indexOf('{') + 1;
const loc = start + end;; const loc = start + end;;
contract.contractName = expression.name;
(contract.injectionPoints[loc]) (contract.injectionPoints[loc])
? contract.injectionPoints[loc].push({ type: 'injectHashMethod'}) ? contract.injectionPoints[loc].push({ type: 'injectHashMethod', contractName: expression.name})
: contract.injectionPoints[loc] = [{ type: 'injectHashMethod'}]; : contract.injectionPoints[loc] = [{ type: 'injectHashMethod', contractName: expression.name}];
} }
if (expression.subNodes) { if (expression.subNodes) {

@ -14,6 +14,8 @@ class Registrar {
* @param {Number} value injection point `id` * @param {Number} value injection point `id`
*/ */
_createInjectionPoint(contract, key, value) { _createInjectionPoint(contract, key, value) {
value.contractName = contract.contractName;
(contract.injectionPoints[key]) (contract.injectionPoints[key])
? contract.injectionPoints[key].push(value) ? contract.injectionPoints[key].push(value)
: contract.injectionPoints[key] = [value]; : contract.injectionPoints[key] = [value];

@ -11,7 +11,7 @@
}, },
"scripts": { "scripts": {
"nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'", "nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'",
"test": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc -- mocha test/units/* --timeout 100000 --no-warnings --exit", "test": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc --exclude '**/sc_temp/**' --exclude '**/test/**/' -- mocha test/units/* --timeout 100000 --no-warnings --exit",
"test:ci": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc --reporter=lcov --exclude '**/sc_temp/**' --exclude '**/test/**/' -- mocha test/units/* --timeout 100000 --no-warnings --exit", "test:ci": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc --reporter=lcov --exclude '**/sc_temp/**' --exclude '**/test/**/' -- mocha test/units/* --timeout 100000 --no-warnings --exit",
"test:debug": "node --max-old-space-size=4096 ./node_modules/.bin/mocha test/units/* --timeout 100000 --no-warnings --exit" "test:debug": "node --max-old-space-size=4096 ./node_modules/.bin/mocha test/units/* --timeout 100000 --no-warnings --exit"
}, },

@ -57,3 +57,10 @@ contract ContractA is ContractB {
//address y = payable(x); // parser-diligence crashing here... //address y = payable(x); // parser-diligence crashing here...
} }
} }
// Making sure same-file inheritance works for solc-6...
contract ContractC is ContractA {
function simpleC(uint x) public {
x++;
}
}

@ -284,7 +284,7 @@ describe('Buidler Plugin: standard use cases', function() {
const expected = [ const expected = [
{ {
file: mock.pathToContract(buidlerConfig, 'ContractA.sol'), file: mock.pathToContract(buidlerConfig, 'ContractA.sol'),
pct: 100 pct: 87.5
}, },
{ {
file: mock.pathToContract(buidlerConfig, 'ContractB.sol'), file: mock.pathToContract(buidlerConfig, 'ContractB.sol'),

Loading…
Cancel
Save