1// Copyright 2015 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"use strict";
6
7class DisassemblyView extends TextView {
8  constructor(id, broker) {
9    super(id, broker, null, false);
10
11    let view = this;
12    let ADDRESS_STYLE = {
13      css: 'tag',
14      location: function(text) {
15        ADDRESS_STYLE.last_address = text;
16        return undefined;
17      }
18    };
19    let ADDRESS_LINK_STYLE = {
20      css: 'tag',
21      link: function(text) {
22        view.select(function(location) { return location.address == text; }, true, true);
23      }
24    };
25    let UNCLASSIFIED_STYLE = {
26      css: 'com'
27    };
28    let NUMBER_STYLE = {
29      css: 'lit'
30    };
31    let COMMENT_STYLE = {
32      css: 'com'
33    };
34    let POSITION_STYLE = {
35      css: 'com',
36      location: function(text) {
37        view.pos_start = Number(text);
38      }
39    };
40    let OPCODE_STYLE = {
41      css: 'kwd',
42      location: function(text) {
43        if (BLOCK_HEADER_STYLE.block_id != undefined) {
44          return {
45            address: ADDRESS_STYLE.last_address,
46            block_id: BLOCK_HEADER_STYLE.block_id
47          };
48        } else {
49          return {
50            address: ADDRESS_STYLE.last_address
51          };
52        }
53      }
54    };
55    const BLOCK_HEADER_STYLE = {
56      css: 'com',
57      block_id: -1,
58      location: function(text) {
59        let matches = /\d+/.exec(text);
60        if (!matches) return undefined;
61        BLOCK_HEADER_STYLE.block_id = Number(matches[0]);
62        return {
63          block_id: BLOCK_HEADER_STYLE.block_id
64        };
65      },
66    };
67    const SOURCE_POSITION_HEADER_STYLE = {
68      css: 'com',
69      location: function(text) {
70        let matches = /(\d+):(\d+)/.exec(text);
71        if (!matches) return undefined;
72        let li = Number(matches[1]);
73        if (view.pos_lines === null) return undefined;
74        let pos = view.pos_lines[li-1] + Number(matches[2]);
75        return {
76          pos_start: pos,
77          pos_end: pos + 1
78        };
79      },
80    };
81    view.SOURCE_POSITION_HEADER_REGEX = /^(\s*-- .+:)(\d+:\d+)( --)/;
82    let patterns = [
83      [
84        [/^0x[0-9a-f]{8,16}/, ADDRESS_STYLE, 1],
85        [view.SOURCE_POSITION_HEADER_REGEX, SOURCE_POSITION_HEADER_STYLE, -1],
86        [/^\s+-- B\d+ start.*/, BLOCK_HEADER_STYLE, -1],
87        [/^.*/, UNCLASSIFIED_STYLE, -1]
88      ],
89      [
90        [/^\s+\d+\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2],
91        [/^.*/, null, -1]
92      ],
93      [
94        [/^\S+\s+/, OPCODE_STYLE, 3],
95        [/^\S+$/, OPCODE_STYLE, -1],
96        [/^.*/, null, -1]
97      ],
98      [
99        [/^\s+/, null],
100        [/^[^\(;]+$/, null, -1],
101        [/^[^\(;]+/, null],
102        [/^\(/, null, 4],
103        [/^;/, COMMENT_STYLE, 5]
104      ],
105      [
106        [/^0x[0-9a-f]{8,16}/, ADDRESS_LINK_STYLE],
107        [/^[^\)]/, null],
108        [/^\)$/, null, -1],
109        [/^\)/, null, 3]
110      ],
111      [
112        [/^; debug\: position /, COMMENT_STYLE, 6],
113        [/^.+$/, COMMENT_STYLE, -1]
114      ],
115      [
116        [/^\d+$/, POSITION_STYLE, -1],
117      ]
118    ];
119    view.setPatterns(patterns);
120  }
121
122  lineLocation(li) {
123    let view = this;
124    let result = undefined;
125    for (let i = 0; i < li.children.length; ++i) {
126      let fragment = li.children[i];
127      let location = fragment.location;
128      if (location != null) {
129        if (location.block_id != undefined) {
130          if (result === undefined) result = {};
131          result.block_id = location.block_id;
132        }
133        if (location.address != undefined) {
134          if (result === undefined) result = {};
135          result.address = location.address;
136        }
137        if (location.pos_start != undefined && location.pos_end != undefined) {
138          if (result === undefined) result = {};
139          result.pos_start = location.pos_start;
140          result.pos_end = location.pos_end;
141        }
142        else if (view.pos_start != -1) {
143          if (result === undefined) result = {};
144          result.pos_start = view.pos_start;
145          result.pos_end = result.pos_start + 1;
146        }
147      }
148    }
149    return result;
150  }
151
152  initializeContent(data, rememberedSelection) {
153    this.data = data;
154    super.initializeContent(data, rememberedSelection);
155  }
156
157  initializeCode(sourceText, sourcePosition) {
158    let view = this;
159    view.pos_start = -1;
160    view.addr_event_counts = null;
161    view.total_event_counts = null;
162    view.max_event_counts = null;
163    view.pos_lines = new Array();
164    // Comment lines for line 0 include sourcePosition already, only need to
165    // add sourcePosition for lines > 0.
166    view.pos_lines[0] = sourcePosition;
167    if (sourceText != "") {
168      let base = sourcePosition;
169      let current = 0;
170      let source_lines = sourceText.split("\n");
171      for (let i = 1; i < source_lines.length; i++) {
172        // Add 1 for newline character that is split off.
173        current += source_lines[i-1].length + 1;
174        view.pos_lines[i] = base + current;
175      }
176    }
177  }
178
179  initializePerfProfile(eventCounts) {
180    let view = this;
181    if (eventCounts !== undefined) {
182      view.addr_event_counts = eventCounts;
183
184      view.total_event_counts = {};
185      view.max_event_counts = {};
186      for (let ev_name in view.addr_event_counts) {
187        let keys = Object.keys(view.addr_event_counts[ev_name]);
188        let values = keys.map(key => view.addr_event_counts[ev_name][key]);
189        view.total_event_counts[ev_name] = values.reduce((a, b) => a + b);
190        view.max_event_counts[ev_name] = values.reduce((a, b) => Math.max(a, b));
191      }
192    }
193    else {
194      view.addr_event_counts = null;
195      view.total_event_counts = null;
196      view.max_event_counts = null;
197    }
198  }
199
200  // Shorten decimals and remove trailing zeroes for readability.
201  humanize(num) {
202    return num.toFixed(3).replace(/\.?0+$/, "") + "%";
203  }
204
205  // Interpolate between the given start and end values by a fraction of val/max.
206  interpolate(val, max, start, end) {
207    return start + (end - start) * (val / max);
208  }
209
210  processLine(line) {
211    let view = this;
212    let func = function(match, p1, p2, p3) {
213      let nums = p2.split(":");
214      let li = Number(nums[0]);
215      let pos = Number(nums[1]);
216      if(li === 0)
217        pos -= view.pos_lines[0];
218      li++;
219      return p1 + li + ":" + pos + p3;
220    };
221    line = line.replace(view.SOURCE_POSITION_HEADER_REGEX, func);
222    let fragments = super.processLine(line);
223
224    // Add profiling data per instruction if available.
225    if (view.total_event_counts) {
226      let matches = /^(0x[0-9a-fA-F]+)\s+\d+\s+[0-9a-fA-F]+/.exec(line);
227      if (matches) {
228        let newFragments = [];
229        for (let event in view.addr_event_counts) {
230          let count = view.addr_event_counts[event][matches[1]];
231          let str = " ";
232          let css_cls = "prof";
233          if(count !== undefined) {
234            let perc = count / view.total_event_counts[event] * 100;
235
236            let col = { r: 255, g: 255, b: 255 };
237            for (let i = 0; i < PROF_COLS.length; i++) {
238              if (perc === PROF_COLS[i].perc) {
239                col = PROF_COLS[i].col;
240                break;
241              }
242              else if (perc > PROF_COLS[i].perc && perc < PROF_COLS[i + 1].perc) {
243                let col1 = PROF_COLS[i].col;
244                let col2 = PROF_COLS[i + 1].col;
245
246                let val = perc - PROF_COLS[i].perc;
247                let max = PROF_COLS[i + 1].perc - PROF_COLS[i].perc;
248
249                col.r = Math.round(view.interpolate(val, max, col1.r, col2.r));
250                col.g = Math.round(view.interpolate(val, max, col1.g, col2.g));
251                col.b = Math.round(view.interpolate(val, max, col1.b, col2.b));
252                break;
253              }
254            }
255
256            str = UNICODE_BLOCK;
257
258            let fragment = view.createFragment(str, css_cls);
259            fragment.title = event + ": " + view.humanize(perc) + " (" + count + ")";
260            fragment.style.color = "rgb(" + col.r + ", " + col.g + ", " + col.b + ")";
261
262            newFragments.push(fragment);
263          }
264          else
265            newFragments.push(view.createFragment(str, css_cls));
266
267        }
268        fragments = newFragments.concat(fragments);
269      }
270    }
271    return fragments;
272  }
273}
274