Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
export const coverageHandler: SingleFileSubtraceHandler = (
contractData: ContractData,
subtrace: Subtrace,
pcToSourceRange: { [programCounter: number]: SourceRange },
fileIndex: number,
): Coverage => {
const absoluteFileName = contractData.sources[fileIndex];
const coverageEntriesDescription = collectCoverageEntries(contractData.sourceCodes[fileIndex], IGNORE_REGEXP);
// if the source wasn't provided for the fileIndex, we can't cover the file
if (_.isUndefined(coverageEntriesDescription)) {
return {};
}
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 comparison. 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 branchIndexToIsBranchCovered = _.map(branchDescription.locations, location => {
this._logger.error('Contract creation not supported');
continue;
}
const bytecode = await this._web3Wrapper.getContractCodeAsync(evmCallStackEntry.address);
const contractData = utils.getContractDataIfExists(this._contractsData, bytecode);
if (_.isUndefined(contractData)) {
const errMsg = isContractCreation
? `Unknown contract creation transaction`
: `Transaction to an unknown address: ${evmCallStackEntry.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,
);
// tslint:disable-next-line:no-unnecessary-initializer
let sourceRange: SourceRange | undefined = undefined;
let pc = evmCallStackEntry.structLog.pc;
// Sometimes there is not a mapping for this pc (e.g. if the revert
// actually happens in assembly). In that case, we want to keep
// searching backwards by decrementing the pc until we find a
// mapped source range.
while (_.isUndefined(sourceRange) && pc > 0) {
sourceRange = pcToSourceRange[pc];
pc -= 1;
}
protected async _recordTxTraceAsync(address: string, data: string | undefined, txHash: string): Promise {
await this._web3Wrapper.awaitTransactionMinedAsync(txHash, 0);
const trace = await this._web3Wrapper.getTransactionTraceAsync(txHash, {
disableMemory: true,
disableStack: false,
disableStorage: true,
});
const evmCallStack = getRevertTrace(trace.structLogs, address);
if (evmCallStack.length > 0) {
// if getRevertTrace returns a call stack it means there was a
// revert.
await this._printStackTraceAsync(evmCallStack);
}
}
private async _printStackTraceAsync(evmCallStack: EvmCallStack): Promise {
while (_.isUndefined(sourceRange) && pc > 0) {
sourceRange = pcToSourceRange[pc];
pc -= 1;
}
if (_.isUndefined(sourceRange)) {
this._logger.warn(
`could not find matching sourceRange for structLog: ${JSON.stringify(
_.omit(evmCallStackEntry.structLog, 'stack'),
)}`,
);
continue;
}
const fileNameToFileIndex = _.invert(contractData.sources);
const fileIndex = _.parseInt(fileNameToFileIndex[sourceRange.fileName]);
const sourceSnippet = getSourceRangeSnippet(sourceRange, contractData.sourceCodes[fileIndex]);
sourceSnippets.push(sourceSnippet);
}
const filteredSnippets = filterSnippets(sourceSnippets);
if (filteredSnippets.length > 0) {
this._logger.error('\n\nStack trace for REVERT:\n');
_.forEach(_.reverse(filteredSnippets), snippet => {
const traceString = getStackTraceString(snippet);
this._logger.error(traceString);
});
this._logger.error('\n');
} else {
this._logger.error('REVERT detected but could not determine stack trace');
}
}
}
const isFunctionCovered = _.some(sourceRanges, range =>
utils.isRangeInside(range.location, functionDescription.loc),
);
(statementDescription: StatementDescription, statementId: number) => {
const isInsideTheModifierEnclosingFunction = utils.isRangeInside(
statementDescription,
enclosingFunction.loc,
);
const isCovered = statementCoverage[statementId];
return isInsideTheModifierEnclosingFunction && isCovered;
},
);
_.map(subtrace, structLog => {
const sourceRange = pcToSourceRange[structLog.pc];
if (_.isUndefined(sourceRange)) {
return 0;
}
if (sourceRange.fileName !== absoluteFileName) {
return 0;
}
if (utils.isRangeInside(sourceRange.location, statementDescription)) {
return structLog.gasCost;
} else {
return 0;
}
}),
);
const isStatementCovered = _.some(sourceRanges, range =>
utils.isRangeInside(range.location, statementDescription),
);
const enclosingFunction = _.find(coverageEntriesDescription.fnMap, functionDescription =>
utils.isRangeInside(modifierDescription, functionDescription.loc),
) as FunctionDescription;
const isBranchCovered = _.some(sourceRanges, range => utils.isRangeInside(range.location, location));
const timesBranchCovered = Number(isBranchCovered);