From 1b8b73086c54952788310c03a3b0af2f71175994 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 7 Jun 2020 17:57:53 -0700 Subject: [PATCH] Support multi-contract files w/ inheritance for solc 0.6.x (#511) --- lib/injector.js | 52 +++++++++++-------- lib/parse.js | 6 ++- lib/registrar.js | 2 + package.json | 2 +- .../projects/solc-6/contracts/ContractA.sol | 7 +++ test/units/buidler/standard.js | 2 +- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/lib/injector.js b/lib/injector.js index b722ae5..c549ae3 100644 --- a/lib/injector.js +++ b/lib/injector.js @@ -12,23 +12,23 @@ class Injector { } } - _getInjectable(fileName, hash, type){ - return `${this._getMethodIdentifier(fileName)}(${hash}); /* ${type} */ \n`; + _getInjectable(id, hash, type){ + return `${this._getMethodIdentifier(id)}(${hash}); /* ${type} */ \n`; } - _getHash(fileName) { + _getHash(id) { this.hashCounter++; - return web3Utils.keccak256(`${fileName}:${this.hashCounter}`); + return web3Utils.keccak256(`${id}:${this.hashCounter}`); } - _getMethodIdentifier(fileName){ - return `coverage_${web3Utils.keccak256(fileName).slice(0,10)}` + _getMethodIdentifier(id){ + 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 hash = this._getHash(fileName) - const injectable = this._getInjectable(fileName, hash, type); + const hash = this._getHash(id) + const injectable = this._getInjectable(id, hash, type); return { start: start, @@ -41,25 +41,26 @@ class Injector { /** * Generates a solidity statement injection. Declared once per fn. * Definition is the same for every fn in file. - * @param {String} fileName + * @param {String} id * @return {String} ex: bytes32[1] memory _sc_82e0891 */ - _getHashMethodDefinition(fileName){ - const hash = web3Utils.keccak256(fileName).slice(0,10); - const method = this._getMethodIdentifier(fileName); + _getHashMethodDefinition(id, contract){ + const hash = web3Utils.keccak256(id).slice(0,10); + const method = this._getMethodIdentifier(id); return `\nfunction ${method}(bytes32 c__${hash}) public pure {}\n`; } injectLine(contract, fileName, injectionPoint, injection, instrumentation){ const type = 'line'; const { start, end } = this._split(contract, injectionPoint); + const id = `${fileName}:${injection.contractName}`; const newLines = start.match(/\n/g); const linecount = ( newLines || []).length + 1; contract.runnableLines.push(linecount); - const hash = this._getHash(fileName) - const injectable = this._getInjectable(fileName, hash, type); + const hash = this._getHash(id) + const injectable = this._getInjectable(id, hash, type); instrumentation[hash] = { id: linecount, @@ -73,13 +74,14 @@ class Injector { injectStatement(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'statement'; + const id = `${fileName}:${injection.contractName}`; const { start, end, hash, injectable - } = this._getInjectionComponents(contract, injectionPoint, fileName, type); + } = this._getInjectionComponents(contract, injectionPoint, id, type); instrumentation[hash] = { id: injection.statementId, @@ -93,13 +95,14 @@ class Injector { injectFunction(contract, fileName, injectionPoint, injection, instrumentation){ const type = 'function'; + const id = `${fileName}:${injection.contractName}`; const { start, end, hash, injectable - } = this._getInjectionComponents(contract, injectionPoint, fileName, type); + } = this._getInjectionComponents(contract, injectionPoint, id, type); instrumentation[hash] = { id: injection.fnId, @@ -113,13 +116,14 @@ class Injector { injectBranch(contract, fileName, injectionPoint, injection, instrumentation){ const type = 'branch'; + const id = `${fileName}:${injection.contractName}`; const { start, end, hash, injectable - } = this._getInjectionComponents(contract, injectionPoint, fileName, type); + } = this._getInjectionComponents(contract, injectionPoint, id, type); instrumentation[hash] = { id: injection.branchId, @@ -134,13 +138,14 @@ class Injector { injectEmptyBranch(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'branch'; + const id = `${fileName}:${injection.contractName}`; const { start, end, hash, injectable - } = this._getInjectionComponents(contract, injectionPoint, fileName, type); + } = this._getInjectionComponents(contract, injectionPoint, id, type); instrumentation[hash] = { id: injection.branchId, @@ -155,13 +160,14 @@ class Injector { injectAssertPre(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'assertPre'; + const id = `${fileName}:${injection.contractName}`; const { start, end, hash, injectable - } = this._getInjectionComponents(contract, injectionPoint, fileName, type); + } = this._getInjectionComponents(contract, injectionPoint, id, type); instrumentation[hash] = { id: injection.branchId, @@ -175,13 +181,14 @@ class Injector { injectAssertPost(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'assertPost'; + const id = `${fileName}:${injection.contractName}`; const { start, end, hash, injectable - } = this._getInjectionComponents(contract, injectionPoint, fileName, type); + } = this._getInjectionComponents(contract, injectionPoint, id, type); instrumentation[hash] = { id: injection.branchId, @@ -196,7 +203,8 @@ class Injector { injectHashMethod(contract, fileName, injectionPoint, injection, instrumentation){ const start = contract.instrumented.slice(0, 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}`; } }; diff --git a/lib/parse.js b/lib/parse.js index 3d561fb..f7bfca3 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -72,9 +72,11 @@ parse.ContractOrLibraryStatement = function(contract, expression) { const end = contract.instrumented.slice(start).indexOf('{') + 1; const loc = start + end;; + contract.contractName = expression.name; + (contract.injectionPoints[loc]) - ? contract.injectionPoints[loc].push({ type: 'injectHashMethod'}) - : contract.injectionPoints[loc] = [{ type: 'injectHashMethod'}]; + ? contract.injectionPoints[loc].push({ type: 'injectHashMethod', contractName: expression.name}) + : contract.injectionPoints[loc] = [{ type: 'injectHashMethod', contractName: expression.name}]; } if (expression.subNodes) { diff --git a/lib/registrar.js b/lib/registrar.js index 8187584..aaa794e 100644 --- a/lib/registrar.js +++ b/lib/registrar.js @@ -14,6 +14,8 @@ class Registrar { * @param {Number} value injection point `id` */ _createInjectionPoint(contract, key, value) { + value.contractName = contract.contractName; + (contract.injectionPoints[key]) ? contract.injectionPoints[key].push(value) : contract.injectionPoints[key] = [value]; diff --git a/package.json b/package.json index 4391a83..cd2aebb 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "scripts": { "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:debug": "node --max-old-space-size=4096 ./node_modules/.bin/mocha test/units/* --timeout 100000 --no-warnings --exit" }, diff --git a/test/integration/projects/solc-6/contracts/ContractA.sol b/test/integration/projects/solc-6/contracts/ContractA.sol index c490583..06b987f 100644 --- a/test/integration/projects/solc-6/contracts/ContractA.sol +++ b/test/integration/projects/solc-6/contracts/ContractA.sol @@ -57,3 +57,10 @@ contract ContractA is ContractB { //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++; + } +} diff --git a/test/units/buidler/standard.js b/test/units/buidler/standard.js index 02c1c72..1eb66fd 100644 --- a/test/units/buidler/standard.js +++ b/test/units/buidler/standard.js @@ -284,7 +284,7 @@ describe('Buidler Plugin: standard use cases', function() { const expected = [ { file: mock.pathToContract(buidlerConfig, 'ContractA.sol'), - pct: 100 + pct: 87.5 }, { file: mock.pathToContract(buidlerConfig, 'ContractB.sol'),