1// Copyright 2014 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
5document.onload = (function(d3){
6  "use strict";
7  var jsonObj;
8  var sourceExpandClassList = document.getElementById(SOURCE_EXPAND_ID).classList;
9  var sourceCollapseClassList = document.getElementById(SOURCE_COLLAPSE_ID).classList;
10  var sourceExpanded = sourceCollapseClassList.contains(COLLAPSE_PANE_BUTTON_VISIBLE);
11  var disassemblyExpandClassList = document.getElementById(DISASSEMBLY_EXPAND_ID).classList;
12  var disassemblyCollapseClassList = document.getElementById(DISASSEMBLY_COLLAPSE_ID).classList;
13  var disassemblyExpanded = disassemblyCollapseClassList.contains(COLLAPSE_PANE_BUTTON_VISIBLE);
14  var svg  = null;
15  var graph = null;
16  var schedule = null;
17  var empty = null;
18  var currentPhaseView = null;
19  var disassemblyView = null;
20  var sourceView = null;
21  var selectionBroker = null;
22
23  function updatePanes() {
24    if (sourceExpanded) {
25      if (disassemblyExpanded) {
26        d3.select("#" + SOURCE_PANE_ID).style(WIDTH, "30%");
27        d3.select("#" + INTERMEDIATE_PANE_ID).style(WIDTH, "40%");
28        d3.select("#" + GENERATED_PANE_ID).style(WIDTH, "30%");
29      } else {
30        d3.select("#" + SOURCE_PANE_ID).style(WIDTH, "50%");
31        d3.select("#" + INTERMEDIATE_PANE_ID).style(WIDTH, "50%");
32        d3.select("#" + GENERATED_PANE_ID).style(WIDTH, "0%");
33      }
34    } else {
35      if (disassemblyExpanded) {
36        d3.select("#" + SOURCE_PANE_ID).style(WIDTH, "0%");
37        d3.select("#" + INTERMEDIATE_PANE_ID).style(WIDTH, "50%");
38        d3.select("#" + GENERATED_PANE_ID).style(WIDTH, "50%");
39      } else {
40        d3.select("#" + SOURCE_PANE_ID).style(WIDTH, "0%");
41        d3.select("#" + INTERMEDIATE_PANE_ID).style(WIDTH, "100%");
42        d3.select("#" + GENERATED_PANE_ID).style(WIDTH, "0%");
43      }
44    }
45  }
46
47  function getLastExpandedState(type, default_state) {
48    var state = window.sessionStorage.getItem("expandedState-"+type);
49    if (state === null) return default_state;
50    return state === 'true';
51  }
52
53  function setLastExpandedState(type, state) {
54    window.sessionStorage.setItem("expandedState-"+type, state);
55  }
56
57  function toggleSourceExpanded() {
58    setSourceExpanded(!sourceExpanded);
59  }
60
61  function setSourceExpanded(newState) {
62    sourceExpanded = newState;
63    setLastExpandedState("source", newState);
64    updatePanes();
65    if (newState) {
66      sourceCollapseClassList.add(COLLAPSE_PANE_BUTTON_VISIBLE);
67      sourceCollapseClassList.remove(COLLAPSE_PANE_BUTTON_INVISIBLE);
68      sourceExpandClassList.add(COLLAPSE_PANE_BUTTON_INVISIBLE);
69      sourceExpandClassList.remove(COLLAPSE_PANE_BUTTON_VISIBLE);
70    } else {
71      sourceCollapseClassList.add(COLLAPSE_PANE_BUTTON_INVISIBLE);
72      sourceCollapseClassList.remove(COLLAPSE_PANE_BUTTON_VISIBLE);
73      sourceExpandClassList.add(COLLAPSE_PANE_BUTTON_VISIBLE);
74      sourceExpandClassList.remove(COLLAPSE_PANE_BUTTON_INVISIBLE);
75    }
76  }
77
78  function toggleDisassemblyExpanded() {
79    setDisassemblyExpanded(!disassemblyExpanded);
80  }
81
82  function setDisassemblyExpanded(newState) {
83    disassemblyExpanded = newState;
84    setLastExpandedState("disassembly", newState);
85    updatePanes();
86    if (newState) {
87      disassemblyCollapseClassList.add(COLLAPSE_PANE_BUTTON_VISIBLE);
88      disassemblyCollapseClassList.remove(COLLAPSE_PANE_BUTTON_INVISIBLE);
89      disassemblyExpandClassList.add(COLLAPSE_PANE_BUTTON_INVISIBLE);
90      disassemblyExpandClassList.remove(COLLAPSE_PANE_BUTTON_VISIBLE);
91    } else {
92      disassemblyCollapseClassList.add(COLLAPSE_PANE_BUTTON_INVISIBLE);
93      disassemblyCollapseClassList.remove(COLLAPSE_PANE_BUTTON_VISIBLE);
94      disassemblyExpandClassList.add(COLLAPSE_PANE_BUTTON_VISIBLE);
95      disassemblyExpandClassList.remove(COLLAPSE_PANE_BUTTON_INVISIBLE);
96    }
97  }
98
99  function hideCurrentPhase() {
100    var rememberedSelection = null;
101    if (currentPhaseView != null) {
102      rememberedSelection = currentPhaseView.detachSelection();
103      currentPhaseView.hide();
104      currentPhaseView = null;
105    }
106    return rememberedSelection;
107  }
108
109  function displayPhaseView(view, data) {
110    var rememberedSelection = hideCurrentPhase();
111    view.show(data, rememberedSelection);
112    d3.select("#middle").classed("scrollable", view.isScrollable());
113    currentPhaseView = view;
114  }
115
116  function displayPhase(phase) {
117    if (phase.type == 'graph') {
118      displayPhaseView(graph, phase.data);
119    } else if (phase.type == 'schedule') {
120      displayPhaseView(schedule, phase.data);
121    } else {
122      displayPhaseView(empty, null);
123    }
124  }
125
126  function fitPanesToParents() {
127    d3.select("#left").classed("scrollable", false)
128    d3.select("#right").classed("scrollable", false);
129
130    graph.fitGraphViewToWindow();
131    disassemblyView.resizeToParent();
132    sourceView.resizeToParent();
133
134    d3.select("#left").classed("scrollable", true);
135    d3.select("#right").classed("scrollable", true);
136  }
137
138  selectionBroker = new SelectionBroker();
139
140  function initializeHandlers(g) {
141    d3.select("#source-collapse").on("click", function(){
142      toggleSourceExpanded(true);
143      setTimeout(function(){
144        g.fitGraphViewToWindow();
145      }, 300);
146    });
147    d3.select("#disassembly-collapse").on("click", function(){
148      toggleDisassemblyExpanded();
149      setTimeout(function(){
150        g.fitGraphViewToWindow();
151      }, 300);
152    });
153    window.onresize = function(){
154      fitPanesToParents();
155    };
156    d3.select("#hidden-file-upload").on("change", function() {
157      if (window.File && window.FileReader && window.FileList) {
158        var uploadFile = this.files[0];
159        var filereader = new window.FileReader();
160        var consts = Node.consts;
161        filereader.onload = function(){
162          var txtRes = filereader.result;
163          // If the JSON isn't properly terminated, assume compiler crashed and
164          // add best-guess empty termination
165          if (txtRes[txtRes.length-2] == ',') {
166            txtRes += '{"name":"disassembly","type":"disassembly","data":""}]}';
167          }
168          try{
169            jsonObj = JSON.parse(txtRes);
170
171            hideCurrentPhase();
172
173            selectionBroker.setNodePositionMap(jsonObj.nodePositions);
174
175            sourceView.initializeCode(jsonObj.source, jsonObj.sourcePosition);
176            disassemblyView.initializeCode(jsonObj.source);
177
178            var selectMenu = document.getElementById('display-selector');
179            var disassemblyPhase = null;
180            selectMenu.innerHTML = '';
181            for (var i = 0; i < jsonObj.phases.length; ++i) {
182              var optionElement = document.createElement("option");
183              optionElement.text = jsonObj.phases[i].name;
184              if (optionElement.text == 'disassembly') {
185                disassemblyPhase = jsonObj.phases[i];
186              } else {
187                selectMenu.add(optionElement, null);
188              }
189            }
190
191            disassemblyView.initializePerfProfile(jsonObj.eventCounts);
192            disassemblyView.show(disassemblyPhase.data, null);
193
194            var initialPhaseIndex = +window.sessionStorage.getItem("lastSelectedPhase");
195            if (!(initialPhaseIndex in jsonObj.phases)) {
196              initialPhaseIndex = 0;
197            }
198
199            // We wish to show the remembered phase {lastSelectedPhase}, but
200            // this will crash if the first view we switch to is a
201            // ScheduleView. So we first switch to the first phase, which
202            // should never be a ScheduleView.
203            displayPhase(jsonObj.phases[0]);
204            displayPhase(jsonObj.phases[initialPhaseIndex]);
205            selectMenu.selectedIndex = initialPhaseIndex;
206
207            selectMenu.onchange = function(item) {
208              window.sessionStorage.setItem("lastSelectedPhase", selectMenu.selectedIndex);
209              displayPhase(jsonObj.phases[selectMenu.selectedIndex]);
210            }
211
212            fitPanesToParents();
213
214            d3.select("#search-input").attr("value", window.sessionStorage.getItem("lastSearch") || "");
215
216          }
217          catch(err) {
218            window.console.log("caught exception, clearing session storage just in case");
219            window.sessionStorage.clear(); // just in case
220            window.console.log("showing error");
221            window.alert("Invalid TurboFan JSON file\n" +
222                         "error: " + err.message);
223            return;
224          }
225        };
226        filereader.readAsText(uploadFile);
227      } else {
228        alert("Can't load graph");
229      }
230    });
231  }
232
233  sourceView = new CodeView(SOURCE_PANE_ID, PR, "", 0, selectionBroker);
234  disassemblyView = new DisassemblyView(DISASSEMBLY_PANE_ID, selectionBroker);
235  graph = new GraphView(d3, GRAPH_PANE_ID, [], [], selectionBroker);
236  schedule = new ScheduleView(SCHEDULE_PANE_ID, selectionBroker);
237  empty = new EmptyView(EMPTY_PANE_ID, selectionBroker);
238
239  initializeHandlers(graph);
240
241  setSourceExpanded(getLastExpandedState("source", true));
242  setDisassemblyExpanded(getLastExpandedState("disassembly", false));
243
244  displayPhaseView(empty, null);
245  fitPanesToParents();
246})(window.d3);
247