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