 Chromium Code Reviews
 Chromium Code Reviews Issue 2745283002:
  DevTools: merge coverage segments from different instances of same URL  (Closed)
    
  
    Issue 2745283002:
  DevTools: merge coverage segments from different instances of same URL  (Closed) 
  | OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 /** @typedef {{startOffset: number, endOffset: number, count: number}} */ | 5 /** @typedef {{startOffset: number, endOffset: number, count: number}} */ | 
| 6 Coverage.RangeUseCount; | 6 Coverage.RangeUseCount; | 
| 7 | 7 | 
| 8 /** @typedef {{end: number, count: (number|undefined), depth: number}} */ | 8 /** @typedef {{end: number, count: (number|undefined), depth: number}} */ | 
| 9 Coverage.CoverageSegment; | 9 Coverage.CoverageSegment; | 
| 10 | 10 | 
| 11 /** @typedef {{ | |
| 12 * contentProvider: !Common.ContentProvider, | |
| 13 * size: number, | |
| 14 * unusedSize: number, | |
| 15 * usedSize: number, | |
| 16 * type: !Coverage.CoverageType, | |
| 17 * lineOffset: number, | |
| 18 * columnOffset: number, | |
| 19 * segments: !Array<!Coverage.CoverageSegment> | |
| 20 * }} | |
| 21 */ | |
| 22 Coverage.CoverageInfo; | |
| 23 | |
| 24 /** | 11 /** | 
| 25 * @enum {number} | 12 * @enum {number} | 
| 26 */ | 13 */ | 
| 27 Coverage.CoverageType = { | 14 Coverage.CoverageType = { | 
| 28 CSS: (1 << 0), | 15 CSS: (1 << 0), | 
| 29 JavaScript: (1 << 1), | 16 JavaScript: (1 << 1), | 
| 30 }; | 17 }; | 
| 31 | 18 | 
| 32 Coverage.CoverageModel = class extends SDK.SDKModel { | 19 Coverage.CoverageModel = class extends SDK.SDKModel { | 
| 33 /** | 20 /** | 
| 34 * @param {!SDK.Target} target | 21 * @param {!SDK.Target} target | 
| 35 */ | 22 */ | 
| 36 constructor(target) { | 23 constructor(target) { | 
| 37 super(target); | 24 super(target); | 
| 38 this._target = target; | 25 this._cpuProfilerModel = target.model(SDK.CPUProfilerModel); | 
| 39 this._cpuProfilerModel = this._target.model(SDK.CPUProfilerModel); | 26 this._cssModel = target.model(SDK.CSSModel); | 
| 40 this._cssModel = this._target.model(SDK.CSSModel); | 27 this._debuggerModel = target.model(SDK.DebuggerModel); | 
| 28 | |
| 29 /** @type {!Map<string, !Coverage.URLCoverageInfo>} */ | |
| 30 this._coverageByURL = new Map(); | |
| 41 } | 31 } | 
| 42 | 32 | 
| 43 /** | 33 /** | 
| 44 * @return {boolean} | 34 * @return {boolean} | 
| 45 */ | 35 */ | 
| 46 start() { | 36 start() { | 
| 37 this._coverageByURL.clear(); | |
| 47 if (this._cssModel) | 38 if (this._cssModel) | 
| 48 this._cssModel.startRuleUsageTracking(); | 39 this._cssModel.startRuleUsageTracking(); | 
| 49 if (this._cpuProfilerModel) | 40 if (this._cpuProfilerModel) | 
| 50 this._cpuProfilerModel.startPreciseCoverage(); | 41 this._cpuProfilerModel.startPreciseCoverage(); | 
| 51 return !!(this._cssModel || this._cpuProfilerModel); | 42 return !!(this._cssModel || this._cpuProfilerModel); | 
| 52 } | 43 } | 
| 53 | 44 | 
| 54 /** | 45 /** | 
| 55 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} | 46 * @return {!Promise<!Array<!Coverage.URLCoverageInfo>>} | 
| 56 */ | 47 */ | 
| 57 async stop() { | 48 async stop() { | 
| 58 var cssCoverageInfoPromise = this._stopCSSCoverage(); | 49 await Promise.all([this._stopCSSCoverage(), this._stopJSCoverage()]); | 
| 59 var jsCoverageInfoPromise = this._stopJSCoverage(); | 50 return Array.from(this._coverageByURL.values()); | 
| 60 var cssCoverageInfo = await cssCoverageInfoPromise; | |
| 61 var jsCoverageInfo = await jsCoverageInfoPromise; | |
| 62 return Coverage.CoverageModel._coalesceByURL(cssCoverageInfo.concat(jsCovera geInfo)); | |
| 63 } | 51 } | 
| 64 | 52 | 
| 65 /** | |
| 66 * @param {!Array<!Coverage.CoverageInfo>} coverageInfo | |
| 67 * @return {!Array<!Coverage.CoverageInfo>} | |
| 68 */ | |
| 69 static _coalesceByURL(coverageInfo) { | |
| 70 coverageInfo.sort((a, b) => (a.contentProvider.contentURL() || '').localeCom pare(b.contentProvider.contentURL())); | |
| 71 var result = []; | |
| 72 for (var entry of coverageInfo) { | |
| 73 var url = entry.contentProvider.contentURL(); | |
| 74 if (!url) | |
| 75 continue; | |
| 76 if (result.length && result.peekLast().contentProvider.contentURL() === ur l) { | |
| 77 var lastEntry = result.peekLast(); | |
| 78 lastEntry.size += entry.size; | |
| 79 lastEntry.usedSize += entry.usedSize; | |
| 80 lastEntry.unusedSize += entry.unusedSize; | |
| 81 lastEntry.type |= entry.type; | |
| 82 } else { | |
| 83 result.push(entry); | |
| 84 } | |
| 85 } | |
| 86 return result; | |
| 87 } | |
| 88 | |
| 89 /** | |
| 90 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} | |
| 91 */ | |
| 92 async _stopJSCoverage() { | 53 async _stopJSCoverage() { | 
| 93 if (!this._cpuProfilerModel) | 54 if (!this._cpuProfilerModel) | 
| 94 return []; | 55 return []; | 
| 95 var coveragePromise = this._cpuProfilerModel.takePreciseCoverage(); | 56 var coveragePromise = this._cpuProfilerModel.takePreciseCoverage(); | 
| 96 this._cpuProfilerModel.stopPreciseCoverage(); | 57 this._cpuProfilerModel.stopPreciseCoverage(); | 
| 97 var rawCoverageData = await coveragePromise; | 58 var rawCoverageData = await coveragePromise; | 
| 98 return Coverage.CoverageModel._processJSCoverage( | 59 this._processJSCoverage(rawCoverageData); | 
| 99 /** @type !SDK.DebuggerModel */ (SDK.DebuggerModel.fromTarget(this.targe t())), rawCoverageData); | |
| 100 } | 60 } | 
| 101 | 61 | 
| 102 /** | 62 /** | 
| 103 * @param {!SDK.DebuggerModel} debuggerModel | |
| 104 * @param {!Array<!Protocol.Profiler.ScriptCoverage>} scriptsCoverage | 63 * @param {!Array<!Protocol.Profiler.ScriptCoverage>} scriptsCoverage | 
| 105 * @return {!Array<!Coverage.CoverageInfo>} | |
| 106 */ | 64 */ | 
| 107 static _processJSCoverage(debuggerModel, scriptsCoverage) { | 65 _processJSCoverage(scriptsCoverage) { | 
| 108 var result = []; | |
| 109 for (var entry of scriptsCoverage) { | 66 for (var entry of scriptsCoverage) { | 
| 110 var script = debuggerModel.scriptForId(entry.scriptId); | 67 var script = this._debuggerModel.scriptForId(entry.scriptId); | 
| 111 if (!script) | 68 if (!script) | 
| 112 continue; | 69 continue; | 
| 113 var ranges = []; | 70 var ranges = []; | 
| 114 for (var func of entry.functions) { | 71 for (var func of entry.functions) { | 
| 115 for (var range of func.ranges) | 72 for (var range of func.ranges) | 
| 116 ranges.push(range); | 73 ranges.push(range); | 
| 117 } | 74 } | 
| 118 ranges.sort((a, b) => a.startOffset - b.startOffset); | 75 ranges.sort((a, b) => a.startOffset - b.startOffset); | 
| 119 result.push(Coverage.CoverageModel._buildCoverageInfo( | 76 this._addCoverage(script, script.contentLength, script.lineOffset, script. columnOffset, ranges); | 
| 120 script, script.contentLength, script.lineOffset, script.columnOffset, ranges)); | |
| 121 } | 77 } | 
| 122 return result; | |
| 123 } | 78 } | 
| 124 | 79 | 
| 125 /** | 80 /** | 
| 126 * @param {!Array<!Coverage.RangeUseCount>} ranges | 81 * @param {!Array<!Coverage.RangeUseCount>} ranges | 
| 127 * @return {!Array<!Coverage.CoverageSegment>} | 82 * @return {!Array<!Coverage.CoverageSegment>} | 
| 128 */ | 83 */ | 
| 129 static _convertToDisjointSegments(ranges) { | 84 static _convertToDisjointSegments(ranges) { | 
| 130 var result = []; | 85 var result = []; | 
| 131 | 86 | 
| 132 var stack = []; | 87 var stack = []; | 
| (...skipping 28 matching lines...) Expand all Loading... | |
| 161 last.end = end; | 116 last.end = end; | 
| 162 return; | 117 return; | 
| 163 } | 118 } | 
| 164 } | 119 } | 
| 165 result.push({end: end, count: count, depth: depth}); | 120 result.push({end: end, count: count, depth: depth}); | 
| 166 } | 121 } | 
| 167 | 122 | 
| 168 return result; | 123 return result; | 
| 169 } | 124 } | 
| 170 | 125 | 
| 171 /** | |
| 172 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} | |
| 173 */ | |
| 174 async _stopCSSCoverage() { | 126 async _stopCSSCoverage() { | 
| 175 if (!this._cssModel) | 127 if (!this._cssModel) | 
| 176 return []; | 128 return []; | 
| 
alph
2017/03/13 20:59:07
return
 | |
| 177 | 129 | 
| 178 var rawCoverageData = await this._cssModel.ruleListPromise(); | 130 var rawCoverageData = await this._cssModel.ruleListPromise(); | 
| 179 return Coverage.CoverageModel._processCSSCoverage( | 131 this._processCSSCoverage(rawCoverageData); | 
| 180 /** @type !SDK.CSSModel */ (this._cssModel), rawCoverageData); | |
| 181 } | 132 } | 
| 182 | 133 | 
| 183 /** | 134 /** | 
| 184 * @param {!SDK.CSSModel} cssModel | |
| 185 * @param {!Array<!Protocol.CSS.RuleUsage>} ruleUsageList | 135 * @param {!Array<!Protocol.CSS.RuleUsage>} ruleUsageList | 
| 186 * @return {!Array<!Coverage.CoverageInfo>} | |
| 187 */ | 136 */ | 
| 188 static _processCSSCoverage(cssModel, ruleUsageList) { | 137 _processCSSCoverage(ruleUsageList) { | 
| 189 /** @type {!Map<?SDK.CSSStyleSheetHeader, !Array<!Coverage.RangeUseCount>>} */ | 138 /** @type {!Map<!SDK.CSSStyleSheetHeader, !Array<!Coverage.RangeUseCount>>} */ | 
| 190 var rulesByStyleSheet = new Map(); | 139 var rulesByStyleSheet = new Map(); | 
| 191 for (var rule of ruleUsageList) { | 140 for (var rule of ruleUsageList) { | 
| 192 var styleSheetHeader = cssModel.styleSheetHeaderForId(rule.styleSheetId); | 141 var styleSheetHeader = this._cssModel.styleSheetHeaderForId(rule.styleShee tId); | 
| 142 if (!styleSheetHeader) | |
| 143 continue; | |
| 193 var ranges = rulesByStyleSheet.get(styleSheetHeader); | 144 var ranges = rulesByStyleSheet.get(styleSheetHeader); | 
| 194 if (!ranges) { | 145 if (!ranges) { | 
| 195 ranges = []; | 146 ranges = []; | 
| 196 rulesByStyleSheet.set(styleSheetHeader, ranges); | 147 rulesByStyleSheet.set(styleSheetHeader, ranges); | 
| 197 } | 148 } | 
| 198 ranges.push({startOffset: rule.startOffset, endOffset: rule.endOffset, cou nt: Number(rule.used)}); | 149 ranges.push({startOffset: rule.startOffset, endOffset: rule.endOffset, cou nt: Number(rule.used)}); | 
| 199 } | 150 } | 
| 200 return Array.from( | 151 for (var entry of rulesByStyleSheet) { | 
| 201 rulesByStyleSheet.entries(), | 152 var styleSheetHeader = /** @type {!SDK.CSSStyleSheetHeader} */ (entry[0]); | 
| 202 entry => Coverage.CoverageModel._buildCoverageInfo( | 153 var ranges = /** @type {!Array<!Coverage.RangeUseCount>} */ (entry[1]); | 
| 203 entry[0], entry[0].contentLength, entry[0].startLine, entry[0].start Column, entry[1])); | 154 this._addCoverage( | 
| 155 styleSheetHeader, styleSheetHeader.contentLength, styleSheetHeader.sta rtLine, styleSheetHeader.startColumn, | |
| 156 ranges); | |
| 157 } | |
| 204 } | 158 } | 
| 205 | 159 | 
| 206 /** | 160 /** | 
| 207 * @param {!Common.ContentProvider} contentProvider | 161 * @param {!Common.ContentProvider} contentProvider | 
| 208 * @param {number} contentLength | 162 * @param {number} contentLength | 
| 209 * @param {number} startLine | 163 * @param {number} startLine | 
| 210 * @param {number} startColumn | 164 * @param {number} startColumn | 
| 211 * @param {!Array<!Coverage.RangeUseCount>} ranges | 165 * @param {!Array<!Coverage.RangeUseCount>} ranges | 
| 212 * @return {!Coverage.CoverageInfo} | 166 */ | 
| 213 */ | 167 _addCoverage(contentProvider, contentLength, startLine, startColumn, ranges) { | 
| 214 static _buildCoverageInfo(contentProvider, contentLength, startLine, startColu mn, ranges) { | |
| 215 /** @type Coverage.CoverageType */ | |
| 216 var coverageType; | |
| 217 var url = contentProvider.contentURL(); | 168 var url = contentProvider.contentURL(); | 
| 218 if (contentProvider.contentType().isScript()) | 169 if (!url) | 
| 219 coverageType = Coverage.CoverageType.JavaScript; | 170 return; | 
| 220 else if (contentProvider.contentType().isStyleSheet()) | 171 var entry = this._coverageByURL.get(url); | 
| 221 coverageType = Coverage.CoverageType.CSS; | 172 if (!entry) { | 
| 222 else | 173 entry = new Coverage.URLCoverageInfo(url); | 
| 223 console.assert(false, `Unexpected resource type ${contentProvider.contentT ype().name} for ${url}`); | 174 this._coverageByURL.set(url, entry); | 
| 224 | 175 } | 
| 225 var segments = Coverage.CoverageModel._convertToDisjointSegments(ranges); | 176 var segments = Coverage.CoverageModel._convertToDisjointSegments(ranges); | 
| 226 var usedSize = 0; | 177 entry.update(contentProvider, contentLength, startLine, startColumn, segment s); | 
| 227 var unusedSize = 0; | 178 } | 
| 179 }; | |
| 180 | |
| 181 Coverage.URLCoverageInfo = class { | |
| 182 /** | |
| 183 * @param {string} url | |
| 184 */ | |
| 185 constructor(url) { | |
| 186 this._url = url; | |
| 187 /** @type {!Map<string, !Coverage.CoverageInfo>} */ | |
| 188 this._coverageInfoByLocation = new Map(); | |
| 189 this._size = 0; | |
| 190 this._unusedSize = 0; | |
| 191 this._usedSize = 0; | |
| 192 /** @type {!Coverage.CoverageType} */ | |
| 193 this._type; | |
| 194 } | |
| 195 | |
| 196 /** | |
| 197 * @param {!Common.ContentProvider} contentProvider | |
| 198 * @param {number} contentLength | |
| 199 * @param {number} lineOffset | |
| 200 * @param {number} columnOffset | |
| 201 * @param {!Array<!Coverage.CoverageSegment>} segments | |
| 202 */ | |
| 203 update(contentProvider, contentLength, lineOffset, columnOffset, segments) { | |
| 204 var key = `${lineOffset}:${columnOffset}`; | |
| 205 var entry = this._coverageInfoByLocation.get(key); | |
| 206 | |
| 207 if (!entry) { | |
| 208 entry = new Coverage.CoverageInfo(contentProvider, lineOffset, columnOffse t); | |
| 209 this._coverageInfoByLocation.set(key, entry); | |
| 210 this._size += contentLength; | |
| 211 this._type |= entry.type(); | |
| 212 } | |
| 213 this._usedSize -= entry._usedSize; | |
| 214 this._unusedSize -= entry._unusedSize; | |
| 215 entry.mergeCoverage(segments); | |
| 216 this._usedSize += entry._usedSize; | |
| 217 this._unusedSize += entry._unusedSize; | |
| 218 } | |
| 219 | |
| 220 /** | |
| 221 * @return {string} | |
| 222 */ | |
| 223 url() { | |
| 224 return this._url; | |
| 225 } | |
| 226 | |
| 227 /** | |
| 228 * @return {!Coverage.CoverageType} | |
| 229 */ | |
| 230 type() { | |
| 231 return this._type; | |
| 232 } | |
| 233 | |
| 234 /** | |
| 235 * @return {number} | |
| 236 */ | |
| 237 size() { | |
| 238 return this._size; | |
| 239 } | |
| 240 | |
| 241 /** | |
| 242 * @return {number} | |
| 243 */ | |
| 244 unusedSize() { | |
| 245 return this._unusedSize; | |
| 246 } | |
| 247 | |
| 248 /** | |
| 249 * @return {number} | |
| 250 */ | |
| 251 usedSize() { | |
| 252 return this._usedSize; | |
| 253 } | |
| 254 | |
| 255 /** | |
| 256 * @return {!Promise<!Array<!{range: !Common.TextRange, count: number}>>} | |
| 257 */ | |
| 258 async buildTextRanges() { | |
| 259 var textRangePromises = []; | |
| 260 for (var coverageInfo of this._coverageInfoByLocation.values()) | |
| 261 textRangePromises.push(coverageInfo.buildTextRanges()); | |
| 262 var allTextRanges = await Promise.all(textRangePromises); | |
| 263 return [].concat(...allTextRanges); | |
| 264 } | |
| 265 }; | |
| 266 | |
| 267 Coverage.CoverageInfo = class { | |
| 268 /** | |
| 269 * @param {!Common.ContentProvider} contentProvider | |
| 270 * @param {number} lineOffset | |
| 271 * @param {number} columnOffset | |
| 272 */ | |
| 273 constructor(contentProvider, lineOffset, columnOffset) { | |
| 274 this._contentProvider = contentProvider; | |
| 275 this._lineOffset = lineOffset; | |
| 276 this._columnOffset = columnOffset; | |
| 277 this._usedSize = 0; | |
| 278 this._unusedSize = 0; | |
| 279 | |
| 280 if (contentProvider.contentType().isScript()) { | |
| 281 this._coverageType = Coverage.CoverageType.JavaScript; | |
| 282 } else if (contentProvider.contentType().isStyleSheet()) { | |
| 283 this._coverageType = Coverage.CoverageType.CSS; | |
| 284 } else { | |
| 285 console.assert( | |
| 286 false, `Unexpected resource type ${contentProvider.contentType().name} for ${contentProvider.contentURL()}`); | |
| 287 } | |
| 288 /** !Array<!Coverage.CoverageSegment> */ | |
| 289 this._segments = []; | |
| 290 } | |
| 291 | |
| 292 /** | |
| 293 * @return {!Coverage.CoverageType} | |
| 294 */ | |
| 295 type() { | |
| 296 return this._coverageType; | |
| 297 } | |
| 298 | |
| 299 /** | |
| 300 * @param {!Array<!Coverage.CoverageSegment>} segments | |
| 301 */ | |
| 302 mergeCoverage(segments) { | |
| 303 this._segments = Coverage.CoverageInfo._mergeCoverage(this._segments, segmen ts); | |
| 304 this._updateStats(); | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * @param {!Array<!Coverage.CoverageSegment>} segmentsA | |
| 309 * @param {!Array<!Coverage.CoverageSegment>} segmentsB | |
| 310 */ | |
| 311 static _mergeCoverage(segmentsA, segmentsB) { | |
| 312 var result = []; | |
| 313 | |
| 314 var indexA = 0; | |
| 315 var indexB = 0; | |
| 316 while (indexA < segmentsA.length && indexB < segmentsB.length) { | |
| 317 var a = segmentsA[indexA]; | |
| 318 var b = segmentsB[indexB]; | |
| 319 var count = | |
| 320 typeof a.count === 'number' || typeof b.count === 'number' ? (a.count || 0) + (b.count || 0) : undefined; | |
| 321 var depth = Math.max(a.depth, b.depth); | |
| 322 var end = Math.min(a.end, b.end); | |
| 323 var last = result.peekLast(); | |
| 324 if (!last || last.count !== count || last.depth !== depth) | |
| 325 result.push({end: end, count: count, depth: depth}); | |
| 326 else | |
| 327 last.end = end; | |
| 328 if (a.end <= b.end) | |
| 329 indexA++; | |
| 330 if (a.end >= b.end) | |
| 331 indexB++; | |
| 332 } | |
| 333 | |
| 334 for (; indexA < segmentsA.length; indexA++) | |
| 335 result.push(segmentsA[indexA]); | |
| 336 for (; indexB < segmentsB.length; indexB++) | |
| 337 result.push(segmentsB[indexB]); | |
| 338 return result; | |
| 339 } | |
| 340 | |
| 341 /** | |
| 342 * @return {!Promise<!Array<!{range: !Common.TextRange, count: number}>>} | |
| 343 */ | |
| 344 async buildTextRanges() { | |
| 345 var contents = await this._contentProvider.requestContent(); | |
| 346 if (!contents) | |
| 347 return; | |
| 
alph
2017/03/13 20:59:07
return [];
 | |
| 348 var text = new Common.Text(contents); | |
| 349 var lastOffset = 0; | |
| 350 var rangesByDepth = []; | |
| 351 for (var segment of this._segments) { | |
| 352 if (typeof segment.count !== 'number') { | |
| 353 lastOffset = segment.end; | |
| 354 continue; | |
| 355 } | |
| 356 var startPosition = text.positionFromOffset(lastOffset); | |
| 357 var endPosition = text.positionFromOffset(segment.end); | |
| 358 if (!startPosition.lineNumber) | |
| 359 startPosition.columnNumber += this._columnOffset; | |
| 360 startPosition.lineNumber += this._lineOffset; | |
| 361 if (!endPosition.lineNumber) | |
| 362 endPosition.columnNumber += this._columnOffset; | |
| 363 endPosition.lineNumber += this._lineOffset; | |
| 364 | |
| 365 var ranges = rangesByDepth[segment.depth - 1]; // depth === 0 => count == = undefined | |
| 366 if (!ranges) { | |
| 367 ranges = []; | |
| 368 rangesByDepth[segment.depth - 1] = ranges; | |
| 369 } | |
| 370 ranges.push({ | |
| 371 count: segment.count, | |
| 372 range: new Common.TextRange( | |
| 373 startPosition.lineNumber, startPosition.columnNumber, endPosition.li neNumber, endPosition.columnNumber) | |
| 374 }); | |
| 375 lastOffset = segment.end; | |
| 376 } | |
| 377 var result = []; | |
| 378 for (var ranges of rangesByDepth) { | |
| 379 for (var r of ranges) | |
| 380 result.push({count: r.count, range: r.range}); | |
| 381 } | |
| 382 return result; | |
| 383 } | |
| 384 | |
| 385 _updateStats() { | |
| 386 this._usedSize = 0; | |
| 387 this._unusedSize = 0; | |
| 388 | |
| 228 var last = 0; | 389 var last = 0; | 
| 229 for (var segment of segments) { | 390 for (var segment of this._segments) { | 
| 230 if (typeof segment.count === 'number') { | 391 if (typeof segment.count === 'number') { | 
| 231 if (segment.count) | 392 if (segment.count) | 
| 232 usedSize += segment.end - last; | 393 this._usedSize += segment.end - last; | 
| 233 else | 394 else | 
| 234 unusedSize += segment.end - last; | 395 this._unusedSize += segment.end - last; | 
| 235 } | 396 } | 
| 236 last = segment.end; | 397 last = segment.end; | 
| 237 } | 398 } | 
| 238 var coverageInfo = { | |
| 239 contentProvider: contentProvider, | |
| 240 segments: segments, | |
| 241 type: coverageType, | |
| 242 size: contentLength, | |
| 243 usedSize: usedSize, | |
| 244 unusedSize: unusedSize, | |
| 245 lineOffset: startLine, | |
| 246 columnOffset: startColumn | |
| 247 }; | |
| 248 return coverageInfo; | |
| 249 } | 399 } | 
| 250 }; | 400 }; | 
| OLD | NEW |