const sha1 = require("sha1"); const web3Utils = require("web3-utils"); class Injector { constructor(){ this.hashCounter = 0; this.definitionCounter = 0; } /** * Generates solidity statement to inject for line, stmt, branch, fn 'events' * @param {String} memoryVariable * @param {String} hash hash key to an instrumentationData entry (see _getHash) * @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`; } _getHash(fileName) { this.hashCounter++; return web3Utils.keccak256(`${fileName}:${this.hashCounter}`); } /** * Generates a solidity statement injection. Declared once per fn. * Definition is the same for every fn in file. * @param {String} fileName * @return {String} ex: bytes32[1] memory _sc_82e0891 */ _getMemoryVariableDefinition(fileName){ this.definitionCounter++; return `\nbytes32[1] memory _sc_${sha1(fileName).slice(0,7)};\n`; } _getMemoryVariableAssignment(fileName){ return `\n_sc_${sha1(fileName).slice(0,7)}`; } injectLine(contract, fileName, injectionPoint, injection, instrumentation){ const type = 'line'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const newLines = start.match(/\n/g); const linecount = ( newLines || []).length + 1; contract.runnableLines.push(linecount); const hash = this._getHash(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash , type) instrumentation[hash] = { id: linecount, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}${injectable}${end}`; } injectStatement(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'statement'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const hash = this._getHash(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash, type) instrumentation[hash] = { id: injection.statementId, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}${injectable}${end}`; }; injectFunction(contract, fileName, injectionPoint, injection, instrumentation){ const type = 'function'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const hash = this._getHash(fileName); const memoryVariableDefinition = this._getMemoryVariableDefinition(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash, type); instrumentation[hash] = { id: injection.fnId, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}${memoryVariableDefinition}${injectable}${end}`; } injectBranch(contract, fileName, injectionPoint, injection, instrumentation){ const type = 'branch'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const hash = this._getHash(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash, type); instrumentation[hash] = { id: injection.branchId, locationIdx: injection.locationIdx, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}${injectable}${end}`; } injectEmptyBranch(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'branch'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const hash = this._getHash(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash, type); instrumentation[hash] = { id: injection.branchId, locationIdx: injection.locationIdx, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}else { ${injectable}}${end}`; } injectAssertPre(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'assertPre'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const hash = this._getHash(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash, type); instrumentation[hash] = { id: injection.branchId, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}${injectable}${end}`; } injectAssertPost(contract, fileName, injectionPoint, injection, instrumentation) { const type = 'assertPost'; const start = contract.instrumented.slice(0, injectionPoint); const end = contract.instrumented.slice(injectionPoint); const hash = this._getHash(fileName); const memoryVariable = this._getMemoryVariableAssignment(fileName); const injectable = this._getInjectable(memoryVariable, hash, type); instrumentation[hash] = { id: injection.branchId, type: type, contractPath: fileName, hits: 0 } contract.instrumented = `${start}${injectable}${end}`; } }; module.exports = Injector;