Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 116x 116x 116x 116x 116x 116x 5563565x 5563565x 5563565x 1877548x 1877548x 1877548x 1877548x 1877548x 243898x 19694x 19694x 19694x 243898x 1877548x 458871x 458871x 236185x 236185x 236185x 236185x 1418677x 30863x 30864x 30863x 1877548x 116x 116x 3596x 3596x 116x 1856x 1856x 116x 80x 1x | /**
* Writes data from the VM step to the in-memory
* coverage map constructed by the Instrumenter.
*/
class DataCollector {
constructor(instrumentationData={}, viaIR){
this.instrumentationData = instrumentationData;
this.validOpcodes = this._getOpcodes(viaIR);
this.lastHash = null;
this.viaIR = viaIR;
this.pcZeroCounter = 0;
this.lastPcZeroCount = 0;
}
/**
* VM step event handler. Detects instrumentation hashes when they are pushed to the
* top of the stack. This runs millions of times - trying to keep it fast.
* @param {Object} info vm step info
*/
step(info){
if (info.pc === 0) this.pcZeroCounter++;
try {
if (this.validOpcodes[info.opcode.name] && info.stack.length > 0){
const idx = info.stack.length - 1;
let hash = '0x' + info.stack[idx].toString(16);
this._registerHash(hash);
}
} catch (err) { /*Ignore*/ };
}
/**
* Normalizes has string and marks hit.
* @param {String} hash bytes32 hash
*/
_registerHash(hash){
hash = this._normalizeHash(hash);
if(this.instrumentationData[hash]){
// abi.encode (used to circumvent viaIR) sometimes puts the hash on the stack twice
// We should only skip duplicate hashes *within* a transaction (see issue #863)
if (this.lastHash !== hash || this.lastPcZeroCount !== this.pcZeroCounter) {
this.lastHash = hash;
this.lastPcZeroCount = this.pcZeroCounter;
this.instrumentationData[hash].hits++
}
return;
}
}
/**
* Left-pads zero prefixed bytes8 hashes to length 18. The '11' in the
* comparison below is arbitrary. It provides a margin for recurring zeros
* but prevents left-padding shorter irrelevant hashes
*
* @param {String} hash data hash from evm stack.
* @return {String} 0x prefixed hash of length 18.
*/
_normalizeHash(hash){
// viaIR sometimes right-pads the hashes out to 32 bytes
// but it doesn't preserve leading zeroes when it does this
if (this.viaIR && hash.length >= 18) {
hash = hash.slice(0,18);
// Detect and recover from viaIR mangled hashes by left-padding single `0`
if(!this.instrumentationData[hash]) {
hash = hash.slice(2);
hash = '0' + hash;
hash = hash.slice(0,16);
hash = '0x' + hash;
}
} else if (hash.length < 18 && hash.length > 11){
hash = hash.slice(2);
while(hash.length < 16) hash = '0' + hash;
hash = '0x' + hash
}
return hash;
}
/**
* Generates a list of all the opcodes to inspect for instrumentation hashes
* When viaIR is true, it includes all DUPs and PUSHs, so things are a little slower.
* @param {boolean} viaIR
*/
_getOpcodes(viaIR) {
let opcodes = {
"PUSH1": true
};
for (let i = 2; i <= 32; i++) {
const key = "PUSH" + i;
opcodes[key] = viaIR;
};
for (let i = 1; i <= 16; i++ ) {
const key = "DUP" + i;
opcodes[key] = viaIR;
}
return opcodes;
}
/**
* Unit test helper
* @param {Object} data Instrumenter.instrumentationData
*/
_setInstrumentationData(data){
this.instrumentationData = data;
}
}
module.exports = DataCollector;
|