Path: blob/main/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
35266 views
//===-- HTMLLogger.js -----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78// Based on selected objects, hide/show sections & populate data from templates.9//10// For example, if the selection is {bb="BB4", elt="BB4.6" iter="BB4:2"}:11// - show the "block" and "element" sections12// - re-render templates within these sections (if selection changed)13// - apply "bb-select" to items with class class "BB4", etc14let selection = {};15function updateSelection(changes, data) {16Object.assign(selection, changes);1718data = Object.create(data);19data.selection = selection;20for (root of document.querySelectorAll('[data-selection]'))21updateSection(root, data);2223for (var k in changes)24applyClassIf(k + '-select', classSelector(changes[k]));25}2627// Given <section data-selection="x,y">:28// - hide section if selections x or y are null29// - re-render templates if x or y have changed30function updateSection(root, data) {31let changed = root.selection == null;32root.selection ||= {};33for (key of root.dataset.selection.split(',')) {34if (!key) continue;35if (data.selection[key] != root.selection[key]) {36root.selection[key] = data.selection[key];37changed = true;38}39if (data.selection[key] == null) {40root.hidden = true;41return;42}43}44if (changed) {45root.hidden = false;46for (tmpl of root.getElementsByTagName('template'))47reinflate(tmpl, data);48}49}5051// Expands template `tmpl` based on input `data`:52// - interpolates {{expressions}} in text and attributes53// - <template> tags can modify expansion: if, for etc54// Outputs to `parent` element, inserting before `next`.55function inflate(tmpl, data, parent, next) {56// We use eval() as our expression language in templates!57// The templates are static and trusted.58let evalExpr = (expr, data) => eval('with (data) { ' + expr + ' }');59let interpolate = (str, data) =>60str.replace(/\{\{(.*?)\}\}/g, (_, expr) => evalExpr(expr, data))61// Anything other than <template> tag: copy, interpolate, recursively inflate.62if (tmpl.nodeName != 'TEMPLATE') {63let clone = tmpl.cloneNode();64clone.inflated = true;65if (clone instanceof Text)66clone.textContent = interpolate(clone.textContent, data);67if (clone instanceof Element) {68for (attr of clone.attributes)69attr.value = interpolate(attr.value, data);70for (c of tmpl.childNodes)71inflate(c, data, clone, /*next=*/null);72}73return parent.insertBefore(clone, next);74}75// data-use="xyz": use <template id="xyz"> instead. (Allows recursion.)76if ('use' in tmpl.dataset)77return inflate(document.getElementById(tmpl.dataset.use), data, parent, next);78// <template> tag handling. Base case: recursively inflate.79function handle(data) {80for (c of tmpl.content.childNodes)81inflate(c, data, parent, next);82}83// Directives on <template> tags modify behavior.84const directives = {85// data-for="x in expr": expr is enumerable, bind x to each in turn86'for': (nameInExpr, data, proceed) => {87let [name, expr] = nameInExpr.split(' in ');88let newData = Object.create(data);89let index = 0;90for (val of evalExpr(expr, data) || []) {91newData[name] = val;92newData[name + '_index'] = index++;93proceed(newData);94}95},96// data-if="expr": only include contents if expression is truthy97'if': (expr, data, proceed) => { if (evalExpr(expr, data)) proceed(data); },98// data-let="x = expr": bind x to value of expr99'let': (nameEqExpr, data, proceed) => {100let [name, expr] = nameEqExpr.split(' = ');101let newData = Object.create(data);102newData[name] = evalExpr(expr, data);103proceed(newData);104},105}106// Compose directive handlers on top of the base handler.107for (let [dir, value] of Object.entries(tmpl.dataset).reverse()) {108if (dir in directives) {109let proceed = handle;110handle = (data) => directives[dir](value, data, proceed);111}112}113handle(data);114}115// Expand a template, after first removing any prior expansion of it.116function reinflate(tmpl, data) {117// Clear previously rendered template contents.118while (tmpl.nextSibling && tmpl.nextSibling.inflated)119tmpl.parentNode.removeChild(tmpl.nextSibling);120inflate(tmpl, data, tmpl.parentNode, tmpl.nextSibling);121}122123// Handle a mouse event on a region containing selectable items.124// This might end up changing the hover state or the selection state.125//126// targetSelector describes what target HTML element is selectable.127// targetToID specifies how to determine the selection from it:128// hover: a function from target to the class name to highlight129// bb: a function from target to the basic-block name to select (BB4)130// elt: a function from target to the CFG element name to select (BB4.5)131// iter: a function from target to the BB iteration to select (BB4:2)132// If an entry is missing, the selection is unmodified.133// If an entry is null, the selection is always cleared.134function mouseEventHandler(event, targetSelector, targetToID, data) {135var target = event.type == "mouseout" ? null : event.target.closest(targetSelector);136let selTarget = k => (target && targetToID[k]) ? targetToID[k](target) : null;137if (event.type == "click") {138let newSel = {};139for (var k in targetToID) {140if (k == 'hover') continue;141let t = selTarget(k);142newSel[k] = t;143}144updateSelection(newSel, data);145} else if ("hover" in targetToID) {146applyClassIf("hover", classSelector(selTarget("hover")));147}148}149function watch(rootSelector, targetSelector, targetToID, data) {150var root = document.querySelector(rootSelector);151for (event of ['mouseout', 'mousemove', 'click'])152root.addEventListener(event, e => mouseEventHandler(e, targetSelector, targetToID, data));153}154function watchSelection(data) {155let lastIter = (bb) => `${bb}:${data.cfg[bb].iters}`;156watch('#code', '.c', {157hover: e => e.dataset.elt,158bb: e => e.dataset.bb,159elt: e => e.dataset.elt,160// If we're already viewing an iteration of this BB, stick with the same.161iter: e => (selection.iter && selection.bb == e.dataset.bb) ? selection.iter : lastIter(e.dataset.bb),162}, data);163watch('#cfg', '.bb', {164hover: e => e.id,165bb: e => e.id,166elt: e => e.id + ".0",167iter: e => lastIter(e.id),168}, data);169watch('#timeline', '.entry', {170hover: e => [e.id, e.dataset.bb],171bb: e => e.dataset.bb,172elt: e => e.dataset.bb + ".0",173iter: e => e.id,174}, data);175watch('#bb-elements', 'tr', {176hover: e => e.id,177elt: e => e.id,178}, data);179watch('#iterations', '.chooser', {180hover: e => e.dataset.iter,181iter: e => e.dataset.iter,182}, data);183updateSelection({}, data);184}185function applyClassIf(cls, query) {186document.querySelectorAll('.' + cls).forEach(elt => elt.classList.remove(cls));187document.querySelectorAll(query).forEach(elt => elt.classList.add(cls));188}189// Turns a class name into a CSS selector matching it, with some wrinkles:190// - we treat id="foo" just like class="foo" to avoid repetition in the HTML191// - cls can be an array of strings, we match them all192function classSelector(cls) {193if (cls == null) return null;194if (Array.isArray(cls)) return cls.map(classSelector).join(', ');195var escaped = cls.replace('.', '\\.').replace(':', '\\:');196// don't require id="foo" class="foo"197return '.' + escaped + ", #" + escaped;198}199200// Add a stylesheet defining colors for n basic blocks.201function addBBColors(n) {202let sheet = new CSSStyleSheet();203// hex values to subtract from fff to get a base color204options = [0x001, 0x010, 0x011, 0x100, 0x101, 0x110, 0x111];205function color(hex) {206return "#" + hex.toString(16).padStart(3, "0");207}208function add(selector, property, hex) {209sheet.insertRule(`${selector} { ${property}: ${color(hex)}; }`)210}211for (var i = 0; i < n; ++i) {212let opt = options[i%options.length];213add(`.B${i}`, 'background-color', 0xfff - 2*opt);214add(`#B${i} polygon`, 'fill', 0xfff - 2*opt);215add(`#B${i} polygon`, 'stroke', 0x888 - 4*opt);216}217document.adoptedStyleSheets.push(sheet);218}219220221