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>