| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| import {SelectionEvent} from './events.mjs'; |
| import {DOM, V8CustomElement} from './helper.mjs'; |
| import {delay, LazyTable} from './helper.mjs'; |
| |
| DOM.defineCustomElement( |
| 'stats-panel', (templateText) => class StatsPanel extends V8CustomElement { |
| _timeline; |
| _transitions; |
| _selectedLogEntries; |
| constructor() { |
| super(templateText); |
| } |
| |
| get stats() { |
| return this.$('#stats'); |
| } |
| |
| set timeline(timeline) { |
| this._timeline = timeline; |
| this.selectedLogEntries = timeline.all |
| } |
| |
| set selectedLogEntries(entries) { |
| this._selectedLogEntries = entries; |
| this.update(); |
| } |
| |
| set transitions(value) { |
| this._transitions = value; |
| } |
| |
| _filterUniqueTransitions(filter) { |
| // Returns a list of Maps whose parent is not in the list. |
| return this._selectedLogEntries.filter((map) => { |
| if (filter(map) === false) return false; |
| let parent = map.parent(); |
| if (parent === undefined) return true; |
| return filter(parent) === false; |
| }); |
| } |
| |
| _update() { |
| this._updateGeneralStats(); |
| this._updateNamedTransitionsStats(); |
| } |
| |
| _updateGeneralStats() { |
| console.assert(this._timeline !== undefined, 'Timeline not set yet!'); |
| let pairs = [ |
| ['Transitions', 'primary', (e) => e.edge && e.edge.isTransition()], |
| ['Fast to Slow', 'violet', (e) => e.edge && e.edge.isFastToSlow()], |
| ['Slow to Fast', 'orange', (e) => e.edge && e.edge.isSlowToFast()], |
| ['Initial Map', 'yellow', (e) => e.edge && e.edge.isInitial()], |
| [ |
| 'Replace Descriptors', |
| 'red', |
| (e) => e.edge && e.edge.isReplaceDescriptors(), |
| ], |
| [ |
| 'Copy as Prototype', |
| 'red', |
| (e) => e.edge && e.edge.isCopyAsPrototype(), |
| ], |
| [ |
| 'Optimize as Prototype', |
| null, |
| (e) => e.edge && e.edge.isOptimizeAsPrototype(), |
| ], |
| ['Deprecated', null, (e) => e.isDeprecated()], |
| ['Bootstrapped', 'green', (e) => e.isBootstrapped()], |
| ['Total', null, (e) => true], |
| ]; |
| |
| let tbody = document.createElement('tbody'); |
| let total = this._selectedLogEntries.length; |
| pairs.forEach(([name, color, filter]) => { |
| let row = DOM.tr(); |
| if (color !== null) { |
| row.appendChild(DOM.td(DOM.div(['colorbox', color]))); |
| } else { |
| row.appendChild(DOM.td('')); |
| } |
| row.classList.add('clickable'); |
| row.onclick = (e) => { |
| // lazily compute the stats |
| let node = e.target.parentNode; |
| if (node.maps == undefined) { |
| node.maps = this._filterUniqueTransitions(filter); |
| } |
| this.dispatchEvent(new SelectionEvent(node.maps)); |
| }; |
| row.appendChild(DOM.td(name)); |
| let count = this._count(filter); |
| row.appendChild(DOM.td(count)); |
| let percent = Math.round((count / total) * 1000) / 10; |
| row.appendChild(DOM.td(percent.toFixed(1) + '%')); |
| tbody.appendChild(row); |
| }); |
| this.$('#typeTable').replaceChild(tbody, this.$('#typeTable tbody')); |
| } |
| |
| _count(filter) { |
| let count = 0; |
| for (const map of this._selectedLogEntries) { |
| if (filter(map)) count++; |
| } |
| return count; |
| } |
| |
| _updateNamedTransitionsStats() { |
| let rowData = Array.from(this._transitions.entries()); |
| rowData.sort((a, b) => b[1].length - a[1].length); |
| new LazyTable(this.$('#nameTable'), rowData, ([name, maps]) => { |
| let row = DOM.tr(); |
| row.maps = maps; |
| row.classList.add('clickable'); |
| row.addEventListener( |
| 'click', |
| (e) => this.dispatchEvent(new SelectionEvent( |
| e.target.parentNode.maps.map((map) => map.to)))); |
| row.appendChild(DOM.td(maps.length)); |
| row.appendChild(DOM.td(name)); |
| return row; |
| }); |
| } |
| }); |