Home | History | Annotate | Download | only in visual
      1 (function() {
      2     'use strict';
      3 
      4     let diameter = 1280;
      5     let radius = diameter / 2;
      6     let innerRadius = radius - 240;
      7 
      8     let cluster = d3.cluster();
      9     cluster.size([ 360, innerRadius ]);
     10 
     11     let line = d3.radialLine();
     12     line.curve(d3.curveBundle.beta(0.85));
     13     line.radius(function(d) { return d.y; });
     14     line.angle(function(d) { return d.x / 180 * Math.PI; });
     15 
     16     let link;
     17     let node;
     18     let selectedNode;
     19     let selectedSubNode;
     20 
     21     function init() {
     22         let domListCol = document.createElement("div");
     23         domListCol.id = "violate_list_column";
     24         let domGraphCol = document.createElement("div");
     25         domGraphCol.id = "dep_graph_column";
     26         let domResetBtn = document.createElement("button");
     27         domResetBtn.id = "reset_btn";
     28         domResetBtn.innerHTML = "Reset";
     29         domGraphCol.appendChild(domResetBtn);
     30 
     31         document.body.appendChild(domListCol);
     32         document.body.appendChild(domGraphCol);
     33 
     34         let canvas = d3.select("#dep_graph_column").append("svg");
     35         canvas.attr("width", diameter + 200);
     36         canvas.attr("height", diameter);
     37 
     38         let svg = canvas.append("g");
     39         svg.attr("transform", "translate(" + (radius + 100) + "," + radius + ")");
     40 
     41         link = svg.append("g").selectAll(".link");
     42         node = svg.append("g").selectAll(".node");
     43 
     44         showResult(depData, violatedLibs);
     45     }
     46 
     47     function showList(depMap, violatedLibs) {
     48         function makeTitle(tagName) {
     49             let domTitle = document.createElement("div");
     50             let domText = document.createElement("h3");
     51             domText.innerHTML = tagName;
     52             domTitle.appendChild(domText);
     53             return domTitle;
     54         }
     55         function makeButton(libName, count) {
     56             let domButton = document.createElement("button");
     57             domButton.className = "violate";
     58             domButton.innerHTML = libName + " (" + count + ")";
     59             domButton.onclick = function() {
     60                 this.classList.toggle("active");
     61                 let currentList = this.nextElementSibling;
     62                 if (currentList.style.display === "block") {
     63                     currentList.style.display = "none";
     64                     selectedNode = undefined;
     65                     if (selectedSubNode) {
     66                         selectedSubNode.classList.toggle("active");
     67                         selectedSubNode.nextElementSibling.style.display = "none";
     68                         selectedSubNode = undefined;
     69                     }
     70                     resetclicked();
     71                 } else {
     72                     currentList.style.display = "block";
     73                     for (let i = 1; i < currentList.childElementCount; i += 2) {
     74                         currentList.childNodes[i].style.display = "none";
     75                     }
     76                     if (selectedNode) {
     77                         selectedNode.classList.toggle("active");
     78                         selectedNode.nextElementSibling.style.display = "none";
     79                         if (selectedSubNode) {
     80                             selectedSubNode.classList.toggle("active");
     81                             selectedSubNode.nextElementSibling.style.display = "none";
     82                             selectedSubNode = undefined;
     83                         }
     84                     }
     85                     selectedNode = domButton;
     86                     mouseclicked(depMap[libName]);
     87                 }
     88             };
     89             return domButton;
     90         }
     91         function makeSubButton(libName, count) {
     92             let domButton = document.createElement("button");
     93             domButton.className = "violate-list";
     94             domButton.innerHTML = libName + " (" + count + ")";
     95             domButton.onclick = function() {
     96                 this.classList.toggle("active");
     97                 let currentSubList = this.nextElementSibling;
     98                 if (currentSubList.style.display === "block") {
     99                     currentSubList.style.display = "none";
    100                     selectedSubNode = undefined;
    101                 } else {
    102                     currentSubList.style.display = "block";
    103                     for (let i = 0; i < currentSubList.childElementCount; i++) {
    104                         if (currentSubList.childNodes[i].childElementCount > 0) {
    105                             currentSubList.childNodes[i].childNodes[1].style.display = "none";
    106                         }
    107                     }
    108                     if (selectedSubNode) {
    109                         selectedSubNode.classList.toggle("active");
    110                         selectedSubNode.nextElementSibling.style.display = "none";
    111                     }
    112                     selectedSubNode = domButton;
    113                 }
    114             };
    115             return domButton;
    116         }
    117         function changeFormat(symbol) {
    118             let res = "";
    119             let i;
    120             for (i = 0; i < symbol.length; i++) {
    121                 if (symbol.charAt(i) >= '0' && symbol.charAt(i) <= '9') {
    122                     break;
    123                 }
    124             }
    125             while (i < symbol.length) {
    126                 if (symbol.charAt(i) < '0' || symbol.charAt(i) > '9') {
    127                     break;
    128                 }
    129                 let len = parseInt(symbol.substr(i, symbol.length));
    130                 let count = 1;
    131                 if (len < 10) {
    132                     count = 0;
    133                 }
    134                 res = res + "::" + symbol.substr(i + 1 + count, len);
    135                 i = i + 1 + count + len;
    136             }
    137             return res.substr(2, res.length);
    138         }
    139         function makeList(domList, list)
    140         {
    141             for (let i = 0; i < list.length; i++) {
    142                 domList.appendChild(makeButton(list[i][0], list[i][1]));
    143                 let domDepList = document.createElement("div");
    144                 let depItem = depMap[list[i][0]];
    145                 let violates = depItem.data.violates;
    146                 for (let j = 0; j < violates.length; j++) {
    147                     let domDepLib = document.createElement("div");
    148                     let tag = depMap[violates[j][0]].data.tag;
    149                     let symbols = violates[j][1];
    150                     let domDepButton = makeSubButton(violates[j][0] + " ["
    151                             + tag.substring(tag.lastIndexOf(".") + 1) + "]", symbols.length);
    152                     for (let k = 0; k < symbols.length; k++) {
    153                         let domDepSym = document.createElement("div");
    154                         domDepSym.className = "violate-list-sym";
    155                         domDepSym.innerHTML = symbols[k];
    156                         if (symbols[k].indexOf("_Z") === 0) {
    157                             let cplusplusSym = document.createElement("span");
    158                             cplusplusSym.className = "cplusplus-sym";
    159                             cplusplusSym.innerHTML =
    160                                 changeFormat(symbols[k].substr(2, symbols[k].length));
    161                             domDepSym.appendChild(cplusplusSym);
    162                             domDepSym.onmouseover = function(e) {
    163                                 e.currentTarget.style.position = "relative";
    164                                 e.currentTarget.childNodes[1].style.display = "block";
    165                             };
    166                             domDepSym.onmouseout = function(e) {
    167                                 e.currentTarget.style.position = "static";
    168                                 e.currentTarget.childNodes[1].style.display = "none";
    169                             };
    170                         }
    171                         domDepLib.appendChild(domDepSym);
    172                     }
    173                     domDepList.appendChild(domDepButton);
    174                     domDepList.appendChild(domDepLib);
    175                 }
    176                 domList.appendChild(domDepList);
    177                 domDepList.style.display = "none";
    178             }
    179         }
    180 
    181         let domViolatedList = document.getElementById("violate_list_column");
    182         if ("vendor.private.bin" in violatedLibs) {
    183             let list = violatedLibs["vendor.private.bin"];
    184             domViolatedList.appendChild(makeTitle("VENDOR (" + list.length + ")"));
    185             makeList(domViolatedList, list);
    186         }
    187         for (let tag in violatedLibs) {
    188             if (tag === "vendor.private.bin")
    189                 continue;
    190             let list = violatedLibs[tag];
    191             if (tag === "system.private.bin")
    192                 tag = "SYSTEM";
    193             else
    194                 tag = tag.substring(tag.lastIndexOf(".") + 1).toUpperCase();
    195             domViolatedList.appendChild(makeTitle(tag + " (" + list.length + ")"));
    196             makeList(domViolatedList, list);
    197         }
    198     }
    199 
    200     function showResult(depDumps, violatedLibs) {
    201         let root = tagHierarchy(depDumps).sum(function(d) { return 1; });
    202         cluster(root);
    203 
    204         let libsDepData = libsDepends(root.leaves());
    205         showList(libsDepData[1], violatedLibs);
    206         link = link.data(libsDepData[0])
    207                    .enter()
    208                    .append("path")
    209                    .each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
    210                    .attr("class", function(d) { return d.allow ? "link" : "link--violate" })
    211                    .attr("d", line);
    212 
    213         node = node.data(root.leaves())
    214                    .enter()
    215                    .append("text")
    216                    .attr("class",
    217                        function(d) {
    218                            return d.data.parent.parent.parent.key == "system" ?
    219                                (d.data.parent.parent.key == "system.public" ?
    220                                         "node--sys-pub" :
    221                                         "node--sys-pri") :
    222                                "node";
    223                        })
    224                    .attr("dy", "0.31em")
    225                    .attr("transform",
    226                        function(d) {
    227                            return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" +
    228                                (d.x < 180 ? "" : "rotate(180)");
    229                        })
    230                    .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
    231                    .text(function(d) { return d.data.key; })
    232                    .on("click", mouseclicked);
    233         document.getElementById("reset_btn").onclick = resetclicked;
    234     }
    235 
    236     function resetclicked() {
    237         if (selectedNode) {
    238             selectedNode.classList.toggle("active");
    239             selectedNode.nextElementSibling.style.display = "none";
    240             if (selectedSubNode) {
    241                 selectedSubNode.classList.toggle("active");
    242                 selectedSubNode.nextElementSibling.style.display = "none";
    243                 selectedSubNode = undefined;
    244             }
    245             selectedNode = undefined;
    246         }
    247         link.classed("link--target", false)
    248             .classed("link--source", false);
    249         node.classed("node--target", false)
    250             .classed("node--source", false)
    251             .classed("node--selected", false);
    252     }
    253 
    254     function mouseclicked(d) {
    255         node.each(function(n) { n.target = n.source = false; });
    256 
    257         link.classed("link--target",
    258                 function(l) {
    259                     if (l.target === d) {
    260                         l.source.source = true;
    261                         return true;
    262                     } else {
    263                         return false;
    264                     }
    265                 })
    266             .classed("link--source",
    267                 function(l) {
    268                     if (l.source === d) {
    269                         l.target.target = true;
    270                         return true;
    271                     } else {
    272                         return false;
    273                     }
    274                 })
    275             .filter(function(l) { return l.target === d || l.source === d; })
    276             .raise();
    277 
    278         node.classed("node--target",
    279                 function(n) {
    280                     return n.target;
    281                 })
    282             .classed("node--source",
    283                 function(n) { return n.source; })
    284             .classed("node--selected",
    285                 function(n) {
    286                     return n === d;
    287                 });
    288     }
    289 
    290     function tagHierarchy(depDumps) {
    291         let map = {};
    292 
    293         function find(name, tag, data) {
    294             let node = map[name], i;
    295             if (!node) {
    296                 node = map[name] = data || { name : name, children : [] };
    297                 if (name.length) {
    298                     node.parent = find(tag, tag.substring(0, tag.lastIndexOf(".")));
    299                     node.parent.children.push(node);
    300                     node.key = name;
    301                 }
    302             }
    303             return node;
    304         }
    305 
    306         depDumps.forEach(function(d) { find(d.name, d.tag, d); });
    307 
    308         return d3.hierarchy(map[""]);
    309     }
    310 
    311     function libsDepends(nodes) {
    312         let map = {}, depends = [];
    313 
    314         // Compute a map from name to node.
    315         nodes.forEach(function(d) { map[d.data.name] = d; });
    316 
    317         // For each dep, construct a link from the source to target node.
    318         nodes.forEach(function(d) {
    319             if (d.data.depends)
    320                 d.data.depends.forEach(function(i) {
    321                     let l = map[d.data.name].path(map[i]);
    322                     l.allow = true;
    323                     depends.push(l);
    324                 });
    325             if (d.data.violates.length) {
    326                 map[d.data.name].not_allow = true;
    327                 d.data.violates.forEach(function(i) {
    328                     map[i[0]].not_allow = true;
    329                     let l = map[d.data.name].path(map[i[0]]);
    330                     l.allow = false;
    331                     depends.push(l);
    332                 });
    333             }
    334         });
    335 
    336         return [ depends, map ];
    337     }
    338 
    339     window.onload = init;
    340 })();
    341