|
|
@ -1,20 +1,19 @@ |
|
|
|
const sha1 = require("sha1"); |
|
|
|
|
|
|
|
const web3Utils = require("web3-utils"); |
|
|
|
const web3Utils = require("web3-utils"); |
|
|
|
|
|
|
|
|
|
|
|
class Injector { |
|
|
|
class Injector { |
|
|
|
constructor(){ |
|
|
|
constructor(){ |
|
|
|
this.hashCounter = 0; |
|
|
|
this.hashCounter = 0; |
|
|
|
this.definitionCounter = 0; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
_split(contract, injectionPoint){ |
|
|
|
* Generates solidity statement to inject for line, stmt, branch, fn 'events' |
|
|
|
return { |
|
|
|
* @param {String} memoryVariable |
|
|
|
start: contract.instrumented.slice(0, injectionPoint), |
|
|
|
* @param {String} hash hash key to an instrumentationData entry (see _getHash) |
|
|
|
end: contract.instrumented.slice(injectionPoint) |
|
|
|
* @param {String} type instrumentation type, e.g. line, statement |
|
|
|
} |
|
|
|
// @return {String} ex: _sc_82e0891[0] = bytes32(0xdc08...08ed1); /* function */
|
|
|
|
} |
|
|
|
_getInjectable(memoryVariable, hash, type){ |
|
|
|
|
|
|
|
return `${memoryVariable}[0] = bytes32(${hash}); /* ${type} */ \n`; |
|
|
|
_getInjectable(fileName, hash, type){ |
|
|
|
|
|
|
|
return `${this._getMethodIdentifier(fileName)}(${hash}); /* ${type} */ \n`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_getHash(fileName) { |
|
|
|
_getHash(fileName) { |
|
|
@ -22,34 +21,45 @@ class Injector { |
|
|
|
return web3Utils.keccak256(`${fileName}:${this.hashCounter}`); |
|
|
|
return web3Utils.keccak256(`${fileName}:${this.hashCounter}`); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_getMethodIdentifier(fileName){ |
|
|
|
|
|
|
|
return `coverage_${web3Utils.keccak256(fileName).slice(0,10)}` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_getInjectionComponents(contract, injectionPoint, fileName, type){ |
|
|
|
|
|
|
|
const { start, end } = this._split(contract, injectionPoint); |
|
|
|
|
|
|
|
const hash = this._getHash(fileName) |
|
|
|
|
|
|
|
const injectable = this._getInjectable(fileName, hash, type); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
start: start, |
|
|
|
|
|
|
|
end: end, |
|
|
|
|
|
|
|
hash: hash, |
|
|
|
|
|
|
|
injectable: injectable |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 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} fileName |
|
|
|
* @return {String} ex: bytes32[1] memory _sc_82e0891 |
|
|
|
* @return {String} ex: bytes32[1] memory _sc_82e0891 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
_getMemoryVariableDefinition(fileName){ |
|
|
|
_getHashMethodDefinition(fileName){ |
|
|
|
this.definitionCounter++; |
|
|
|
const hash = web3Utils.keccak256(fileName).slice(0,10); |
|
|
|
return `\nbytes32[1] memory _sc_${sha1(fileName).slice(0,7)};\n`; |
|
|
|
const method = this._getMethodIdentifier(fileName); |
|
|
|
} |
|
|
|
return `\nfunction ${method}(bytes32 c__${hash}) public pure {}\n`; |
|
|
|
|
|
|
|
|
|
|
|
_getMemoryVariableAssignment(fileName){ |
|
|
|
|
|
|
|
return `\n_sc_${sha1(fileName).slice(0,7)}`; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
injectLine(contract, fileName, injectionPoint, injection, instrumentation){ |
|
|
|
injectLine(contract, fileName, injectionPoint, injection, instrumentation){ |
|
|
|
const type = 'line'; |
|
|
|
const type = 'line'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
const { start, end } = this._split(contract, injectionPoint); |
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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(fileName) |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
const injectable = this._getInjectable(fileName, hash, type); |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash , type) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: linecount, |
|
|
|
id: linecount, |
|
|
@ -63,12 +73,13 @@ class Injector { |
|
|
|
|
|
|
|
|
|
|
|
injectStatement(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
injectStatement(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
const type = 'statement'; |
|
|
|
const type = 'statement'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
|
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hash = this._getHash(fileName); |
|
|
|
const { |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
start, |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash, type) |
|
|
|
end, |
|
|
|
|
|
|
|
hash, |
|
|
|
|
|
|
|
injectable |
|
|
|
|
|
|
|
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); |
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: injection.statementId, |
|
|
|
id: injection.statementId, |
|
|
@ -82,13 +93,13 @@ class Injector { |
|
|
|
|
|
|
|
|
|
|
|
injectFunction(contract, fileName, injectionPoint, injection, instrumentation){ |
|
|
|
injectFunction(contract, fileName, injectionPoint, injection, instrumentation){ |
|
|
|
const type = 'function'; |
|
|
|
const type = 'function'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
|
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hash = this._getHash(fileName); |
|
|
|
const { |
|
|
|
const memoryVariableDefinition = this._getMemoryVariableDefinition(fileName); |
|
|
|
start, |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
end, |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash, type); |
|
|
|
hash, |
|
|
|
|
|
|
|
injectable |
|
|
|
|
|
|
|
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); |
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: injection.fnId, |
|
|
|
id: injection.fnId, |
|
|
@ -97,17 +108,18 @@ class Injector { |
|
|
|
hits: 0 |
|
|
|
hits: 0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
contract.instrumented = `${start}${memoryVariableDefinition}${injectable}${end}`; |
|
|
|
contract.instrumented = `${start}${injectable}${end}`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
injectBranch(contract, fileName, injectionPoint, injection, instrumentation){ |
|
|
|
injectBranch(contract, fileName, injectionPoint, injection, instrumentation){ |
|
|
|
const type = 'branch'; |
|
|
|
const type = 'branch'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
|
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hash = this._getHash(fileName); |
|
|
|
const { |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
start, |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash, type); |
|
|
|
end, |
|
|
|
|
|
|
|
hash, |
|
|
|
|
|
|
|
injectable |
|
|
|
|
|
|
|
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); |
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: injection.branchId, |
|
|
|
id: injection.branchId, |
|
|
@ -122,12 +134,13 @@ class Injector { |
|
|
|
|
|
|
|
|
|
|
|
injectEmptyBranch(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
injectEmptyBranch(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
const type = 'branch'; |
|
|
|
const type = 'branch'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
|
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hash = this._getHash(fileName); |
|
|
|
const { |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
start, |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash, type); |
|
|
|
end, |
|
|
|
|
|
|
|
hash, |
|
|
|
|
|
|
|
injectable |
|
|
|
|
|
|
|
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); |
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: injection.branchId, |
|
|
|
id: injection.branchId, |
|
|
@ -142,12 +155,13 @@ class Injector { |
|
|
|
|
|
|
|
|
|
|
|
injectAssertPre(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
injectAssertPre(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
const type = 'assertPre'; |
|
|
|
const type = 'assertPre'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
|
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hash = this._getHash(fileName); |
|
|
|
const { |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
start, |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash, type); |
|
|
|
end, |
|
|
|
|
|
|
|
hash, |
|
|
|
|
|
|
|
injectable |
|
|
|
|
|
|
|
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); |
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: injection.branchId, |
|
|
|
id: injection.branchId, |
|
|
@ -161,12 +175,13 @@ class Injector { |
|
|
|
|
|
|
|
|
|
|
|
injectAssertPost(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
injectAssertPost(contract, fileName, injectionPoint, injection, instrumentation) { |
|
|
|
const type = 'assertPost'; |
|
|
|
const type = 'assertPost'; |
|
|
|
const start = contract.instrumented.slice(0, injectionPoint); |
|
|
|
|
|
|
|
const end = contract.instrumented.slice(injectionPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hash = this._getHash(fileName); |
|
|
|
const { |
|
|
|
const memoryVariable = this._getMemoryVariableAssignment(fileName); |
|
|
|
start, |
|
|
|
const injectable = this._getInjectable(memoryVariable, hash, type); |
|
|
|
end, |
|
|
|
|
|
|
|
hash, |
|
|
|
|
|
|
|
injectable |
|
|
|
|
|
|
|
} = this._getInjectionComponents(contract, injectionPoint, fileName, type); |
|
|
|
|
|
|
|
|
|
|
|
instrumentation[hash] = { |
|
|
|
instrumentation[hash] = { |
|
|
|
id: injection.branchId, |
|
|
|
id: injection.branchId, |
|
|
@ -177,6 +192,12 @@ class Injector { |
|
|
|
|
|
|
|
|
|
|
|
contract.instrumented = `${start}${injectable}${end}`; |
|
|
|
contract.instrumented = `${start}${injectable}${end}`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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}`; |
|
|
|
|
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
module.exports = Injector; |
|
|
|
module.exports = Injector; |
|
|
|