Import Cobalt 21.master.0.301323
diff --git a/src/third_party/v8/tools/parse-processor.html b/src/third_party/v8/tools/parse-processor.html
new file mode 100644
index 0000000..9d78bbf
--- /dev/null
+++ b/src/third_party/v8/tools/parse-processor.html
@@ -0,0 +1,412 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2016 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.
+-->
+
+<head>
+<meta charset="utf-8">
+<title>V8 Parse Processor</title>
+<style>
+ html {
+ font-family: monospace;
+ }
+
+ .parse {
+ background-color: red;
+ border: 1px red solid;
+ }
+
+ .preparse {
+ background-color: orange;
+ border: 1px orange solid;
+ }
+
+ .resolution {
+ background-color: green;
+ border: 1px green solid;
+ }
+
+ .execution {
+ background-color: black;
+ border-left: 2px black solid;
+ z-index: -1;
+ }
+
+ .script {
+ margin-top: 1em;
+ overflow: visible;
+ clear: both;
+ border-top: 2px black dotted;
+ }
+ .script h3 {
+ height: 20px;
+ margin-bottom: 0.5em;
+ white-space: nowrap;
+ }
+
+ .script-details {
+ float: left;
+ }
+
+ .chart {
+ float: left;
+ margin-right: 2em;
+ }
+
+ .funktion-list {
+ float: left;
+ height: 400px;
+ }
+
+ .funktion-list > ul {
+ height: 80%;
+ overflow-y: scroll;
+ }
+
+ .funktion {
+ }
+
+ .script-size {
+ display: inline-flex;
+ background-color: #505050;
+ border-radius: 3px;
+ padding: 3px;
+ margin: 2px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-decoration: none;
+ color: white;
+ }
+ .script-size.eval {
+ background-color: #ee6300fc;
+ }
+ .script-size.streaming {
+ background-color: #008aff;
+ }
+ .script-size.deserialized {
+ background-color: #1fad00fc;
+ }
+
+ .script-details {
+ padding-right: 5px;
+ margin-right: 4px;
+ }
+ /* all but the last need a border */
+ .script-details:nth-last-child(n+2) {
+ border-right: 1px white solid;
+ }
+
+ .script-details.id {
+ min-width: 2em;
+ text-align: right;
+ }
+</style>
+<script src="https://www.gstatic.com/charts/loader.js"></script>
+<script type="module">
+
+import { ParseProcessor, kSecondsToMillis } from "./parse-processor.mjs";
+
+google.charts.load('current', {packages: ['corechart']});
+
+function $(query) {
+ return document.querySelector(query);
+}
+
+window.addEventListener('DOMContentLoaded', (event) => {
+ $("#uploadInput").focus();
+});
+
+document.loadFile = function() {
+ let files = $('#uploadInput').files;
+
+ let file = files[0];
+ let reader = new FileReader();
+
+ reader.onload = function(evt) {
+ const kTimerName = 'parse log file';
+ console.time(kTimerName);
+ let parseProcessor = new ParseProcessor();
+ parseProcessor.processString(this.result);
+ console.timeEnd(kTimerName);
+ renderParseResults(parseProcessor);
+ document.parseProcessor = parseProcessor;
+ }
+ reader.readAsText(file);
+}
+
+function createNode(tag, classNames) {
+ let node = document.createElement(tag);
+ if (classNames) {
+ if (Array.isArray(classNames)) {
+ node.classList.add(...classNames);
+ } else {
+ node.className = classNames;
+ }
+ }
+ return node;
+}
+
+function div(...args) {
+ return createNode('div', ...args);
+}
+
+function h1(string) {
+ let node = createNode('h1');
+ node.appendChild(text(string));
+ return node;
+}
+
+function h3(string, ...args) {
+ let node = createNode('h3', ...args);
+ if (string) node.appendChild(text(string));
+ return node;
+}
+
+function a(href, string, ...args) {
+ let link = createNode('a', ...args);
+ if (href.length) link.href = href;
+ if (string) link.appendChild(text(string));
+ return link;
+}
+
+function text(string) {
+ return document.createTextNode(string);
+}
+
+function delay(t) {
+ return new Promise(resolve => setTimeout(resolve, t));
+}
+
+function renderParseResults(parseProcessor) {
+ let result = $('#result');
+ // clear out all existing result pages;
+ result.innerHTML = '';
+ const start = parseProcessor.firstEventTimestamp;
+ const end = parseProcessor.lastEventTimestamp;
+ renderScript(result, parseProcessor.totalScript, start, end);
+ // Build up the graphs lazily to keep the page responsive.
+ parseProcessor.scripts.forEach(
+ script => renderScript(result, script, start, end));
+ renderScriptSizes(parseProcessor);
+ // Install an intersection observer to lazily load the graphs when the script
+ // div becomes visible for the first time.
+ var io = new IntersectionObserver((entries, observer) => {
+ entries.forEach(entry => {
+ if (entry.intersectionRatio == 0) return;
+ console.assert(!entry.target.querySelector('.graph'));
+ let target = entry.target;
+ appendGraph(target.script, target, start, end);
+ observer.unobserve(entry.target);
+ });
+ }, {rootMargin: '400px'});
+ document.querySelectorAll('.script').forEach(div => io.observe(div));
+}
+
+const kTimeFactor = 10;
+const kHeight = 20;
+const kFunktionTopOffset = 50;
+
+function renderScript(result, script, start, end) {
+ // Filter out empty scripts.
+ if (script.isEmpty() || script.lastParseEvent == 0) return;
+
+ let scriptDiv = div('script');
+ scriptDiv.script = script;
+
+ let scriptTitle = h3();
+ let anchor = a("", 'Script #' + script.id);
+ anchor.name = "script"+script.id
+ scriptTitle.appendChild(anchor);
+ scriptDiv.appendChild(scriptTitle);
+ if (script.file) scriptTitle.appendChild(a(script.file, script.file));
+ let summary = createNode('pre', 'script-details');
+ summary.appendChild(text(script.summary));
+ scriptDiv.appendChild(summary);
+ result.appendChild(scriptDiv);
+}
+
+function renderScriptSizes(parseProcessor) {
+ let scriptsDiv = $('#scripts');
+ parseProcessor.scripts.forEach(
+ script => {
+ let scriptDiv = a('#script'+script.id, '', 'script-size');
+ let scriptId = div('script-details');
+ scriptId.classList.add('id');
+ scriptId.innerText = script.id;
+ scriptDiv.appendChild(scriptId);
+ let scriptSize = div('script-details');
+ scriptSize.innerText = BYTES(script.bytesTotal);
+ scriptDiv.appendChild(scriptSize);
+ let scriptUrl = div('script-details');
+ if (script.isEval) {
+ scriptUrl.innerText = "eval";
+ scriptDiv.classList.add('eval');
+ } else {
+ scriptUrl.innerText = script.file.split("/").pop();
+ }
+ if (script.isStreamingCompiled ) {
+ scriptDiv.classList.add('streaming');
+ } else if (script.deserializationTimestamp > 0) {
+ scriptDiv.classList.add('deserialized');
+ }
+ scriptDiv.appendChild(scriptUrl);
+ scriptDiv.style.width = script.bytesTotal * 0.001;
+ scriptsDiv.appendChild(scriptDiv);
+ });
+}
+
+const kMaxTime = 120 * kSecondsToMillis;
+// Resolution of the graphs
+const kTimeIncrement = 1;
+const kSelectionTimespan = 2;
+// TODO(cbruni): support compilation cache hit.
+const series = [
+ ['firstParseEvent', 'Any Parse', 'area'],
+ ['execution', '1st Exec', 'area'],
+ ['firstCompileEvent', 'Any Compile', 'area'],
+ ['compile', 'Eager Compile'],
+ ['lazyCompile', 'Lazy Compile'],
+ ['parse', 'Parsing'],
+ ['preparse', 'Preparse'],
+ ['resolution', 'Preparse with Var. Resolution'],
+ ['deserialization', 'Deserialization'],
+ ['optimization', 'Optimize'],
+];
+const metricNames = series.map(each => each[0]);
+// Display cumulative values (useuful for bytes).
+const kCumulative = true;
+// Include durations in the graphs.
+const kUseDuration = false;
+
+
+function appendGraph(script, parentNode, start, end) {
+ const timerLabel = 'graph script=' + script.id;
+ // TODO(cbruni): add support for network events
+
+ console.time(timerLabel);
+ let data = new google.visualization.DataTable();
+ data.addColumn('number', 'Duration');
+ // The series are interleave bytes processed, time spent and thus have two
+ // different vAxes.
+ let seriesOptions = [];
+ let colors = ['#4D4D4D', '#fff700', '#5DA5DA', '#FAA43A', '#60BD68',
+ '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#F15854'];
+ series.forEach(([metric, description, type]) => {
+ let color = colors.shift();
+ // Add the bytes column.
+ data.addColumn('number', description);
+ let options = {targetAxisIndex: 0, color: color};
+ if (type == 'area') options.type = 'area';
+ seriesOptions.push(options)
+ // Add the time column.
+ if (kUseDuration) {
+ data.addColumn('number', description + ' Duration');
+ seriesOptions.push(
+ {targetAxisIndex: 1, color: color, lineDashStyle: [3, 2]});
+ }
+ });
+
+ const maxTime = Math.min(kMaxTime, end);
+ console.time('metrics');
+ let metricValues =
+ script.getAccumulatedTimeMetrics(metricNames , 0, maxTime, kTimeIncrement,
+ kCumulative, kUseDuration);
+ console.timeEnd('metrics');
+ // Make sure that the series added to the graph matches the returned values.
+ console.assert(metricValues[0].length == seriesOptions.length + 1);
+ data.addRows(metricValues);
+
+ let options = {
+ explorer: {
+ actions: ['dragToZoom', 'rightClickToReset'],
+ maxZoomIn: 0.01
+ },
+ hAxis: {
+ format: '#,###.##s'
+ },
+ vAxes: {
+ 0: {title: 'Bytes Touched', format: 'short'},
+ 1: {title: 'Duration', format: '#,###ms'}
+ },
+ height: 400,
+ width: 1000,
+ chartArea: {left: 70, top: 0, right: 160, height: "90%"},
+ // The first series should be a area chart (total bytes touched),
+ series: seriesOptions,
+ // everthing else is a line.
+ seriesType: 'line'
+ };
+ let graphNode = createNode('div', 'chart');
+ let listNode = createNode('div', 'funktion-list');
+ parentNode.appendChild(graphNode);
+ parentNode.appendChild(listNode);
+ let chart = new google.visualization.ComboChart(graphNode);
+ google.visualization.events.addListener(chart, 'select',
+ () => selectGraphPointHandler(chart, data, script, parentNode));
+ chart.draw(data, options);
+ // Add event listeners
+ console.timeEnd(timerLabel);
+}
+
+function selectGraphPointHandler(chart, data, script, parentNode) {
+ let selection = chart.getSelection();
+ if (selection.length <= 0) return;
+ // Display a list of funktions with events at the given time.
+ let {row, column} = selection[0];
+ if (row === null|| column === null) return;
+ const kEntrySize = kUseDuration ? 2 : 1;
+ let [metric, description] = series[((column-1)/ kEntrySize) | 0];
+ let time = data.getValue(row, 0);
+ let funktions = script.getFunktionsAtTime(
+ time * kSecondsToMillis, kSelectionTimespan, metric);
+ let oldList = parentNode.querySelector('.funktion-list');
+ parentNode.replaceChild(
+ createFunktionList(metric, description, time, funktions), oldList);
+}
+
+function createFunktionList(metric, description, time, funktions) {
+ let container = createNode('div', 'funktion-list');
+ container.appendChild(h3('Changes of "' + description + '" at ' +
+ time + 's: ' + funktions.length));
+ let listNode = createNode('ul');
+ funktions.forEach(funktion => {
+ let node = createNode('li', 'funktion');
+ node.funktion = funktion;
+ node.appendChild(text(funktion.toString(false) + " "));
+ let script = funktion.script;
+ if (script) {
+ node.appendChild(a("#script" + script.id, "in script " + script.id));
+ }
+ listNode.appendChild(node);
+ });
+ container.appendChild(listNode);
+ return container;
+}
+</script>
+</head>
+
+<body>
+ <h1>BEHOLD, THIS IS PARSEROR!</h1>
+
+ <h2>Usage</h2>
+ Run your script with <code>--log-function-events</code> and upload <code>v8.log</code> on this page:<br/>
+ <code>/path/to/d8 --log-function-events your_script.js</code>
+
+ <h2>Data</h2>
+ <form name="fileForm">
+ <p>
+ <input id="uploadInput" type="file" name="files" onchange="loadFile();" accept=".log"> trace entries: <span id="count">0</span>
+ </p>
+ </form>
+
+
+ <h2>Scripts</h2>
+ <div id="scripts"></div>
+
+ <h2>Result</h2>
+ <div id="result"></div>
+</body>
+
+</html>