| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as C from "./constants.js" |
| import {SourceResolver} from "./source-resolver.js" |
| import {SelectionBroker} from "./selection-broker.js" |
| import {DisassemblyView} from "./disassembly-view.js" |
| import {GraphMultiView} from "./graphmultiview.js" |
| import {CodeMode, CodeView} from "./code-view.js" |
| import * as d3 from "d3" |
| |
| class Snapper { |
| resizer: Resizer; |
| sourceExpand: HTMLElement; |
| sourceCollapse: HTMLElement; |
| disassemblyExpand: HTMLElement; |
| disassemblyCollapse: HTMLElement; |
| |
| constructor(resizer: Resizer) { |
| const snapper = this; |
| snapper.resizer = resizer; |
| snapper.sourceExpand = document.getElementById(C.SOURCE_EXPAND_ID); |
| snapper.sourceCollapse = document.getElementById(C.SOURCE_COLLAPSE_ID); |
| snapper.disassemblyExpand = document.getElementById(C.DISASSEMBLY_EXPAND_ID); |
| snapper.disassemblyCollapse = document.getElementById(C.DISASSEMBLY_COLLAPSE_ID); |
| |
| document.getElementById("source-collapse").addEventListener("click", function () { |
| resizer.snapper.toggleSourceExpanded(); |
| }); |
| document.getElementById("disassembly-collapse").addEventListener("click", function () { |
| resizer.snapper.toggleDisassemblyExpanded(); |
| }); |
| } |
| |
| getLastExpandedState(type, default_state) { |
| var state = window.sessionStorage.getItem("expandedState-" + type); |
| if (state === null) return default_state; |
| return state === 'true'; |
| } |
| |
| setLastExpandedState(type, state) { |
| window.sessionStorage.setItem("expandedState-" + type, state); |
| } |
| |
| toggleSourceExpanded(): void { |
| this.setSourceExpanded(!this.sourceExpand.classList.contains("invisible")); |
| } |
| |
| sourceExpandUpdate(newState: boolean) { |
| this.setLastExpandedState("source", newState); |
| this.sourceExpand.classList.toggle("invisible", newState); |
| this.sourceCollapse.classList.toggle("invisible", !newState); |
| } |
| |
| setSourceExpanded(newState) { |
| if (this.sourceExpand.classList.contains("invisible") === newState) return; |
| this.sourceExpandUpdate(newState); |
| let resizer = this.resizer; |
| if (newState) { |
| resizer.sep_left = resizer.sep_left_snap; |
| resizer.sep_left_snap = 0; |
| } else { |
| resizer.sep_left_snap = resizer.sep_left; |
| resizer.sep_left = 0; |
| } |
| resizer.updatePanes(); |
| } |
| |
| toggleDisassemblyExpanded() { |
| this.setDisassemblyExpanded(!this.disassemblyExpand.classList.contains("invisible")); |
| } |
| |
| disassemblyExpandUpdate(newState) { |
| this.setLastExpandedState("disassembly", newState); |
| this.disassemblyExpand.classList.toggle("invisible", newState); |
| this.disassemblyCollapse.classList.toggle("invisible", !newState); |
| } |
| |
| setDisassemblyExpanded(newState) { |
| if (this.disassemblyExpand.classList.contains("invisible") === newState) return; |
| this.disassemblyExpandUpdate(newState); |
| let resizer = this.resizer; |
| if (newState) { |
| resizer.sep_right = resizer.sep_right_snap; |
| resizer.sep_right_snap = resizer.client_width; |
| } else { |
| resizer.sep_right_snap = resizer.sep_right; |
| resizer.sep_right = resizer.client_width; |
| } |
| resizer.updatePanes(); |
| } |
| |
| panesUpated() { |
| this.sourceExpandUpdate(this.resizer.sep_left > this.resizer.dead_width); |
| this.disassemblyExpandUpdate(this.resizer.sep_right < |
| (this.resizer.client_width - this.resizer.dead_width)); |
| } |
| } |
| |
| class Resizer { |
| snapper: Snapper; |
| dead_width: number; |
| client_width: number; |
| left: HTMLElement; |
| right: HTMLElement; |
| middle: HTMLElement; |
| sep_left: number; |
| sep_right: number; |
| sep_left_snap: number; |
| sep_right_snap: number; |
| sep_width_offset: number; |
| panes_updated_callback: () => void; |
| resizer_right: d3.Selection<HTMLDivElement, any, any, any>; |
| resizer_left: d3.Selection<HTMLDivElement, any, any, any>; |
| |
| constructor(panes_updated_callback: () => void, dead_width: number) { |
| let resizer = this; |
| resizer.snapper = new Snapper(resizer) |
| resizer.panes_updated_callback = panes_updated_callback; |
| resizer.dead_width = dead_width |
| resizer.client_width = document.body.getBoundingClientRect().width; |
| resizer.left = document.getElementById(C.SOURCE_PANE_ID); |
| resizer.middle = document.getElementById(C.INTERMEDIATE_PANE_ID); |
| resizer.right = document.getElementById(C.GENERATED_PANE_ID); |
| resizer.resizer_left = d3.select('.resizer-left'); |
| resizer.resizer_right = d3.select('.resizer-right'); |
| resizer.sep_left = resizer.client_width / 3; |
| resizer.sep_right = resizer.client_width / 3 * 2; |
| resizer.sep_left_snap = 0; |
| resizer.sep_right_snap = 0; |
| // Offset to prevent resizers from sliding slightly over one another. |
| resizer.sep_width_offset = 7; |
| |
| let dragResizeLeft = d3.drag() |
| .on('drag', function () { |
| let x = d3.mouse(this.parentElement)[0]; |
| resizer.sep_left = Math.min(Math.max(0, x), resizer.sep_right - resizer.sep_width_offset); |
| resizer.updatePanes(); |
| }) |
| .on('start', function () { |
| resizer.resizer_left.classed("dragged", true); |
| let x = d3.mouse(this.parentElement)[0]; |
| if (x > dead_width) { |
| resizer.sep_left_snap = resizer.sep_left; |
| } |
| }) |
| .on('end', function () { |
| resizer.resizer_left.classed("dragged", false); |
| }); |
| resizer.resizer_left.call(dragResizeLeft); |
| |
| let dragResizeRight = d3.drag() |
| .on('drag', function () { |
| let x = d3.mouse(this.parentElement)[0]; |
| resizer.sep_right = Math.max(resizer.sep_left + resizer.sep_width_offset, Math.min(x, resizer.client_width)); |
| resizer.updatePanes(); |
| }) |
| .on('start', function () { |
| resizer.resizer_right.classed("dragged", true); |
| let x = d3.mouse(this.parentElement)[0]; |
| if (x < (resizer.client_width - dead_width)) { |
| resizer.sep_right_snap = resizer.sep_right; |
| } |
| }) |
| .on('end', function () { |
| resizer.resizer_right.classed("dragged", false); |
| });; |
| resizer.resizer_right.call(dragResizeRight); |
| window.onresize = function () { |
| resizer.updateWidths(); |
| resizer.updatePanes(); |
| }; |
| } |
| |
| updatePanes() { |
| let left_snapped = this.sep_left === 0; |
| let right_snapped = this.sep_right >= this.client_width - 1; |
| this.resizer_left.classed("snapped", left_snapped); |
| this.resizer_right.classed("snapped", right_snapped); |
| this.left.style.width = this.sep_left + 'px'; |
| this.middle.style.width = (this.sep_right - this.sep_left) + 'px'; |
| this.right.style.width = (this.client_width - this.sep_right) + 'px'; |
| this.resizer_left.style('left', this.sep_left + 'px'); |
| this.resizer_right.style('right', (this.client_width - this.sep_right - 1) + 'px'); |
| |
| this.snapper.panesUpated(); |
| this.panes_updated_callback(); |
| } |
| |
| updateWidths() { |
| this.client_width = document.body.getBoundingClientRect().width; |
| this.sep_right = Math.min(this.sep_right, this.client_width); |
| this.sep_left = Math.min(Math.max(0, this.sep_left), this.sep_right); |
| } |
| } |
| |
| window.onload = function () { |
| var svg = null; |
| var multiview = null; |
| var disassemblyView = null; |
| var sourceViews = []; |
| var selectionBroker = null; |
| var sourceResolver = null; |
| let resizer = new Resizer(panesUpdatedCallback, 100); |
| |
| function panesUpdatedCallback() { |
| if (multiview) multiview.onresize(); |
| } |
| |
| function loadFile(txtRes) { |
| // If the JSON isn't properly terminated, assume compiler crashed and |
| // add best-guess empty termination |
| if (txtRes[txtRes.length - 2] == ',') { |
| txtRes += '{"name":"disassembly","type":"disassembly","data":""}]}'; |
| } |
| try { |
| sourceViews.forEach((sv) => sv.hide()); |
| if (multiview) multiview.hide(); |
| multiview = null; |
| if (disassemblyView) disassemblyView.hide(); |
| sourceViews = []; |
| sourceResolver = new SourceResolver(); |
| selectionBroker = new SelectionBroker(sourceResolver); |
| |
| const jsonObj = JSON.parse(txtRes); |
| |
| let fnc = jsonObj.function; |
| // Backwards compatibility. |
| if (typeof fnc == 'string') { |
| fnc = { |
| functionName: fnc, |
| sourceId: -1, |
| startPosition: jsonObj.sourcePosition, |
| endPosition: jsonObj.sourcePosition + jsonObj.source.length, |
| sourceText: jsonObj.source, |
| backwardsCompatibility: true |
| }; |
| } |
| |
| sourceResolver.setInlinings(jsonObj.inlinings); |
| sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition); |
| sourceResolver.setSources(jsonObj.sources, fnc) |
| sourceResolver.setNodePositionMap(jsonObj.nodePositions); |
| sourceResolver.parsePhases(jsonObj.phases); |
| |
| let sourceView = new CodeView(C.SOURCE_PANE_ID, selectionBroker, sourceResolver, fnc, CodeMode.MAIN_SOURCE); |
| sourceView.show(null, null); |
| sourceViews.push(sourceView); |
| |
| sourceResolver.forEachSource((source) => { |
| let sourceView = new CodeView(C.SOURCE_PANE_ID, selectionBroker, sourceResolver, source, CodeMode.INLINED_SOURCE); |
| sourceView.show(null, null); |
| sourceViews.push(sourceView); |
| }); |
| |
| disassemblyView = new DisassemblyView(C.GENERATED_PANE_ID, selectionBroker); |
| disassemblyView.initializeCode(fnc.sourceText); |
| if (sourceResolver.disassemblyPhase) { |
| disassemblyView.initializePerfProfile(jsonObj.eventCounts); |
| disassemblyView.show(sourceResolver.disassemblyPhase.data, null); |
| } |
| |
| multiview = new GraphMultiView(C.INTERMEDIATE_PANE_ID, selectionBroker, sourceResolver); |
| multiview.show(jsonObj); |
| } catch (err) { |
| if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" + |
| "error: " + err.message + "\nDo you want to clear session storage?")) { |
| window.sessionStorage.clear(); |
| } |
| return; |
| } |
| } |
| |
| function initializeUploadHandlers() { |
| // The <input> form #upload-helper with type file can't be a picture. |
| // We hence keep it hidden, and forward the click from the picture |
| // button #upload. |
| d3.select("#upload").on("click", |
| () => document.getElementById("upload-helper").click()); |
| d3.select("#upload-helper").on("change", function (this: HTMLInputElement) { |
| var uploadFile = this.files && this.files[0]; |
| var filereader = new FileReader(); |
| filereader.onload = function (e) { |
| var txtRes = e.target.result; |
| loadFile(txtRes); |
| }; |
| if (uploadFile) |
| filereader.readAsText(uploadFile); |
| }); |
| } |
| |
| initializeUploadHandlers(); |
| |
| |
| resizer.snapper.setSourceExpanded(resizer.snapper.getLastExpandedState("source", true)); |
| resizer.snapper.setDisassemblyExpanded(resizer.snapper.getLastExpandedState("disassembly", false)); |
| |
| resizer.updatePanes(); |
| |
| }; |