| OLD | NEW | 
|---|
| 1 // Copyright 2017 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 "use strict" | 5 "use strict" | 
| 6 | 6 | 
| 7 function $(id) { | 7 function $(id) { | 
| 8   return document.getElementById(id); | 8   return document.getElementById(id); | 
| 9 } | 9 } | 
| 10 | 10 | 
| 11 let components = []; | 11 let components = []; | 
| 12 | 12 | 
| 13 function createViews() { | 13 function createViews() { | 
| 14   components.push(new CallTreeView()); | 14   components.push(new CallTreeView()); | 
| 15   components.push(new TimelineView()); | 15   components.push(new TimelineView()); | 
| 16   components.push(new HelpView()); | 16   components.push(new HelpView()); | 
|  | 17   components.push(new SummaryView()); | 
|  | 18   components.push(new ModeBarView()); | 
| 17 | 19 | 
| 18   let modeBar = $("mode-bar"); | 20   main.setMode("summary"); | 
| 19 |  | 
| 20   function addMode(id, text, active) { |  | 
| 21     let div = document.createElement("div"); |  | 
| 22     div.classList = "mode-button" + (active ? " active-mode-button" : ""); |  | 
| 23     div.id = "mode-" + id; |  | 
| 24     div.textContent = text; |  | 
| 25     div.onclick = () => { |  | 
| 26       if (main.currentState.callTree.mode === id) return; |  | 
| 27       let old = $("mode-" + main.currentState.callTree.mode); |  | 
| 28       old.classList = "mode-button"; |  | 
| 29       div.classList = "mode-button active-mode-button"; |  | 
| 30       main.setMode(id); |  | 
| 31     }; |  | 
| 32     modeBar.appendChild(div); |  | 
| 33   } |  | 
| 34 |  | 
| 35   addMode("bottom-up", "Bottom up", true); |  | 
| 36   addMode("top-down", "Top down"); |  | 
| 37   addMode("function-list", "Functions"); |  | 
| 38 |  | 
| 39   main.setMode("bottom-up"); |  | 
| 40 } | 21 } | 
| 41 | 22 | 
| 42 function emptyState() { | 23 function emptyState() { | 
| 43   return { | 24   return { | 
| 44     file : null, | 25     file : null, | 
|  | 26     mode : "none", | 
|  | 27     currentCodeId : null, | 
| 45     start : 0, | 28     start : 0, | 
| 46     end : Infinity, | 29     end : Infinity, | 
| 47     timeLine : { | 30     timeLine : { | 
| 48       width : 100, | 31       width : 100, | 
| 49       height : 100 | 32       height : 100 | 
| 50     }, | 33     }, | 
| 51     callTree : { | 34     callTree : { | 
| 52       mode : "none", |  | 
| 53       attribution : "js-exclude-bc", | 35       attribution : "js-exclude-bc", | 
| 54       categories : "code-type", | 36       categories : "code-type", | 
| 55       sort : "time" | 37       sort : "time" | 
| 56     } | 38     } | 
| 57   }; | 39   }; | 
| 58 } | 40 } | 
| 59 | 41 | 
| 60 function setCallTreeState(state, callTreeState) { | 42 function setCallTreeState(state, callTreeState) { | 
| 61   state = Object.assign({}, state); | 43   state = Object.assign({}, state); | 
| 62   state.callTree = callTreeState; | 44   state.callTree = callTreeState; | 
| 63   return state; | 45   return state; | 
| 64 } | 46 } | 
| 65 | 47 | 
| 66 let main = { | 48 let main = { | 
| 67   currentState : emptyState(), | 49   currentState : emptyState(), | 
| 68 | 50 | 
| 69   setMode(mode) { | 51   setMode(mode) { | 
| 70     if (mode != main.currentState.mode) { | 52     if (mode != main.currentState.mode) { | 
| 71       let callTreeState = Object.assign({}, main.currentState.callTree); | 53 | 
| 72       callTreeState.mode = mode; | 54       function setCallTreeModifiers(attribution, categories, sort) { | 
|  | 55         let callTreeState = Object.assign({}, main.currentState.callTree); | 
|  | 56         callTreeState.attribution = attribution; | 
|  | 57         callTreeState.categories = categories; | 
|  | 58         callTreeState.sort = sort; | 
|  | 59         return callTreeState; | 
|  | 60       } | 
|  | 61 | 
|  | 62       let state = Object.assign({}, main.currentState); | 
|  | 63 | 
| 73       switch (mode) { | 64       switch (mode) { | 
| 74         case "bottom-up": | 65         case "bottom-up": | 
| 75           callTreeState.attribution = "js-exclude-bc"; | 66           state.callTree = | 
| 76           callTreeState.categories = "code-type"; | 67               setCallTreeModifiers("js-exclude-bc", "code-type", "time"); | 
| 77           callTreeState.sort = "time"; |  | 
| 78           break; | 68           break; | 
| 79         case "top-down": | 69         case "top-down": | 
| 80           callTreeState.attribution = "js-exclude-bc"; | 70           state.callTree = | 
| 81           callTreeState.categories = "none"; | 71               setCallTreeModifiers("js-exclude-bc", "none", "time"); | 
| 82           callTreeState.sort = "time"; |  | 
| 83           break; | 72           break; | 
| 84         case "function-list": | 73         case "function-list": | 
| 85           callTreeState.attribution = "js-exclude-bc"; | 74           state.callTree = | 
| 86           callTreeState.categories = "code-type"; | 75               setCallTreeModifiers("js-exclude-bc", "code-type", "own-time"); | 
| 87           callTreeState.sort = "own-time"; |  | 
| 88           break; | 76           break; | 
| 89         default: |  | 
| 90           console.error("Invalid mode"); |  | 
| 91       } | 77       } | 
| 92       main.currentState = setCallTreeState(main.currentState,  callTreeState); | 78 | 
|  | 79       state.mode = mode; | 
|  | 80 | 
|  | 81       main.currentState = state; | 
| 93       main.delayRender(); | 82       main.delayRender(); | 
| 94     } | 83     } | 
| 95   }, | 84   }, | 
| 96 | 85 | 
| 97   setCallTreeAttribution(attribution) { | 86   setCallTreeAttribution(attribution) { | 
| 98     if (attribution != main.currentState.attribution) { | 87     if (attribution != main.currentState.attribution) { | 
| 99       let callTreeState = Object.assign({}, main.currentState.callTree); | 88       let callTreeState = Object.assign({}, main.currentState.callTree); | 
| 100       callTreeState.attribution = attribution; | 89       callTreeState.attribution = attribution; | 
| 101       main.currentState = setCallTreeState(main.currentState,  callTreeState); | 90       main.currentState = setCallTreeState(main.currentState,  callTreeState); | 
| 102       main.delayRender(); | 91       main.delayRender(); | 
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 194     Promise.resolve().then(() => { | 183     Promise.resolve().then(() => { | 
| 195       for (let c of components) { | 184       for (let c of components) { | 
| 196         c.render(main.currentState); | 185         c.render(main.currentState); | 
| 197       } | 186       } | 
| 198     }); | 187     }); | 
| 199   } | 188   } | 
| 200 }; | 189 }; | 
| 201 | 190 | 
| 202 let bucketDescriptors = | 191 let bucketDescriptors = | 
| 203     [ { kinds : [ "JSOPT" ], | 192     [ { kinds : [ "JSOPT" ], | 
|  | 193         color : "#00ff00", | 
|  | 194         backgroundColor : "#c0ffc0", | 
|  | 195         text : "JS Optimized" }, | 
|  | 196       { kinds : [ "JSUNOPT", "BC" ], | 
| 204         color : "#ffb000", | 197         color : "#ffb000", | 
| 205         backgroundColor : "#ffe0c0", | 198         backgroundColor : "#ffe0c0", | 
| 206         text : "JS Optimized" }, |  | 
| 207       { kinds : [ "JSUNOPT", "BC" ], |  | 
| 208         color : "#00ff00", |  | 
| 209         backgroundColor : "#c0ffc0", |  | 
| 210         text : "JS Unoptimized" }, | 199         text : "JS Unoptimized" }, | 
| 211       { kinds : [ "IC" ], | 200       { kinds : [ "IC" ], | 
| 212         color : "#ffff00", | 201         color : "#ffff00", | 
| 213         backgroundColor : "#ffffc0", | 202         backgroundColor : "#ffffc0", | 
| 214         text : "IC" }, | 203         text : "IC" }, | 
| 215       { kinds : [ "STUB", "BUILTIN", "REGEXP" ], | 204       { kinds : [ "STUB", "BUILTIN", "REGEXP" ], | 
| 216         color : "#ffb0b0", | 205         color : "#ffb0b0", | 
| 217         backgroundColor : "#fff0f0", | 206         backgroundColor : "#fff0f0", | 
| 218         text : "Other generated" }, | 207         text : "Other generated" }, | 
| 219       { kinds : [ "CPP", "LIB" ], | 208       { kinds : [ "CPP", "LIB" ], | 
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 318     case "full-tree": | 307     case "full-tree": | 
| 319       return (type, kind) => true; | 308       return (type, kind) => true; | 
| 320     case "js-funs": | 309     case "js-funs": | 
| 321       return (type, kind) => type !== 'CODE'; | 310       return (type, kind) => type !== 'CODE'; | 
| 322     case "js-exclude-bc": | 311     case "js-exclude-bc": | 
| 323       return (type, kind) => | 312       return (type, kind) => | 
| 324           type !== 'CODE' || !isBytecodeHandler(kind); | 313           type !== 'CODE' || !isBytecodeHandler(kind); | 
| 325   } | 314   } | 
| 326 } | 315 } | 
| 327 | 316 | 
|  | 317 function createTableExpander(indent) { | 
|  | 318   let div = document.createElement("div"); | 
|  | 319   div.style.width = (indent + 0.5) + "em"; | 
|  | 320   div.style.display = "inline-block"; | 
|  | 321   div.style.textAlign = "right"; | 
|  | 322   return div; | 
|  | 323 } | 
|  | 324 | 
|  | 325 function createFunctionNode(name, codeId) { | 
|  | 326   if (codeId == -1) { | 
|  | 327     return document.createTextNode(name); | 
|  | 328   } | 
|  | 329   let nameElement = document.createElement("span"); | 
|  | 330   nameElement.classList.add("codeid-link") | 
|  | 331   nameElement.onclick = function() { | 
|  | 332     main.setCurrentCode(codeId); | 
|  | 333   }; | 
|  | 334   nameElement.appendChild(document.createTextNode(name)); | 
|  | 335   return nameElement; | 
|  | 336 } | 
|  | 337 | 
| 328 class CallTreeView { | 338 class CallTreeView { | 
| 329   constructor() { | 339   constructor() { | 
| 330     this.element = $("calltree"); | 340     this.element = $("calltree"); | 
| 331     this.treeElement = $("calltree-table"); | 341     this.treeElement = $("calltree-table"); | 
| 332     this.selectAttribution = $("calltree-attribution"); | 342     this.selectAttribution = $("calltree-attribution"); | 
| 333     this.selectCategories = $("calltree-categories"); | 343     this.selectCategories = $("calltree-categories"); | 
| 334     this.selectSort = $("calltree-sort"); | 344     this.selectSort = $("calltree-sort"); | 
| 335 | 345 | 
| 336     this.selectAttribution.onchange = () => { | 346     this.selectAttribution.onchange = () => { | 
| 337       main.setCallTreeAttribution(this.selectAttribution.value); | 347       main.setCallTreeAttribution(this.selectAttribution.value); | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 370         }; | 380         }; | 
| 371       case "category-own-time": | 381       case "category-own-time": | 
| 372         return (c1, c2) => { | 382         return (c1, c2) => { | 
| 373           if (c1.type === c2.type) return c2.ownTicks - c1.ownTicks; | 383           if (c1.type === c2.type) return c2.ownTicks - c1.ownTicks; | 
| 374           if (c1.type < c2.type) return 1; | 384           if (c1.type < c2.type) return 1; | 
| 375           return -1; | 385           return -1; | 
| 376         }; | 386         }; | 
| 377     } | 387     } | 
| 378   } | 388   } | 
| 379 | 389 | 
| 380   createExpander(indent) { |  | 
| 381     let div = document.createElement("div"); |  | 
| 382     div.style.width = (1 + indent) + "em"; |  | 
| 383     div.style.display = "inline-block"; |  | 
| 384     div.style.textAlign = "right"; |  | 
| 385     return div; |  | 
| 386   } |  | 
| 387 |  | 
| 388   createFunctionNode(name, codeId) { |  | 
| 389     if (codeId == -1) { |  | 
| 390       return document.createTextNode(name); |  | 
| 391     } |  | 
| 392     let nameElement = document.createElement("span"); |  | 
| 393     nameElement.classList.add("codeid-link") |  | 
| 394     nameElement.onclick = function() { |  | 
| 395       main.setCurrentCode(codeId); |  | 
| 396     }; |  | 
| 397     nameElement.appendChild(document.createTextNode(name)); |  | 
| 398     return nameElement; |  | 
| 399   } |  | 
| 400 |  | 
| 401   expandTree(tree, indent) { | 390   expandTree(tree, indent) { | 
| 402     let that = this; | 391     let that = this; | 
| 403     let index = 0; | 392     let index = 0; | 
| 404     let id = "R/"; | 393     let id = "R/"; | 
| 405     let row = tree.row; | 394     let row = tree.row; | 
| 406     let expander = tree.expander; | 395     let expander = tree.expander; | 
| 407 | 396 | 
| 408     if (row) { | 397     if (row) { | 
| 409       console.assert("expander"); |  | 
| 410       index = row.rowIndex; | 398       index = row.rowIndex; | 
| 411       id = row.id; | 399       id = row.id; | 
| 412 | 400 | 
| 413       // Make sure we collapse the children when the row is clicked | 401       // Make sure we collapse the children when the row is clicked | 
| 414       // again. | 402       // again. | 
| 415       expander.textContent = "\u25BE"; | 403       expander.textContent = "\u25BE"; | 
| 416       let expandHandler = expander.onclick; | 404       let expandHandler = expander.onclick; | 
| 417       expander.onclick = () => { | 405       expander.onclick = () => { | 
| 418         that.collapseRow(tree, expander, expandHandler); | 406         that.collapseRow(tree, expander, expandHandler); | 
| 419       } | 407       } | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 445 | 433 | 
| 446       // Inclusive time % cell. | 434       // Inclusive time % cell. | 
| 447       let c = row.insertCell(); | 435       let c = row.insertCell(); | 
| 448       c.textContent = (node.ticks * 100 / this.tickCount).toFixed(2) + "%"; | 436       c.textContent = (node.ticks * 100 / this.tickCount).toFixed(2) + "%"; | 
| 449       c.style.textAlign = "right"; | 437       c.style.textAlign = "right"; | 
| 450       // Percent-of-parent cell. | 438       // Percent-of-parent cell. | 
| 451       c = row.insertCell(); | 439       c = row.insertCell(); | 
| 452       c.textContent = (node.ticks * 100 / tree.ticks).toFixed(2) + "%"; | 440       c.textContent = (node.ticks * 100 / tree.ticks).toFixed(2) + "%"; | 
| 453       c.style.textAlign = "right"; | 441       c.style.textAlign = "right"; | 
| 454       // Exclusive time % cell. | 442       // Exclusive time % cell. | 
| 455       if (this.currentState.callTree.mode !== "bottom-up") { | 443       if (this.currentState.mode !== "bottom-up") { | 
| 456         c = row.insertCell(-1); | 444         c = row.insertCell(-1); | 
| 457         c.textContent = (node.ownTicks * 100 / this.tickCount).toFixed(2) + "%"; | 445         c.textContent = (node.ownTicks * 100 / this.tickCount).toFixed(2) + "%"; | 
| 458         c.style.textAlign = "right"; | 446         c.style.textAlign = "right"; | 
| 459       } | 447       } | 
| 460 | 448 | 
| 461       // Create the name cell. | 449       // Create the name cell. | 
| 462       let nameCell = row.insertCell(); | 450       let nameCell = row.insertCell(); | 
| 463       let expander = this.createExpander(indent); | 451       let expander = createTableExpander(indent + 1); | 
| 464       nameCell.appendChild(expander); | 452       nameCell.appendChild(expander); | 
| 465       nameCell.appendChild(createTypeDiv(node.type)); | 453       nameCell.appendChild(createTypeDiv(node.type)); | 
| 466       nameCell.appendChild(this.createFunctionNode(node.name, node.codeId)); | 454       nameCell.appendChild(createFunctionNode(node.name, node.codeId)); | 
| 467 | 455 | 
| 468       // Inclusive ticks cell. | 456       // Inclusive ticks cell. | 
| 469       c = row.insertCell(); | 457       c = row.insertCell(); | 
| 470       c.textContent = node.ticks; | 458       c.textContent = node.ticks; | 
| 471       c.style.textAlign = "right"; | 459       c.style.textAlign = "right"; | 
| 472       if (this.currentState.callTree.mode !== "bottom-up") { | 460       if (this.currentState.mode !== "bottom-up") { | 
| 473         // Exclusive ticks cell. | 461         // Exclusive ticks cell. | 
| 474         c = row.insertCell(-1); | 462         c = row.insertCell(-1); | 
| 475         c.textContent = node.ownTicks; | 463         c.textContent = node.ownTicks; | 
| 476         c.style.textAlign = "right"; | 464         c.style.textAlign = "right"; | 
| 477       } | 465       } | 
| 478       if (node.children.length > 0) { | 466       if (node.children.length > 0) { | 
| 479         expander.textContent = "\u25B8"; | 467         expander.textContent = "\u25B8"; | 
| 480         expander.onclick = () => { that.expandTree(node, indent + 1); }; | 468         expander.onclick = () => { that.expandTree(node, indent + 1); }; | 
| 481       } | 469       } | 
| 482 | 470 | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 493     let index = row.rowIndex; | 481     let index = row.rowIndex; | 
| 494     while (row.rowIndex < this.rows.rows.length && | 482     while (row.rowIndex < this.rows.rows.length && | 
| 495         this.rows.rows[index].id.startsWith(id)) { | 483         this.rows.rows[index].id.startsWith(id)) { | 
| 496       this.rows.deleteRow(index); | 484       this.rows.deleteRow(index); | 
| 497     } | 485     } | 
| 498 | 486 | 
| 499     expander.textContent = "\u25B8"; | 487     expander.textContent = "\u25B8"; | 
| 500     expander.onclick = expandHandler; | 488     expander.onclick = expandHandler; | 
| 501   } | 489   } | 
| 502 | 490 | 
| 503   fillSelects(calltree) { | 491   fillSelects(mode, calltree) { | 
| 504     function addOptions(e, values, current) { | 492     function addOptions(e, values, current) { | 
| 505       while (e.options.length > 0) { | 493       while (e.options.length > 0) { | 
| 506         e.remove(0); | 494         e.remove(0); | 
| 507       } | 495       } | 
| 508       for (let i = 0; i < values.length; i++) { | 496       for (let i = 0; i < values.length; i++) { | 
| 509         let option = document.createElement("option"); | 497         let option = document.createElement("option"); | 
| 510         option.value = values[i].value; | 498         option.value = values[i].value; | 
| 511         option.textContent = values[i].text; | 499         option.textContent = values[i].text; | 
| 512         e.appendChild(option); | 500         e.appendChild(option); | 
| 513       } | 501       } | 
| 514       e.value = current; | 502       e.value = current; | 
| 515     } | 503     } | 
| 516 | 504 | 
| 517     let attributions = [ | 505     let attributions = [ | 
| 518         { value : "js-exclude-bc", | 506         { value : "js-exclude-bc", | 
| 519           text : "Attribute bytecode handlers to caller" }, | 507           text : "Attribute bytecode handlers to caller" }, | 
| 520         { value : "full-tree", | 508         { value : "full-tree", | 
| 521           text : "Count each code object separately" }, | 509           text : "Count each code object separately" }, | 
| 522         { value : "js-funs", | 510         { value : "js-funs", | 
| 523           text : "Attribute non-functions to JS functions"  } | 511           text : "Attribute non-functions to JS functions"  } | 
| 524     ]; | 512     ]; | 
| 525 | 513 | 
| 526     switch (calltree.mode) { | 514     switch (mode) { | 
| 527       case "bottom-up": | 515       case "bottom-up": | 
| 528         addOptions(this.selectAttribution, attributions, calltree.attribution); | 516         addOptions(this.selectAttribution, attributions, calltree.attribution); | 
| 529         addOptions(this.selectCategories, [ | 517         addOptions(this.selectCategories, [ | 
| 530             { value : "code-type", text : "Code type" }, | 518             { value : "code-type", text : "Code type" }, | 
| 531             { value : "none", text : "None" } | 519             { value : "none", text : "None" } | 
| 532         ], calltree.categories); | 520         ], calltree.categories); | 
| 533         addOptions(this.selectSort, [ | 521         addOptions(this.selectSort, [ | 
| 534             { value : "time", text : "Time (including children)" }, | 522             { value : "time", text : "Time (including children)" }, | 
| 535             { value : "category-time", text : "Code category, time" }, | 523             { value : "category-time", text : "Code category, time" }, | 
| 536         ], calltree.sort); | 524         ], calltree.sort); | 
| (...skipping 20 matching lines...) Expand all  Loading... | 
| 557             { value : "own-time", text : "Own time" }, | 545             { value : "own-time", text : "Own time" }, | 
| 558             { value : "time", text : "Time (including children)" }, | 546             { value : "time", text : "Time (including children)" }, | 
| 559             { value : "category-own-time", text : "Code category, own time"}, | 547             { value : "category-own-time", text : "Code category, own time"}, | 
| 560             { value : "category-time", text : "Code category, time" }, | 548             { value : "category-time", text : "Code category, time" }, | 
| 561         ], calltree.sort); | 549         ], calltree.sort); | 
| 562         return; | 550         return; | 
| 563     } | 551     } | 
| 564     console.error("Unexpected mode"); | 552     console.error("Unexpected mode"); | 
| 565   } | 553   } | 
| 566 | 554 | 
|  | 555   static isCallTreeMode(mode) { | 
|  | 556     switch (mode) { | 
|  | 557       case "bottom-up": | 
|  | 558       case "top-down": | 
|  | 559       case "function-list": | 
|  | 560         return true; | 
|  | 561       default: | 
|  | 562         return false; | 
|  | 563     } | 
|  | 564   } | 
|  | 565 | 
| 567   render(newState) { | 566   render(newState) { | 
| 568     let oldState = this.currentState; | 567     let oldState = this.currentState; | 
| 569     if (!newState.file) { | 568     if (!newState.file || !CallTreeView.isCallTreeMode(newState.mode)) { | 
| 570       this.element.style.display = "none"; | 569       this.element.style.display = "none"; | 
|  | 570       this.currentState = null; | 
| 571       return; | 571       return; | 
| 572     } | 572     } | 
| 573 | 573 | 
| 574     this.currentState = newState; | 574     this.currentState = newState; | 
| 575     if (oldState) { | 575     if (oldState) { | 
| 576       if (newState.file === oldState.file && | 576       if (newState.file === oldState.file && | 
| 577           newState.start === oldState.start && | 577           newState.start === oldState.start && | 
| 578           newState.end === oldState.end && | 578           newState.end === oldState.end && | 
| 579           newState.callTree.mode === oldState.callTree.mode && | 579           newState.mode === oldState.mode && | 
| 580           newState.callTree.attribution === oldState.callTree.attribution && | 580           newState.callTree.attribution === oldState.callTree.attribution && | 
| 581           newState.callTree.categories === oldState.callTree.categories && | 581           newState.callTree.categories === oldState.callTree.categories && | 
| 582           newState.callTree.sort === oldState.callTree.sort) { | 582           newState.callTree.sort === oldState.callTree.sort) { | 
| 583         // No change => just return. | 583         // No change => just return. | 
| 584         return; | 584         return; | 
| 585       } | 585       } | 
| 586     } | 586     } | 
| 587 | 587 | 
| 588     this.element.style.display = "inherit"; | 588     this.element.style.display = "inherit"; | 
| 589 | 589 | 
| 590     let mode = this.currentState.callTree.mode; | 590     let mode = this.currentState.mode; | 
| 591     if (!oldState || mode !== oldState.callTree.mode) { | 591     if (!oldState || mode !== oldState.mode) { | 
| 592       // Technically, we should also call this if attribution, categories or | 592       // Technically, we should also call this if attribution, categories or | 
| 593       // sort change, but the selection is already highlighted by the combobox | 593       // sort change, but the selection is already highlighted by the combobox | 
| 594       // itself, so we do need to do anything here. | 594       // itself, so we do need to do anything here. | 
| 595       this.fillSelects(newState.callTree); | 595       this.fillSelects(newState.mode, newState.callTree); | 
| 596     } | 596     } | 
| 597 | 597 | 
| 598     let ownTimeClass = (mode === "bottom-up") ? "numeric-hidden" : "numeric"; | 598     let ownTimeClass = (mode === "bottom-up") ? "numeric-hidden" : "numeric"; | 
| 599     let ownTimeTh = $(this.treeElement.id + "-own-time-header"); | 599     let ownTimeTh = $(this.treeElement.id + "-own-time-header"); | 
| 600     ownTimeTh.classList = ownTimeClass; | 600     ownTimeTh.classList = ownTimeClass; | 
| 601     let ownTicksTh = $(this.treeElement.id + "-own-ticks-header"); | 601     let ownTicksTh = $(this.treeElement.id + "-own-ticks-header"); | 
| 602     ownTicksTh.classList = ownTimeClass; | 602     ownTicksTh.classList = ownTimeClass; | 
| 603 | 603 | 
| 604     // Build the tree. | 604     // Build the tree. | 
| 605     let stackProcessor; | 605     let stackProcessor; | 
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 654     this.canvas.onmousedown = this.onMouseDown.bind(this); | 654     this.canvas.onmousedown = this.onMouseDown.bind(this); | 
| 655     this.canvas.onmouseup = this.onMouseUp.bind(this); | 655     this.canvas.onmouseup = this.onMouseUp.bind(this); | 
| 656     this.canvas.onmousemove = this.onMouseMove.bind(this); | 656     this.canvas.onmousemove = this.onMouseMove.bind(this); | 
| 657 | 657 | 
| 658     this.selectionStart = null; | 658     this.selectionStart = null; | 
| 659     this.selectionEnd = null; | 659     this.selectionEnd = null; | 
| 660     this.selecting = false; | 660     this.selecting = false; | 
| 661 | 661 | 
| 662     this.fontSize = 12; | 662     this.fontSize = 12; | 
| 663     this.imageOffset = Math.round(this.fontSize * 1.2); | 663     this.imageOffset = Math.round(this.fontSize * 1.2); | 
| 664     this.functionTimelineHeight = 16; | 664     this.functionTimelineHeight = 24; | 
|  | 665     this.functionTimelineTickHeight = 16; | 
| 665 | 666 | 
| 666     this.currentState = null; | 667     this.currentState = null; | 
| 667   } | 668   } | 
| 668 | 669 | 
| 669   onMouseDown(e) { | 670   onMouseDown(e) { | 
| 670     this.selectionStart = | 671     this.selectionStart = | 
| 671         e.clientX - this.canvas.getBoundingClientRect().left; | 672         e.clientX - this.canvas.getBoundingClientRect().left; | 
| 672     this.selectionEnd = this.selectionStart + 1; | 673     this.selectionEnd = this.selectionStart + 1; | 
| 673     this.selecting = true; | 674     this.selecting = true; | 
| 674   } | 675   } | 
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 857       for (let j = 0; j < bucketDescriptors.length; j++) { | 858       for (let j = 0; j < bucketDescriptors.length; j++) { | 
| 858         let desc = bucketDescriptors[j]; | 859         let desc = bucketDescriptors[j]; | 
| 859         for (let k = 0; k < desc.kinds.length; k++) { | 860         for (let k = 0; k < desc.kinds.length; k++) { | 
| 860           sum += buckets[i][desc.kinds[k]]; | 861           sum += buckets[i][desc.kinds[k]]; | 
| 861         } | 862         } | 
| 862         bucketData.push(Math.round(graphHeight * sum / total)); | 863         bucketData.push(Math.round(graphHeight * sum / total)); | 
| 863       } | 864       } | 
| 864       bucketsGraph.push(bucketData); | 865       bucketsGraph.push(bucketData); | 
| 865     } | 866     } | 
| 866 | 867 | 
| 867     // Draw the graph into the buffer. | 868     // Draw the category graph into the buffer. | 
| 868     let bucketWidth = width / (bucketsGraph.length - 1); | 869     let bucketWidth = width / bucketsGraph.length; | 
| 869     let ctx = buffer.getContext('2d'); | 870     let ctx = buffer.getContext('2d'); | 
| 870     for (let i = 0; i < bucketsGraph.length - 1; i++) { | 871     for (let i = 0; i < bucketsGraph.length - 1; i++) { | 
| 871       let bucketData = bucketsGraph[i]; | 872       let bucketData = bucketsGraph[i]; | 
| 872       let nextBucketData = bucketsGraph[i + 1]; | 873       let nextBucketData = bucketsGraph[i + 1]; | 
| 873       for (let j = 0; j < bucketData.length; j++) { | 874       for (let j = 0; j < bucketData.length; j++) { | 
| 874         let x1 = Math.round(i * bucketWidth); | 875         let x1 = Math.round(i * bucketWidth); | 
| 875         let x2 = Math.round((i + 1) * bucketWidth); | 876         let x2 = Math.round((i + 1) * bucketWidth); | 
| 876         ctx.beginPath(); | 877         ctx.beginPath(); | 
| 877         ctx.moveTo(x1, j && bucketData[j - 1]); | 878         ctx.moveTo(x1, j && bucketData[j - 1]); | 
| 878         ctx.lineTo(x2, j && nextBucketData[j - 1]); | 879         ctx.lineTo(x2, j && nextBucketData[j - 1]); | 
| 879         ctx.lineTo(x2, nextBucketData[j]); | 880         ctx.lineTo(x2, nextBucketData[j]); | 
| 880         ctx.lineTo(x1, bucketData[j]); | 881         ctx.lineTo(x1, bucketData[j]); | 
| 881         ctx.closePath(); | 882         ctx.closePath(); | 
| 882         ctx.fillStyle = bucketDescriptors[j].color; | 883         ctx.fillStyle = bucketDescriptors[j].color; | 
| 883         ctx.fill(); | 884         ctx.fill(); | 
| 884       } | 885       } | 
| 885     } | 886     } | 
|  | 887 | 
|  | 888     // Draw the function ticks. | 
| 886     let functionTimelineYOffset = graphHeight; | 889     let functionTimelineYOffset = graphHeight; | 
| 887     let functionTimelineHeight = this.functionTimelineHeight; | 890     let functionTimelineTickHeight = this.functionTimelineTickHeight; | 
| 888     let functionTimelineHalfHeight = Math.round(functionTimelineHeight / 2); | 891     let functionTimelineHalfHeight = | 
|  | 892         Math.round(functionTimelineTickHeight / 2); | 
| 889     let timestampScaler = width / (lastTime - firstTime); | 893     let timestampScaler = width / (lastTime - firstTime); | 
|  | 894     let timestampToX = (t) => Math.round((t - firstTime) * timestampScaler); | 
| 890     ctx.fillStyle = "white"; | 895     ctx.fillStyle = "white"; | 
| 891     ctx.fillRect( | 896     ctx.fillRect( | 
| 892       0, | 897       0, | 
| 893       functionTimelineYOffset, | 898       functionTimelineYOffset, | 
| 894       buffer.width, | 899       buffer.width, | 
| 895       functionTimelineHeight); | 900       this.functionTimelineHeight); | 
| 896     for (let i = 0; i < codeIdProcessor.blocks.length; i++) { | 901     for (let i = 0; i < codeIdProcessor.blocks.length; i++) { | 
| 897       let block = codeIdProcessor.blocks[i]; | 902       let block = codeIdProcessor.blocks[i]; | 
| 898       let bucket = kindToBucketDescriptor[block.kind]; | 903       let bucket = kindToBucketDescriptor[block.kind]; | 
| 899       ctx.fillStyle = bucket.color; | 904       ctx.fillStyle = bucket.color; | 
| 900       ctx.fillRect( | 905       ctx.fillRect( | 
| 901         Math.round((block.start - firstTime) * timestampScaler), | 906         timestampToX(block.start), | 
| 902         functionTimelineYOffset, | 907         functionTimelineYOffset, | 
| 903         Math.max(1, Math.round((block.end - block.start) * timestampScaler)), | 908         Math.max(1, Math.round((block.end - block.start) * timestampScaler)), | 
| 904         block.topOfStack ? functionTimelineHeight : functionTimelineHalfHeight); | 909         block.topOfStack ? | 
|  | 910             functionTimelineTickHeight : functionTimelineHalfHeight); | 
| 905     } | 911     } | 
| 906     ctx.strokeStyle = "black"; | 912     ctx.strokeStyle = "black"; | 
| 907     ctx.lineWidth = "1"; | 913     ctx.lineWidth = "1"; | 
| 908     ctx.beginPath(); | 914     ctx.beginPath(); | 
| 909     ctx.moveTo(0, functionTimelineYOffset + 0.5); | 915     ctx.moveTo(0, functionTimelineYOffset + 0.5); | 
| 910     ctx.lineTo(buffer.width, functionTimelineYOffset + 0.5); | 916     ctx.lineTo(buffer.width, functionTimelineYOffset + 0.5); | 
| 911     ctx.stroke(); | 917     ctx.stroke(); | 
| 912     ctx.strokeStyle = "rgba(0,0,0,0.2)"; | 918     ctx.strokeStyle = "rgba(0,0,0,0.2)"; | 
| 913     ctx.lineWidth = "1"; | 919     ctx.lineWidth = "1"; | 
| 914     ctx.beginPath(); | 920     ctx.beginPath(); | 
| 915     ctx.moveTo(0, functionTimelineYOffset + functionTimelineHalfHeight - 0.5); | 921     ctx.moveTo(0, functionTimelineYOffset + functionTimelineHalfHeight - 0.5); | 
| 916     ctx.lineTo(buffer.width, | 922     ctx.lineTo(buffer.width, | 
| 917         functionTimelineYOffset + functionTimelineHalfHeight - 0.5); | 923         functionTimelineYOffset + functionTimelineHalfHeight - 0.5); | 
| 918     ctx.stroke(); | 924     ctx.stroke(); | 
| 919 | 925 | 
|  | 926     // Draw marks for optimizations and deoptimizations in the function | 
|  | 927     // timeline. | 
|  | 928     if (currentCodeId && currentCodeId >= 0 && | 
|  | 929         file.code[currentCodeId].func) { | 
|  | 930       let y = Math.round(functionTimelineYOffset + functionTimelineTickHeight + | 
|  | 931           (this.functionTimelineHeight - functionTimelineTickHeight) / 2); | 
|  | 932       let func = file.functions[file.code[currentCodeId].func]; | 
|  | 933       for (let i = 0; i < func.codes.length; i++) { | 
|  | 934         let code = file.code[func.codes[i]]; | 
|  | 935         if (code.kind === "Opt") { | 
|  | 936           if (code.deopt) { | 
|  | 937             // Draw deoptimization mark. | 
|  | 938             let x = timestampToX(code.deopt.tm); | 
|  | 939             ctx.lineWidth = 0.7; | 
|  | 940             ctx.strokeStyle = "red"; | 
|  | 941             ctx.beginPath(); | 
|  | 942             ctx.moveTo(x - 3, y - 3); | 
|  | 943             ctx.lineTo(x + 3, y + 3); | 
|  | 944             ctx.stroke(); | 
|  | 945             ctx.beginPath(); | 
|  | 946             ctx.moveTo(x - 3, y + 3); | 
|  | 947             ctx.lineTo(x + 3, y - 3); | 
|  | 948             ctx.stroke(); | 
|  | 949           } | 
|  | 950           // Draw optimization mark. | 
|  | 951           let x = timestampToX(code.tm); | 
|  | 952           ctx.lineWidth = 0.7; | 
|  | 953           ctx.strokeStyle = "blue"; | 
|  | 954           ctx.beginPath(); | 
|  | 955           ctx.moveTo(x - 3, y - 3); | 
|  | 956           ctx.lineTo(x, y); | 
|  | 957           ctx.stroke(); | 
|  | 958           ctx.beginPath(); | 
|  | 959           ctx.moveTo(x - 3, y + 3); | 
|  | 960           ctx.lineTo(x, y); | 
|  | 961           ctx.stroke(); | 
|  | 962         } else { | 
|  | 963           // Draw code creation mark. | 
|  | 964           let x = Math.round(timestampToX(code.tm)); | 
|  | 965           ctx.beginPath(); | 
|  | 966           ctx.fillStyle = "black"; | 
|  | 967           ctx.arc(x, y, 3, 0, 2 * Math.PI); | 
|  | 968           ctx.fill(); | 
|  | 969         } | 
|  | 970       } | 
|  | 971     } | 
|  | 972 | 
| 920     // Remember stuff for later. | 973     // Remember stuff for later. | 
| 921     this.buffer = buffer; | 974     this.buffer = buffer; | 
| 922 | 975 | 
| 923     // Draw the buffer. | 976     // Draw the buffer. | 
| 924     this.drawSelection(); | 977     this.drawSelection(); | 
| 925 | 978 | 
| 926     // (Re-)Populate the graph legend. | 979     // (Re-)Populate the graph legend. | 
| 927     while (this.legend.cells.length > 0) { | 980     while (this.legend.cells.length > 0) { | 
| 928       this.legend.deleteCell(0); | 981       this.legend.deleteCell(0); | 
| 929     } | 982     } | 
| (...skipping 21 matching lines...) Expand all  Loading... | 
| 951     } | 1004     } | 
| 952     if (currentCodeId) { | 1005     if (currentCodeId) { | 
| 953       let currentCode = file.code[currentCodeId]; | 1006       let currentCode = file.code[currentCodeId]; | 
| 954       this.currentCode.appendChild(document.createTextNode(currentCode.name)); | 1007       this.currentCode.appendChild(document.createTextNode(currentCode.name)); | 
| 955     } else { | 1008     } else { | 
| 956       this.currentCode.appendChild(document.createTextNode("<none>")); | 1009       this.currentCode.appendChild(document.createTextNode("<none>")); | 
| 957     } | 1010     } | 
| 958   } | 1011   } | 
| 959 } | 1012 } | 
| 960 | 1013 | 
|  | 1014 class ModeBarView { | 
|  | 1015   constructor() { | 
|  | 1016     let modeBar = this.element = $("mode-bar"); | 
|  | 1017 | 
|  | 1018     function addMode(id, text, active) { | 
|  | 1019       let div = document.createElement("div"); | 
|  | 1020       div.classList = "mode-button" + (active ? " active-mode-button" : ""); | 
|  | 1021       div.id = "mode-" + id; | 
|  | 1022       div.textContent = text; | 
|  | 1023       div.onclick = () => { | 
|  | 1024         if (main.currentState.mode === id) return; | 
|  | 1025         let old = $("mode-" + main.currentState.mode); | 
|  | 1026         old.classList = "mode-button"; | 
|  | 1027         div.classList = "mode-button active-mode-button"; | 
|  | 1028         main.setMode(id); | 
|  | 1029       }; | 
|  | 1030       modeBar.appendChild(div); | 
|  | 1031     } | 
|  | 1032 | 
|  | 1033     addMode("summary", "Summary", true); | 
|  | 1034     addMode("bottom-up", "Bottom up"); | 
|  | 1035     addMode("top-down", "Top down"); | 
|  | 1036     addMode("function-list", "Functions"); | 
|  | 1037   } | 
|  | 1038 | 
|  | 1039   render(newState) { | 
|  | 1040     if (!newState.file) { | 
|  | 1041       this.element.style.display = "none"; | 
|  | 1042       return; | 
|  | 1043     } | 
|  | 1044 | 
|  | 1045     this.element.style.display = "inherit"; | 
|  | 1046   } | 
|  | 1047 } | 
|  | 1048 | 
|  | 1049 class SummaryView { | 
|  | 1050   constructor() { | 
|  | 1051     this.element = $("summary"); | 
|  | 1052     this.currentState = null; | 
|  | 1053   } | 
|  | 1054 | 
|  | 1055   render(newState) { | 
|  | 1056     let oldState = this.currentState; | 
|  | 1057 | 
|  | 1058     if (!newState.file || newState.mode !== "summary") { | 
|  | 1059       this.element.style.display = "none"; | 
|  | 1060       this.currentState = null; | 
|  | 1061       return; | 
|  | 1062     } | 
|  | 1063 | 
|  | 1064     this.currentState = newState; | 
|  | 1065     if (oldState) { | 
|  | 1066       if (newState.file === oldState.file && | 
|  | 1067           newState.start === oldState.start && | 
|  | 1068           newState.end === oldState.end) { | 
|  | 1069         // No change, nothing to do. | 
|  | 1070         return; | 
|  | 1071       } | 
|  | 1072     } | 
|  | 1073 | 
|  | 1074     this.element.style.display = "inherit"; | 
|  | 1075 | 
|  | 1076     while (this.element.firstChild) { | 
|  | 1077       this.element.removeChild(this.element.firstChild); | 
|  | 1078     } | 
|  | 1079 | 
|  | 1080     let stats = computeOptimizationStats( | 
|  | 1081         this.currentState.file, newState.start, newState.end); | 
|  | 1082 | 
|  | 1083     let table = document.createElement("table"); | 
|  | 1084     let rows = document.createElement("tbody"); | 
|  | 1085 | 
|  | 1086     function addRow(text, number, indent) { | 
|  | 1087       let row = rows.insertRow(-1); | 
|  | 1088       let textCell = row.insertCell(-1); | 
|  | 1089       textCell.textContent = text; | 
|  | 1090       let numberCell = row.insertCell(-1); | 
|  | 1091       numberCell.textContent = number; | 
|  | 1092       if (indent) { | 
|  | 1093         textCell.style.textIndent = indent + "em"; | 
|  | 1094         numberCell.style.textIndent = indent + "em"; | 
|  | 1095       } | 
|  | 1096       return row; | 
|  | 1097     } | 
|  | 1098 | 
|  | 1099     function makeCollapsible(row, expander) { | 
|  | 1100       expander.textContent = "\u25BE"; | 
|  | 1101       let expandHandler = expander.onclick; | 
|  | 1102       expander.onclick = () => { | 
|  | 1103         let id = row.id; | 
|  | 1104         let index = row.rowIndex + 1; | 
|  | 1105         while (index < rows.rows.length && | 
|  | 1106           rows.rows[index].id.startsWith(id)) { | 
|  | 1107           rows.deleteRow(index); | 
|  | 1108         } | 
|  | 1109         expander.textContent = "\u25B8"; | 
|  | 1110         expander.onclick = expandHandler; | 
|  | 1111       } | 
|  | 1112     } | 
|  | 1113 | 
|  | 1114     function expandDeoptInstances(row, expander, instances, indent, kind) { | 
|  | 1115       let index = row.rowIndex; | 
|  | 1116       for (let i = 0; i < instances.length; i++) { | 
|  | 1117         let childRow = rows.insertRow(index + 1); | 
|  | 1118         childRow.id = row.id + i + "/"; | 
|  | 1119 | 
|  | 1120         let deopt = instances[i].deopt; | 
|  | 1121 | 
|  | 1122         let textCell = childRow.insertCell(-1); | 
|  | 1123         textCell.appendChild(document.createTextNode(deopt.posText)); | 
|  | 1124         textCell.style.textIndent = indent + "em"; | 
|  | 1125         let reasonCell = childRow.insertCell(-1); | 
|  | 1126         reasonCell.appendChild( | 
|  | 1127             document.createTextNode("Reason: " + deopt.reason)); | 
|  | 1128         reasonCell.style.textIndent = indent + "em"; | 
|  | 1129       } | 
|  | 1130       makeCollapsible(row, expander); | 
|  | 1131     } | 
|  | 1132 | 
|  | 1133     function expandDeoptFunctionList(row, expander, list, indent, kind) { | 
|  | 1134       let index = row.rowIndex; | 
|  | 1135       for (let i = 0; i < list.length; i++) { | 
|  | 1136         let childRow = rows.insertRow(index + 1); | 
|  | 1137         childRow.id = row.id + i + "/"; | 
|  | 1138 | 
|  | 1139         let textCell = childRow.insertCell(-1); | 
|  | 1140         let expander = createTableExpander(indent); | 
|  | 1141         textCell.appendChild(expander); | 
|  | 1142         textCell.appendChild( | 
|  | 1143             createFunctionNode(list[i].f.name, list[i].f.codes[0])); | 
|  | 1144 | 
|  | 1145         let numberCell = childRow.insertCell(-1); | 
|  | 1146         numberCell.textContent = list[i].instances.length; | 
|  | 1147         numberCell.style.textIndent = indent + "em"; | 
|  | 1148 | 
|  | 1149         expander.textContent = "\u25B8"; | 
|  | 1150         expander.onclick = () => { | 
|  | 1151           expandDeoptInstances( | 
|  | 1152               childRow, expander, list[i].instances, indent + 1); | 
|  | 1153         }; | 
|  | 1154       } | 
|  | 1155       makeCollapsible(row, expander); | 
|  | 1156     } | 
|  | 1157 | 
|  | 1158     function expandOptimizedFunctionList(row, expander, list, indent, kind) { | 
|  | 1159       let index = row.rowIndex; | 
|  | 1160       for (let i = 0; i < list.length; i++) { | 
|  | 1161         let childRow = rows.insertRow(index + 1); | 
|  | 1162         childRow.id = row.id + i + "/"; | 
|  | 1163 | 
|  | 1164         let textCell = childRow.insertCell(-1); | 
|  | 1165         textCell.appendChild( | 
|  | 1166             createFunctionNode(list[i].f.name, list[i].f.codes[0])); | 
|  | 1167         textCell.style.textIndent = indent + "em"; | 
|  | 1168 | 
|  | 1169         let numberCell = childRow.insertCell(-1); | 
|  | 1170         numberCell.textContent = list[i].instances.length; | 
|  | 1171         numberCell.style.textIndent = indent + "em"; | 
|  | 1172       } | 
|  | 1173       makeCollapsible(row, expander); | 
|  | 1174     } | 
|  | 1175 | 
|  | 1176     function addExpandableRow(text, list, indent, kind) { | 
|  | 1177       let row = rows.insertRow(-1); | 
|  | 1178 | 
|  | 1179       row.id = "opt-table/" + kind + "/"; | 
|  | 1180 | 
|  | 1181       let textCell = row.insertCell(-1); | 
|  | 1182       let expander = createTableExpander(indent); | 
|  | 1183       textCell.appendChild(expander); | 
|  | 1184       textCell.appendChild(document.createTextNode(text)); | 
|  | 1185 | 
|  | 1186       let numberCell = row.insertCell(-1); | 
|  | 1187       numberCell.textContent = list.count; | 
|  | 1188       if (indent) { | 
|  | 1189         numberCell.style.textIndent = indent + "em"; | 
|  | 1190       } | 
|  | 1191 | 
|  | 1192       if (list.count > 0) { | 
|  | 1193         expander.textContent = "\u25B8"; | 
|  | 1194         if (kind === "opt") { | 
|  | 1195           expander.onclick = () => { | 
|  | 1196             expandOptimizedFunctionList( | 
|  | 1197                 row, expander, list.functions, indent + 1, kind); | 
|  | 1198           }; | 
|  | 1199         } else { | 
|  | 1200           expander.onclick = () => { | 
|  | 1201             expandDeoptFunctionList( | 
|  | 1202                 row, expander, list.functions, indent + 1, kind); | 
|  | 1203           }; | 
|  | 1204         } | 
|  | 1205       } | 
|  | 1206       return row; | 
|  | 1207     } | 
|  | 1208 | 
|  | 1209     addRow("Total function count:", stats.functionCount); | 
|  | 1210     addRow("Optimized function count:", stats.optimizedFunctionCount, 1); | 
|  | 1211     addRow("Deoptimized function count:", stats.deoptimizedFunctionCount, 2); | 
|  | 1212 | 
|  | 1213     addExpandableRow("Optimization count:", stats.optimizations, 0, "opt"); | 
|  | 1214     let deoptCount = stats.eagerDeoptimizations.count + | 
|  | 1215         stats.softDeoptimizations.count + stats.lazyDeoptimizations.count; | 
|  | 1216     addRow("Deoptimization count:", deoptCount); | 
|  | 1217     addExpandableRow("Eager:", stats.eagerDeoptimizations, 1, "eager"); | 
|  | 1218     addExpandableRow("Lazy:", stats.lazyDeoptimizations, 1, "lazy"); | 
|  | 1219     addExpandableRow("Soft:", stats.softDeoptimizations, 1, "soft"); | 
|  | 1220 | 
|  | 1221     table.appendChild(rows); | 
|  | 1222     this.element.appendChild(table); | 
|  | 1223   } | 
|  | 1224 } | 
|  | 1225 | 
| 961 class HelpView { | 1226 class HelpView { | 
| 962   constructor() { | 1227   constructor() { | 
| 963     this.element = $("help"); | 1228     this.element = $("help"); | 
| 964   } | 1229   } | 
| 965 | 1230 | 
| 966   render(newState) { | 1231   render(newState) { | 
| 967     this.element.style.display = newState.file ? "none" : "inherit"; | 1232     this.element.style.display = newState.file ? "none" : "inherit"; | 
| 968   } | 1233   } | 
| 969 } | 1234 } | 
| OLD | NEW | 
|---|