web 3d图形渲染器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
35 KiB

  1. import { deepNormalizeScriptCov, normalizeFunctionCov, normalizeProcessCov, normalizeRangeTree, normalizeScriptCov, } from "./normalize";
  2. import { RangeTree } from "./range-tree";
  3. /**
  4. * Merges a list of process coverages.
  5. *
  6. * The result is normalized.
  7. * The input values may be mutated, it is not safe to use them after passing
  8. * them to this function.
  9. * The computation is synchronous.
  10. *
  11. * @param processCovs Process coverages to merge.
  12. * @return Merged process coverage.
  13. */
  14. export function mergeProcessCovs(processCovs) {
  15. if (processCovs.length === 0) {
  16. return { result: [] };
  17. }
  18. const urlToScripts = new Map();
  19. for (const processCov of processCovs) {
  20. for (const scriptCov of processCov.result) {
  21. let scriptCovs = urlToScripts.get(scriptCov.url);
  22. if (scriptCovs === undefined) {
  23. scriptCovs = [];
  24. urlToScripts.set(scriptCov.url, scriptCovs);
  25. }
  26. scriptCovs.push(scriptCov);
  27. }
  28. }
  29. const result = [];
  30. for (const scripts of urlToScripts.values()) {
  31. // assert: `scripts.length > 0`
  32. result.push(mergeScriptCovs(scripts));
  33. }
  34. const merged = { result };
  35. normalizeProcessCov(merged);
  36. return merged;
  37. }
  38. /**
  39. * Merges a list of matching script coverages.
  40. *
  41. * Scripts are matching if they have the same `url`.
  42. * The result is normalized.
  43. * The input values may be mutated, it is not safe to use them after passing
  44. * them to this function.
  45. * The computation is synchronous.
  46. *
  47. * @param scriptCovs Process coverages to merge.
  48. * @return Merged script coverage, or `undefined` if the input list was empty.
  49. */
  50. export function mergeScriptCovs(scriptCovs) {
  51. if (scriptCovs.length === 0) {
  52. return undefined;
  53. }
  54. else if (scriptCovs.length === 1) {
  55. const merged = scriptCovs[0];
  56. deepNormalizeScriptCov(merged);
  57. return merged;
  58. }
  59. const first = scriptCovs[0];
  60. const scriptId = first.scriptId;
  61. const url = first.url;
  62. const rangeToFuncs = new Map();
  63. for (const scriptCov of scriptCovs) {
  64. for (const funcCov of scriptCov.functions) {
  65. const rootRange = stringifyFunctionRootRange(funcCov);
  66. let funcCovs = rangeToFuncs.get(rootRange);
  67. if (funcCovs === undefined ||
  68. // if the entry in rangeToFuncs is function-level granularity and
  69. // the new coverage is block-level, prefer block-level.
  70. (!funcCovs[0].isBlockCoverage && funcCov.isBlockCoverage)) {
  71. funcCovs = [];
  72. rangeToFuncs.set(rootRange, funcCovs);
  73. }
  74. else if (funcCovs[0].isBlockCoverage && !funcCov.isBlockCoverage) {
  75. // if the entry in rangeToFuncs is block-level granularity, we should
  76. // not append function level granularity.
  77. continue;
  78. }
  79. funcCovs.push(funcCov);
  80. }
  81. }
  82. const functions = [];
  83. for (const funcCovs of rangeToFuncs.values()) {
  84. // assert: `funcCovs.length > 0`
  85. functions.push(mergeFunctionCovs(funcCovs));
  86. }
  87. const merged = { scriptId, url, functions };
  88. normalizeScriptCov(merged);
  89. return merged;
  90. }
  91. /**
  92. * Returns a string representation of the root range of the function.
  93. *
  94. * This string can be used to match function with same root range.
  95. * The string is derived from the start and end offsets of the root range of
  96. * the function.
  97. * This assumes that `ranges` is non-empty (true for valid function coverages).
  98. *
  99. * @param funcCov Function coverage with the range to stringify
  100. * @internal
  101. */
  102. function stringifyFunctionRootRange(funcCov) {
  103. const rootRange = funcCov.ranges[0];
  104. return `${rootRange.startOffset.toString(10)};${rootRange.endOffset.toString(10)}`;
  105. }
  106. /**
  107. * Merges a list of matching function coverages.
  108. *
  109. * Functions are matching if their root ranges have the same span.
  110. * The result is normalized.
  111. * The input values may be mutated, it is not safe to use them after passing
  112. * them to this function.
  113. * The computation is synchronous.
  114. *
  115. * @param funcCovs Function coverages to merge.
  116. * @return Merged function coverage, or `undefined` if the input list was empty.
  117. */
  118. export function mergeFunctionCovs(funcCovs) {
  119. if (funcCovs.length === 0) {
  120. return undefined;
  121. }
  122. else if (funcCovs.length === 1) {
  123. const merged = funcCovs[0];
  124. normalizeFunctionCov(merged);
  125. return merged;
  126. }
  127. const functionName = funcCovs[0].functionName;
  128. const trees = [];
  129. for (const funcCov of funcCovs) {
  130. // assert: `fn.ranges.length > 0`
  131. // assert: `fn.ranges` is sorted
  132. trees.push(RangeTree.fromSortedRanges(funcCov.ranges));
  133. }
  134. // assert: `trees.length > 0`
  135. const mergedTree = mergeRangeTrees(trees);
  136. normalizeRangeTree(mergedTree);
  137. const ranges = mergedTree.toRanges();
  138. const isBlockCoverage = !(ranges.length === 1 && ranges[0].count === 0);
  139. const merged = { functionName, ranges, isBlockCoverage };
  140. // assert: `merged` is normalized
  141. return merged;
  142. }
  143. /**
  144. * @precondition Same `start` and `end` for all the trees
  145. */
  146. function mergeRangeTrees(trees) {
  147. if (trees.length <= 1) {
  148. return trees[0];
  149. }
  150. const first = trees[0];
  151. let delta = 0;
  152. for (const tree of trees) {
  153. delta += tree.delta;
  154. }
  155. const children = mergeRangeTreeChildren(trees);
  156. return new RangeTree(first.start, first.end, delta, children);
  157. }
  158. class RangeTreeWithParent {
  159. constructor(parentIndex, tree) {
  160. this.parentIndex = parentIndex;
  161. this.tree = tree;
  162. }
  163. }
  164. class StartEvent {
  165. constructor(offset, trees) {
  166. this.offset = offset;
  167. this.trees = trees;
  168. }
  169. static compare(a, b) {
  170. return a.offset - b.offset;
  171. }
  172. }
  173. class StartEventQueue {
  174. constructor(queue) {
  175. this.queue = queue;
  176. this.nextIndex = 0;
  177. this.pendingOffset = 0;
  178. this.pendingTrees = undefined;
  179. }
  180. static fromParentTrees(parentTrees) {
  181. const startToTrees = new Map();
  182. for (const [parentIndex, parentTree] of parentTrees.entries()) {
  183. for (const child of parentTree.children) {
  184. let trees = startToTrees.get(child.start);
  185. if (trees === undefined) {
  186. trees = [];
  187. startToTrees.set(child.start, trees);
  188. }
  189. trees.push(new RangeTreeWithParent(parentIndex, child));
  190. }
  191. }
  192. const queue = [];
  193. for (const [startOffset, trees] of startToTrees) {
  194. queue.push(new StartEvent(startOffset, trees));
  195. }
  196. queue.sort(StartEvent.compare);
  197. return new StartEventQueue(queue);
  198. }
  199. setPendingOffset(offset) {
  200. this.pendingOffset = offset;
  201. }
  202. pushPendingTree(tree) {
  203. if (this.pendingTrees === undefined) {
  204. this.pendingTrees = [];
  205. }
  206. this.pendingTrees.push(tree);
  207. }
  208. next() {
  209. const pendingTrees = this.pendingTrees;
  210. const nextEvent = this.queue[this.nextIndex];
  211. if (pendingTrees === undefined) {
  212. this.nextIndex++;
  213. return nextEvent;
  214. }
  215. else if (nextEvent === undefined) {
  216. this.pendingTrees = undefined;
  217. return new StartEvent(this.pendingOffset, pendingTrees);
  218. }
  219. else {
  220. if (this.pendingOffset < nextEvent.offset) {
  221. this.pendingTrees = undefined;
  222. return new StartEvent(this.pendingOffset, pendingTrees);
  223. }
  224. else {
  225. if (this.pendingOffset === nextEvent.offset) {
  226. this.pendingTrees = undefined;
  227. for (const tree of pendingTrees) {
  228. nextEvent.trees.push(tree);
  229. }
  230. }
  231. this.nextIndex++;
  232. return nextEvent;
  233. }
  234. }
  235. }
  236. }
  237. function mergeRangeTreeChildren(parentTrees) {
  238. const result = [];
  239. const startEventQueue = StartEventQueue.fromParentTrees(parentTrees);
  240. const parentToNested = new Map();
  241. let openRange;
  242. while (true) {
  243. const event = startEventQueue.next();
  244. if (event === undefined) {
  245. break;
  246. }
  247. if (openRange !== undefined && openRange.end <= event.offset) {
  248. result.push(nextChild(openRange, parentToNested));
  249. openRange = undefined;
  250. }
  251. if (openRange === undefined) {
  252. let openRangeEnd = event.offset + 1;
  253. for (const { parentIndex, tree } of event.trees) {
  254. openRangeEnd = Math.max(openRangeEnd, tree.end);
  255. insertChild(parentToNested, parentIndex, tree);
  256. }
  257. startEventQueue.setPendingOffset(openRangeEnd);
  258. openRange = { start: event.offset, end: openRangeEnd };
  259. }
  260. else {
  261. for (const { parentIndex, tree } of event.trees) {
  262. if (tree.end > openRange.end) {
  263. const right = tree.split(openRange.end);
  264. startEventQueue.pushPendingTree(new RangeTreeWithParent(parentIndex, right));
  265. }
  266. insertChild(parentToNested, parentIndex, tree);
  267. }
  268. }
  269. }
  270. if (openRange !== undefined) {
  271. result.push(nextChild(openRange, parentToNested));
  272. }
  273. return result;
  274. }
  275. function insertChild(parentToNested, parentIndex, tree) {
  276. let nested = parentToNested.get(parentIndex);
  277. if (nested === undefined) {
  278. nested = [];
  279. parentToNested.set(parentIndex, nested);
  280. }
  281. nested.push(tree);
  282. }
  283. function nextChild(openRange, parentToNested) {
  284. const matchingTrees = [];
  285. for (const nested of parentToNested.values()) {
  286. if (nested.length === 1 && nested[0].start === openRange.start && nested[0].end === openRange.end) {
  287. matchingTrees.push(nested[0]);
  288. }
  289. else {
  290. matchingTrees.push(new RangeTree(openRange.start, openRange.end, 0, nested));
  291. }
  292. }
  293. parentToNested.clear();
  294. return mergeRangeTrees(matchingTrees);
  295. }
  296. //# sourceMappingURL=data:application/json;charset=utf8;base64,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