Home | History | Annotate | Download | only in src
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 import * as C from "./constants.js"
      6 import {SourceResolver} from "./source-resolver.js"
      7 import {SelectionBroker} from "./selection-broker.js"
      8 import {DisassemblyView} from "./disassembly-view.js"
      9 import {GraphMultiView} from "./graphmultiview.js"
     10 import {CodeMode, CodeView} from "./code-view.js"
     11 import * as d3 from "d3"
     12 
     13 class Snapper {
     14   resizer: Resizer;
     15   sourceExpand: HTMLElement;
     16   sourceCollapse: HTMLElement;
     17   disassemblyExpand: HTMLElement;
     18   disassemblyCollapse: HTMLElement;
     19 
     20   constructor(resizer: Resizer) {
     21     const snapper = this;
     22     snapper.resizer = resizer;
     23     snapper.sourceExpand = document.getElementById(C.SOURCE_EXPAND_ID);
     24     snapper.sourceCollapse = document.getElementById(C.SOURCE_COLLAPSE_ID);
     25     snapper.disassemblyExpand = document.getElementById(C.DISASSEMBLY_EXPAND_ID);
     26     snapper.disassemblyCollapse = document.getElementById(C.DISASSEMBLY_COLLAPSE_ID);
     27 
     28     document.getElementById("source-collapse").addEventListener("click", function () {
     29       resizer.snapper.toggleSourceExpanded();
     30     });
     31     document.getElementById("disassembly-collapse").addEventListener("click", function () {
     32       resizer.snapper.toggleDisassemblyExpanded();
     33     });
     34   }
     35 
     36   getLastExpandedState(type, default_state) {
     37     var state = window.sessionStorage.getItem("expandedState-" + type);
     38     if (state === null) return default_state;
     39     return state === 'true';
     40   }
     41 
     42   setLastExpandedState(type, state) {
     43     window.sessionStorage.setItem("expandedState-" + type, state);
     44   }
     45 
     46   toggleSourceExpanded(): void {
     47     this.setSourceExpanded(!this.sourceExpand.classList.contains("invisible"));
     48   }
     49 
     50   sourceExpandUpdate(newState: boolean) {
     51     this.setLastExpandedState("source", newState);
     52     this.sourceExpand.classList.toggle("invisible", newState);
     53     this.sourceCollapse.classList.toggle("invisible", !newState);
     54   }
     55 
     56   setSourceExpanded(newState) {
     57     if (this.sourceExpand.classList.contains("invisible") === newState) return;
     58     this.sourceExpandUpdate(newState);
     59     let resizer = this.resizer;
     60     if (newState) {
     61       resizer.sep_left = resizer.sep_left_snap;
     62       resizer.sep_left_snap = 0;
     63     } else {
     64       resizer.sep_left_snap = resizer.sep_left;
     65       resizer.sep_left = 0;
     66     }
     67     resizer.updatePanes();
     68   }
     69 
     70   toggleDisassemblyExpanded() {
     71     this.setDisassemblyExpanded(!this.disassemblyExpand.classList.contains("invisible"));
     72   }
     73 
     74   disassemblyExpandUpdate(newState) {
     75     this.setLastExpandedState("disassembly", newState);
     76     this.disassemblyExpand.classList.toggle("invisible", newState);
     77     this.disassemblyCollapse.classList.toggle("invisible", !newState);
     78   }
     79 
     80   setDisassemblyExpanded(newState) {
     81     if (this.disassemblyExpand.classList.contains("invisible") === newState) return;
     82     this.disassemblyExpandUpdate(newState);
     83     let resizer = this.resizer;
     84     if (newState) {
     85       resizer.sep_right = resizer.sep_right_snap;
     86       resizer.sep_right_snap = resizer.client_width;
     87     } else {
     88       resizer.sep_right_snap = resizer.sep_right;
     89       resizer.sep_right = resizer.client_width;
     90     }
     91     resizer.updatePanes();
     92   }
     93 
     94   panesUpated() {
     95     this.sourceExpandUpdate(this.resizer.sep_left > this.resizer.dead_width);
     96     this.disassemblyExpandUpdate(this.resizer.sep_right <
     97       (this.resizer.client_width - this.resizer.dead_width));
     98   }
     99 }
    100 
    101 class Resizer {
    102   snapper: Snapper;
    103   dead_width: number;
    104   client_width: number;
    105   left: HTMLElement;
    106   right: HTMLElement;
    107   middle: HTMLElement;
    108   sep_left: number;
    109   sep_right: number;
    110   sep_left_snap: number;
    111   sep_right_snap: number;
    112   sep_width_offset: number;
    113   panes_updated_callback: () => void;
    114   resizer_right: d3.Selection<HTMLDivElement, any, any, any>;
    115   resizer_left: d3.Selection<HTMLDivElement, any, any, any>;
    116 
    117   constructor(panes_updated_callback: () => void, dead_width: number) {
    118     let resizer = this;
    119     resizer.snapper = new Snapper(resizer)
    120     resizer.panes_updated_callback = panes_updated_callback;
    121     resizer.dead_width = dead_width
    122     resizer.client_width = document.body.getBoundingClientRect().width;
    123     resizer.left = document.getElementById(C.SOURCE_PANE_ID);
    124     resizer.middle = document.getElementById(C.INTERMEDIATE_PANE_ID);
    125     resizer.right = document.getElementById(C.GENERATED_PANE_ID);
    126     resizer.resizer_left = d3.select('.resizer-left');
    127     resizer.resizer_right = d3.select('.resizer-right');
    128     resizer.sep_left = resizer.client_width / 3;
    129     resizer.sep_right = resizer.client_width / 3 * 2;
    130     resizer.sep_left_snap = 0;
    131     resizer.sep_right_snap = 0;
    132     // Offset to prevent resizers from sliding slightly over one another.
    133     resizer.sep_width_offset = 7;
    134 
    135     let dragResizeLeft = d3.drag()
    136       .on('drag', function () {
    137         let x = d3.mouse(this.parentElement)[0];
    138         resizer.sep_left = Math.min(Math.max(0, x), resizer.sep_right - resizer.sep_width_offset);
    139         resizer.updatePanes();
    140       })
    141       .on('start', function () {
    142         resizer.resizer_left.classed("dragged", true);
    143         let x = d3.mouse(this.parentElement)[0];
    144         if (x > dead_width) {
    145           resizer.sep_left_snap = resizer.sep_left;
    146         }
    147       })
    148       .on('end', function () {
    149         resizer.resizer_left.classed("dragged", false);
    150       });
    151     resizer.resizer_left.call(dragResizeLeft);
    152 
    153     let dragResizeRight = d3.drag()
    154       .on('drag', function () {
    155         let x = d3.mouse(this.parentElement)[0];
    156         resizer.sep_right = Math.max(resizer.sep_left + resizer.sep_width_offset, Math.min(x, resizer.client_width));
    157         resizer.updatePanes();
    158       })
    159       .on('start', function () {
    160         resizer.resizer_right.classed("dragged", true);
    161         let x = d3.mouse(this.parentElement)[0];
    162         if (x < (resizer.client_width - dead_width)) {
    163           resizer.sep_right_snap = resizer.sep_right;
    164         }
    165       })
    166       .on('end', function () {
    167         resizer.resizer_right.classed("dragged", false);
    168       });;
    169     resizer.resizer_right.call(dragResizeRight);
    170     window.onresize = function () {
    171       resizer.updateWidths();
    172       resizer.updatePanes();
    173     };
    174   }
    175 
    176   updatePanes() {
    177     let left_snapped = this.sep_left === 0;
    178     let right_snapped = this.sep_right >= this.client_width - 1;
    179     this.resizer_left.classed("snapped", left_snapped);
    180     this.resizer_right.classed("snapped", right_snapped);
    181     this.left.style.width = this.sep_left + 'px';
    182     this.middle.style.width = (this.sep_right - this.sep_left) + 'px';
    183     this.right.style.width = (this.client_width - this.sep_right) + 'px';
    184     this.resizer_left.style('left', this.sep_left + 'px');
    185     this.resizer_right.style('right', (this.client_width - this.sep_right - 1) + 'px');
    186 
    187     this.snapper.panesUpated();
    188     this.panes_updated_callback();
    189   }
    190 
    191   updateWidths() {
    192     this.client_width = document.body.getBoundingClientRect().width;
    193     this.sep_right = Math.min(this.sep_right, this.client_width);
    194     this.sep_left = Math.min(Math.max(0, this.sep_left), this.sep_right);
    195   }
    196 }
    197 
    198 window.onload = function () {
    199   var svg = null;
    200   var multiview = null;
    201   var disassemblyView = null;
    202   var sourceViews = [];
    203   var selectionBroker = null;
    204   var sourceResolver = null;
    205   let resizer = new Resizer(panesUpdatedCallback, 100);
    206 
    207   function panesUpdatedCallback() {
    208     if (multiview) multiview.onresize();
    209   }
    210 
    211   function loadFile(txtRes) {
    212     // If the JSON isn't properly terminated, assume compiler crashed and
    213     // add best-guess empty termination
    214     if (txtRes[txtRes.length - 2] == ',') {
    215       txtRes += '{"name":"disassembly","type":"disassembly","data":""}]}';
    216     }
    217     try {
    218       sourceViews.forEach((sv) => sv.hide());
    219       if (multiview) multiview.hide();
    220       multiview = null;
    221       if (disassemblyView) disassemblyView.hide();
    222       sourceViews = [];
    223       sourceResolver = new SourceResolver();
    224       selectionBroker = new SelectionBroker(sourceResolver);
    225 
    226       const jsonObj = JSON.parse(txtRes);
    227 
    228       let fnc = jsonObj.function;
    229       // Backwards compatibility.
    230       if (typeof fnc == 'string') {
    231         fnc = {
    232           functionName: fnc,
    233           sourceId: -1,
    234           startPosition: jsonObj.sourcePosition,
    235           endPosition: jsonObj.sourcePosition + jsonObj.source.length,
    236           sourceText: jsonObj.source,
    237           backwardsCompatibility: true
    238         };
    239       }
    240 
    241       sourceResolver.setInlinings(jsonObj.inlinings);
    242       sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
    243       sourceResolver.setSources(jsonObj.sources, fnc)
    244       sourceResolver.setNodePositionMap(jsonObj.nodePositions);
    245       sourceResolver.parsePhases(jsonObj.phases);
    246 
    247       let sourceView = new CodeView(C.SOURCE_PANE_ID, selectionBroker, sourceResolver, fnc, CodeMode.MAIN_SOURCE);
    248       sourceView.show(null, null);
    249       sourceViews.push(sourceView);
    250 
    251       sourceResolver.forEachSource((source) => {
    252         let sourceView = new CodeView(C.SOURCE_PANE_ID, selectionBroker, sourceResolver, source, CodeMode.INLINED_SOURCE);
    253         sourceView.show(null, null);
    254         sourceViews.push(sourceView);
    255       });
    256 
    257       disassemblyView = new DisassemblyView(C.GENERATED_PANE_ID, selectionBroker);
    258       disassemblyView.initializeCode(fnc.sourceText);
    259       if (sourceResolver.disassemblyPhase) {
    260         disassemblyView.initializePerfProfile(jsonObj.eventCounts);
    261         disassemblyView.show(sourceResolver.disassemblyPhase.data, null);
    262       }
    263 
    264       multiview = new GraphMultiView(C.INTERMEDIATE_PANE_ID, selectionBroker, sourceResolver);
    265       multiview.show(jsonObj);
    266     } catch (err) {
    267       if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" +
    268         "error: " + err.message + "\nDo you want to clear session storage?")) {
    269         window.sessionStorage.clear();
    270       }
    271       return;
    272     }
    273   }
    274 
    275   function initializeUploadHandlers() {
    276     // The <input> form #upload-helper with type file can't be a picture.
    277     // We hence keep it hidden, and forward the click from the picture
    278     // button #upload.
    279     d3.select("#upload").on("click",
    280       () => document.getElementById("upload-helper").click());
    281     d3.select("#upload-helper").on("change", function (this: HTMLInputElement) {
    282       var uploadFile = this.files && this.files[0];
    283       var filereader = new FileReader();
    284       filereader.onload = function (e) {
    285         var txtRes = e.target.result;
    286         loadFile(txtRes);
    287       };
    288       if (uploadFile)
    289         filereader.readAsText(uploadFile);
    290     });
    291   }
    292 
    293   initializeUploadHandlers();
    294 
    295 
    296   resizer.snapper.setSourceExpanded(resizer.snapper.getLastExpandedState("source", true));
    297   resizer.snapper.setDisassemblyExpanded(resizer.snapper.getLastExpandedState("disassembly", false));
    298 
    299   resizer.updatePanes();
    300 
    301 };
    302