Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
35266 views
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
//===----------------------------------------------------------------------===//
8
9
// Based on selected objects, hide/show sections & populate data from templates.
10
//
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);
18
19
data = Object.create(data);
20
data.selection = selection;
21
for (root of document.querySelectorAll('[data-selection]'))
22
updateSection(root, data);
23
24
for (var k in changes)
25
applyClassIf(k + '-select', classSelector(changes[k]));
26
}
27
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
}
50
}
51
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);
115
}
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);
122
}
123
124
// Handle a mouse event on a region containing selectable items.
125
// This might end up changing the hover state or the selection state.
126
//
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
}
149
}
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));
154
}
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);
185
}
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));
189
}
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;
199
}
200
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);
219
}
220
221