From 9778695b4ad1fd999eb79b01c768a2f2b9938917 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 4 Jun 2018 19:48:21 -0700 Subject: Try enabling no-unused-variable... --- packages/sol-cov/src/coverage_manager.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'packages/sol-cov/src/coverage_manager.ts') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index 31b0e6fbc..064338a32 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -1,11 +1,10 @@ import { promisify } from '@0xproject/utils'; -import { addHexPrefix, stripHexPrefix } from 'ethereumjs-util'; +import { stripHexPrefix } from 'ethereumjs-util'; import * as fs from 'fs'; import { Collector } from 'istanbul'; import * as _ from 'lodash'; -import { getLogger, levels, Logger, LogLevel } from 'loglevel'; +import { getLogger, levels, Logger } from 'loglevel'; import * as mkdirp from 'mkdirp'; -import * as path from 'path'; import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; import { collectCoverageEntries } from './collect_coverage_entries'; @@ -14,18 +13,14 @@ import { parseSourceMap } from './source_maps'; import { BranchCoverage, BranchDescription, - BranchMap, ContractData, Coverage, - FnMap, FunctionCoverage, FunctionDescription, - LineColumn, SingleFileSourceRange, SourceRange, StatementCoverage, StatementDescription, - StatementMap, TraceInfo, TraceInfoExistingContract, TraceInfoNewContract, @@ -38,6 +33,7 @@ export class CoverageManager { private _artifactAdapter: AbstractArtifactAdapter; private _logger: Logger; private _traceInfos: TraceInfo[] = []; + // tslint:disable-next-line:no-unused-variable private _getContractCodeAsync: (address: string) => Promise; private static _getSingleFileCoverageForTrace( contractData: ContractData, -- cgit v1.2.3 From 760bab8f866ec3d5fc7627ce9bbf5c2eaaef1f36 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 8 Jun 2018 11:18:32 -0700 Subject: Implement SolidityProfiler & adapt sol-cov to work with Geth --- packages/sol-cov/src/coverage_manager.ts | 154 ++++++++++++------------------- 1 file changed, 59 insertions(+), 95 deletions(-) (limited to 'packages/sol-cov/src/coverage_manager.ts') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index 064338a32..3ab363b52 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -21,6 +21,7 @@ import { SourceRange, StatementCoverage, StatementDescription, + Subtrace, TraceInfo, TraceInfoExistingContract, TraceInfoNewContract, @@ -29,21 +30,30 @@ import { utils } from './utils'; const mkdirpAsync = promisify(mkdirp); +/** + * CoverageManager is used by CoverageSubprovider to compute code coverage based on collected trace data. + */ export class CoverageManager { private _artifactAdapter: AbstractArtifactAdapter; private _logger: Logger; private _traceInfos: TraceInfo[] = []; - // tslint:disable-next-line:no-unused-variable - private _getContractCodeAsync: (address: string) => Promise; - private static _getSingleFileCoverageForTrace( + /** + * Computed partial coverage for a single file & subtrace + * @param contractData Contract metadata (source, srcMap, bytecode) + * @param subtrace A subset of a transcation/call trace that was executed within that contract + * @param pcToSourceRange A mapping from program counters to source ranges + * @param fileIndex Index of a file to compute coverage for + * @return Partial istanbul coverage for that file & subtrace + */ + private static _getSingleFileCoverageForSubtrace( contractData: ContractData, - coveredPcs: number[], + subtrace: Subtrace, pcToSourceRange: { [programCounter: number]: SourceRange }, fileIndex: number, ): Coverage { const absoluteFileName = contractData.sources[fileIndex]; const coverageEntriesDescription = collectCoverageEntries(contractData.sourceCodes[fileIndex]); - let sourceRanges = _.map(coveredPcs, coveredPc => pcToSourceRange[coveredPc]); + let sourceRanges = _.map(subtrace, structLog => pcToSourceRange[structLog.pc]); sourceRanges = _.compact(sourceRanges); // Some PC's don't map to a source range and we just ignore them. // By default lodash does a shallow object comparasion. We JSON.stringify them and compare as strings. sourceRanges = _.uniqBy(sourceRanges, s => JSON.stringify(s)); // We don't care if one PC was covered multiple times within a single transaction @@ -52,26 +62,32 @@ export class CoverageManager { const branchIds = _.keys(coverageEntriesDescription.branchMap); for (const branchId of branchIds) { const branchDescription = coverageEntriesDescription.branchMap[branchId]; - const isCoveredByBranchIndex = _.map(branchDescription.locations, location => - _.some(sourceRanges, range => utils.isRangeInside(range.location, location)), - ); - branchCoverage[branchId] = isCoveredByBranchIndex; + const isBranchCoveredByBranchIndex = _.map(branchDescription.locations, location => { + const isBranchCovered = _.some(sourceRanges, range => utils.isRangeInside(range.location, location)); + const timesBranchCovered = Number(isBranchCovered); + return timesBranchCovered; + }); + branchCoverage[branchId] = isBranchCoveredByBranchIndex; } const statementCoverage: StatementCoverage = {}; const statementIds = _.keys(coverageEntriesDescription.statementMap); for (const statementId of statementIds) { const statementDescription = coverageEntriesDescription.statementMap[statementId]; - const isCovered = _.some(sourceRanges, range => utils.isRangeInside(range.location, statementDescription)); - statementCoverage[statementId] = isCovered; + const isStatementCovered = _.some(sourceRanges, range => + utils.isRangeInside(range.location, statementDescription), + ); + const timesStatementCovered = Number(isStatementCovered); + statementCoverage[statementId] = timesStatementCovered; } const functionCoverage: FunctionCoverage = {}; const functionIds = _.keys(coverageEntriesDescription.fnMap); for (const fnId of functionIds) { const functionDescription = coverageEntriesDescription.fnMap[fnId]; - const isCovered = _.some(sourceRanges, range => + const isFunctionCovered = _.some(sourceRanges, range => utils.isRangeInside(range.location, functionDescription.loc), ); - functionCoverage[fnId] = isCovered; + const timesFunctionCovered = Number(isFunctionCovered); + functionCoverage[fnId] = timesFunctionCovered; } // HACK: Solidity doesn't emit any opcodes that map back to modifiers with no args, that's why we map back to the // function range and check if there is any covered statement within that range. @@ -95,12 +111,12 @@ export class CoverageManager { return isInsideTheModifierEnclosingFunction && isCovered; }, ); - statementCoverage[modifierStatementId] = isModifierCovered; + const timesModifierCovered = Number(isModifierCovered); + statementCoverage[modifierStatementId] = timesModifierCovered; } const partialCoverage = { [absoluteFileName]: { ...coverageEntriesDescription, - l: {}, // It's able to derive it from statement coverage path: absoluteFileName, f: functionCoverage, s: statementCoverage, @@ -109,37 +125,7 @@ export class CoverageManager { }; return partialCoverage; } - private static _bytecodeToBytecodeRegex(bytecode: string): string { - const bytecodeRegex = bytecode - // Library linking placeholder: __ConvertLib____________________________ - .replace(/_.*_/, '.*') - // Last 86 characters is solidity compiler metadata that's different between compilations - .replace(/.{86}$/, '') - // Libraries contain their own address at the beginning of the code and it's impossible to know it in advance - .replace(/^0x730000000000000000000000000000000000000000/, '0x73........................................'); - return bytecodeRegex; - } - private static _getContractDataIfExists(contractsData: ContractData[], bytecode: string): ContractData | undefined { - if (!bytecode.startsWith('0x')) { - throw new Error(`0x hex prefix missing: ${bytecode}`); - } - const contractData = _.find(contractsData, contractDataCandidate => { - const bytecodeRegex = CoverageManager._bytecodeToBytecodeRegex(contractDataCandidate.bytecode); - const runtimeBytecodeRegex = CoverageManager._bytecodeToBytecodeRegex( - contractDataCandidate.runtimeBytecode, - ); - // We use that function to find by bytecode or runtimeBytecode. Those are quasi-random strings so - // collisions are practically impossible and it allows us to reuse that code - return !_.isNull(bytecode.match(bytecodeRegex)) || !_.isNull(bytecode.match(runtimeBytecodeRegex)); - }); - return contractData; - } - constructor( - artifactAdapter: AbstractArtifactAdapter, - getContractCodeAsync: (address: string) => Promise, - isVerbose: boolean, - ) { - this._getContractCodeAsync = getContractCodeAsync; + constructor(artifactAdapter: AbstractArtifactAdapter, isVerbose: boolean) { this._artifactAdapter = artifactAdapter; this._logger = getLogger('sol-cov'); this._logger.setLevel(isVerbose ? levels.TRACE : levels.ERROR); @@ -157,56 +143,34 @@ export class CoverageManager { const contractsData = await this._artifactAdapter.collectContractsDataAsync(); const collector = new Collector(); for (const traceInfo of this._traceInfos) { - if (traceInfo.address !== constants.NEW_CONTRACT) { - // Runtime transaction - const runtimeBytecode = (traceInfo as TraceInfoExistingContract).runtimeBytecode; - const contractData = CoverageManager._getContractDataIfExists(contractsData, runtimeBytecode); - if (_.isUndefined(contractData)) { - this._logger.warn(`Transaction to an unknown address: ${traceInfo.address}`); - continue; - } - const bytecodeHex = stripHexPrefix(runtimeBytecode); - const sourceMap = contractData.sourceMapRuntime; - const pcToSourceRange = parseSourceMap( - contractData.sourceCodes, - sourceMap, - bytecodeHex, - contractData.sources, - ); - for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForTrace( - contractData, - traceInfo.coveredPcs, - pcToSourceRange, - fileIndex, - ); - collector.add(singleFileCoverageForTrace); - } - } else { - // Contract creation transaction - const bytecode = (traceInfo as TraceInfoNewContract).bytecode; - const contractData = CoverageManager._getContractDataIfExists(contractsData, bytecode); - if (_.isUndefined(contractData)) { - this._logger.warn(`Unknown contract creation transaction`); - continue; - } - const bytecodeHex = stripHexPrefix(bytecode); - const sourceMap = contractData.sourceMap; - const pcToSourceRange = parseSourceMap( - contractData.sourceCodes, - sourceMap, - bytecodeHex, - contractData.sources, + const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; + const bytecode = isContractCreation + ? (traceInfo as TraceInfoNewContract).bytecode + : (traceInfo as TraceInfoExistingContract).runtimeBytecode; + const contractData = utils.getContractDataIfExists(contractsData, bytecode); + if (_.isUndefined(contractData)) { + const errMsg = isContractCreation + ? `Unknown contract creation transaction` + : `Transaction to an unknown address: ${traceInfo.address}`; + this._logger.warn(errMsg); + continue; + } + const bytecodeHex = stripHexPrefix(bytecode); + const sourceMap = isContractCreation ? contractData.sourceMap : contractData.sourceMapRuntime; + const pcToSourceRange = parseSourceMap( + contractData.sourceCodes, + sourceMap, + bytecodeHex, + contractData.sources, + ); + for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { + const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForSubtrace( + contractData, + traceInfo.subtrace, + pcToSourceRange, + fileIndex, ); - for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForTrace( - contractData, - traceInfo.coveredPcs, - pcToSourceRange, - fileIndex, - ); - collector.add(singleFileCoverageForTrace); - } + collector.add(singleFileCoverageForTrace); } } return collector.getFinalCoverage(); -- cgit v1.2.3 From 33f066910021bc4969f4d564fc1648f6769ee3ec Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 12 Jun 2018 12:42:14 -0700 Subject: Refactor sol-cov to avoid keeping traceInfo in memory --- packages/sol-cov/src/coverage_manager.ts | 67 ++++++++++++++------------------ 1 file changed, 29 insertions(+), 38 deletions(-) (limited to 'packages/sol-cov/src/coverage_manager.ts') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index 3ab363b52..a55967243 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -36,7 +36,8 @@ const mkdirpAsync = promisify(mkdirp); export class CoverageManager { private _artifactAdapter: AbstractArtifactAdapter; private _logger: Logger; - private _traceInfos: TraceInfo[] = []; + private _contractsData!: ContractData[]; + private _collector = new Collector(); /** * Computed partial coverage for a single file & subtrace * @param contractData Contract metadata (source, srcMap, bytecode) @@ -130,49 +131,39 @@ export class CoverageManager { this._logger = getLogger('sol-cov'); this._logger.setLevel(isVerbose ? levels.TRACE : levels.ERROR); } - public appendTraceInfo(traceInfo: TraceInfo): void { - this._traceInfos.push(traceInfo); - } public async writeCoverageAsync(): Promise { - const finalCoverage = await this._computeCoverageAsync(); + const finalCoverage = this._collector.getFinalCoverage(); const stringifiedCoverage = JSON.stringify(finalCoverage, null, '\t'); await mkdirpAsync('coverage'); fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); } - private async _computeCoverageAsync(): Promise { - const contractsData = await this._artifactAdapter.collectContractsDataAsync(); - const collector = new Collector(); - for (const traceInfo of this._traceInfos) { - const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; - const bytecode = isContractCreation - ? (traceInfo as TraceInfoNewContract).bytecode - : (traceInfo as TraceInfoExistingContract).runtimeBytecode; - const contractData = utils.getContractDataIfExists(contractsData, bytecode); - if (_.isUndefined(contractData)) { - const errMsg = isContractCreation - ? `Unknown contract creation transaction` - : `Transaction to an unknown address: ${traceInfo.address}`; - this._logger.warn(errMsg); - continue; - } - const bytecodeHex = stripHexPrefix(bytecode); - const sourceMap = isContractCreation ? contractData.sourceMap : contractData.sourceMapRuntime; - const pcToSourceRange = parseSourceMap( - contractData.sourceCodes, - sourceMap, - bytecodeHex, - contractData.sources, + public async computeCoverageAsync(traceInfo: TraceInfo): Promise { + if (_.isUndefined(this._contractsData)) { + this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); + } + const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; + const bytecode = isContractCreation + ? (traceInfo as TraceInfoNewContract).bytecode + : (traceInfo as TraceInfoExistingContract).runtimeBytecode; + const contractData = utils.getContractDataIfExists(this._contractsData, bytecode); + if (_.isUndefined(contractData)) { + const errMsg = isContractCreation + ? `Unknown contract creation transaction` + : `Transaction to an unknown address: ${traceInfo.address}`; + this._logger.warn(errMsg); + return; + } + const bytecodeHex = stripHexPrefix(bytecode); + const sourceMap = isContractCreation ? contractData.sourceMap : contractData.sourceMapRuntime; + const pcToSourceRange = parseSourceMap(contractData.sourceCodes, sourceMap, bytecodeHex, contractData.sources); + for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { + const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForSubtrace( + contractData, + traceInfo.subtrace, + pcToSourceRange, + fileIndex, ); - for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForSubtrace( - contractData, - traceInfo.subtrace, - pcToSourceRange, - fileIndex, - ); - collector.add(singleFileCoverageForTrace); - } + this._collector.add(singleFileCoverageForTrace); } - return collector.getFinalCoverage(); } } -- cgit v1.2.3 From 627ea6c860e97a6e04877f10f9f842c5d5a907d6 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 12 Jun 2018 14:11:11 -0700 Subject: Rename computeCoverageAsync -> computeSingleTraceCoverageAsync --- packages/sol-cov/src/coverage_manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/sol-cov/src/coverage_manager.ts') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index a55967243..673a3e600 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -137,7 +137,7 @@ export class CoverageManager { await mkdirpAsync('coverage'); fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); } - public async computeCoverageAsync(traceInfo: TraceInfo): Promise { + public async computeSingleTraceCoverageAsync(traceInfo: TraceInfo): Promise { if (_.isUndefined(this._contractsData)) { this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); } -- cgit v1.2.3 From d0c348e5957d2adcd5b6f7b0727a5afd326e3b33 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 12 Jun 2018 15:27:08 -0700 Subject: Refactor sol-cov to de-duplicate code for coverage and profiling --- packages/sol-cov/src/coverage_manager.ts | 169 ------------------------------- 1 file changed, 169 deletions(-) delete mode 100644 packages/sol-cov/src/coverage_manager.ts (limited to 'packages/sol-cov/src/coverage_manager.ts') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts deleted file mode 100644 index 673a3e600..000000000 --- a/packages/sol-cov/src/coverage_manager.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { promisify } from '@0xproject/utils'; -import { stripHexPrefix } from 'ethereumjs-util'; -import * as fs from 'fs'; -import { Collector } from 'istanbul'; -import * as _ from 'lodash'; -import { getLogger, levels, Logger } from 'loglevel'; -import * as mkdirp from 'mkdirp'; - -import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; -import { collectCoverageEntries } from './collect_coverage_entries'; -import { constants } from './constants'; -import { parseSourceMap } from './source_maps'; -import { - BranchCoverage, - BranchDescription, - ContractData, - Coverage, - FunctionCoverage, - FunctionDescription, - SingleFileSourceRange, - SourceRange, - StatementCoverage, - StatementDescription, - Subtrace, - TraceInfo, - TraceInfoExistingContract, - TraceInfoNewContract, -} from './types'; -import { utils } from './utils'; - -const mkdirpAsync = promisify(mkdirp); - -/** - * CoverageManager is used by CoverageSubprovider to compute code coverage based on collected trace data. - */ -export class CoverageManager { - private _artifactAdapter: AbstractArtifactAdapter; - private _logger: Logger; - private _contractsData!: ContractData[]; - private _collector = new Collector(); - /** - * Computed partial coverage for a single file & subtrace - * @param contractData Contract metadata (source, srcMap, bytecode) - * @param subtrace A subset of a transcation/call trace that was executed within that contract - * @param pcToSourceRange A mapping from program counters to source ranges - * @param fileIndex Index of a file to compute coverage for - * @return Partial istanbul coverage for that file & subtrace - */ - private static _getSingleFileCoverageForSubtrace( - contractData: ContractData, - subtrace: Subtrace, - pcToSourceRange: { [programCounter: number]: SourceRange }, - fileIndex: number, - ): Coverage { - const absoluteFileName = contractData.sources[fileIndex]; - const coverageEntriesDescription = collectCoverageEntries(contractData.sourceCodes[fileIndex]); - let sourceRanges = _.map(subtrace, structLog => pcToSourceRange[structLog.pc]); - sourceRanges = _.compact(sourceRanges); // Some PC's don't map to a source range and we just ignore them. - // By default lodash does a shallow object comparasion. We JSON.stringify them and compare as strings. - sourceRanges = _.uniqBy(sourceRanges, s => JSON.stringify(s)); // We don't care if one PC was covered multiple times within a single transaction - sourceRanges = _.filter(sourceRanges, sourceRange => sourceRange.fileName === absoluteFileName); - const branchCoverage: BranchCoverage = {}; - const branchIds = _.keys(coverageEntriesDescription.branchMap); - for (const branchId of branchIds) { - const branchDescription = coverageEntriesDescription.branchMap[branchId]; - const isBranchCoveredByBranchIndex = _.map(branchDescription.locations, location => { - const isBranchCovered = _.some(sourceRanges, range => utils.isRangeInside(range.location, location)); - const timesBranchCovered = Number(isBranchCovered); - return timesBranchCovered; - }); - branchCoverage[branchId] = isBranchCoveredByBranchIndex; - } - const statementCoverage: StatementCoverage = {}; - const statementIds = _.keys(coverageEntriesDescription.statementMap); - for (const statementId of statementIds) { - const statementDescription = coverageEntriesDescription.statementMap[statementId]; - const isStatementCovered = _.some(sourceRanges, range => - utils.isRangeInside(range.location, statementDescription), - ); - const timesStatementCovered = Number(isStatementCovered); - statementCoverage[statementId] = timesStatementCovered; - } - const functionCoverage: FunctionCoverage = {}; - const functionIds = _.keys(coverageEntriesDescription.fnMap); - for (const fnId of functionIds) { - const functionDescription = coverageEntriesDescription.fnMap[fnId]; - const isFunctionCovered = _.some(sourceRanges, range => - utils.isRangeInside(range.location, functionDescription.loc), - ); - const timesFunctionCovered = Number(isFunctionCovered); - functionCoverage[fnId] = timesFunctionCovered; - } - // HACK: Solidity doesn't emit any opcodes that map back to modifiers with no args, that's why we map back to the - // function range and check if there is any covered statement within that range. - for (const modifierStatementId of coverageEntriesDescription.modifiersStatementIds) { - if (statementCoverage[modifierStatementId]) { - // Already detected as covered - continue; - } - const modifierDescription = coverageEntriesDescription.statementMap[modifierStatementId]; - const enclosingFunction = _.find(coverageEntriesDescription.fnMap, functionDescription => - utils.isRangeInside(modifierDescription, functionDescription.loc), - ) as FunctionDescription; - const isModifierCovered = _.some( - coverageEntriesDescription.statementMap, - (statementDescription: StatementDescription, statementId: number) => { - const isInsideTheModifierEnclosingFunction = utils.isRangeInside( - statementDescription, - enclosingFunction.loc, - ); - const isCovered = statementCoverage[statementId]; - return isInsideTheModifierEnclosingFunction && isCovered; - }, - ); - const timesModifierCovered = Number(isModifierCovered); - statementCoverage[modifierStatementId] = timesModifierCovered; - } - const partialCoverage = { - [absoluteFileName]: { - ...coverageEntriesDescription, - path: absoluteFileName, - f: functionCoverage, - s: statementCoverage, - b: branchCoverage, - }, - }; - return partialCoverage; - } - constructor(artifactAdapter: AbstractArtifactAdapter, isVerbose: boolean) { - this._artifactAdapter = artifactAdapter; - this._logger = getLogger('sol-cov'); - this._logger.setLevel(isVerbose ? levels.TRACE : levels.ERROR); - } - public async writeCoverageAsync(): Promise { - const finalCoverage = this._collector.getFinalCoverage(); - const stringifiedCoverage = JSON.stringify(finalCoverage, null, '\t'); - await mkdirpAsync('coverage'); - fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); - } - public async computeSingleTraceCoverageAsync(traceInfo: TraceInfo): Promise { - if (_.isUndefined(this._contractsData)) { - this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); - } - const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; - const bytecode = isContractCreation - ? (traceInfo as TraceInfoNewContract).bytecode - : (traceInfo as TraceInfoExistingContract).runtimeBytecode; - const contractData = utils.getContractDataIfExists(this._contractsData, bytecode); - if (_.isUndefined(contractData)) { - const errMsg = isContractCreation - ? `Unknown contract creation transaction` - : `Transaction to an unknown address: ${traceInfo.address}`; - this._logger.warn(errMsg); - return; - } - const bytecodeHex = stripHexPrefix(bytecode); - const sourceMap = isContractCreation ? contractData.sourceMap : contractData.sourceMapRuntime; - const pcToSourceRange = parseSourceMap(contractData.sourceCodes, sourceMap, bytecodeHex, contractData.sources); - for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForSubtrace( - contractData, - traceInfo.subtrace, - pcToSourceRange, - fileIndex, - ); - this._collector.add(singleFileCoverageForTrace); - } - } -} -- cgit v1.2.3