11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciimport {SelectRelatedEvent} from './events.mjs';
61cb0ef41Sopenharmony_ciimport {CollapsableElement, DOM, formatBytes, formatMicroSeconds} from './helper.mjs';
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciDOM.defineCustomElement('view/code-panel',
91cb0ef41Sopenharmony_ci                        (templateText) =>
101cb0ef41Sopenharmony_ci                            class CodePanel extends CollapsableElement {
111cb0ef41Sopenharmony_ci  _timeline;
121cb0ef41Sopenharmony_ci  _selectedEntries;
131cb0ef41Sopenharmony_ci  _entry;
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci  constructor() {
161cb0ef41Sopenharmony_ci    super(templateText);
171cb0ef41Sopenharmony_ci    this._propertiesNode = this.$('#properties');
181cb0ef41Sopenharmony_ci    this._codeSelectNode = this.$('#codeSelect');
191cb0ef41Sopenharmony_ci    this._disassemblyNode = this.$('#disassembly');
201cb0ef41Sopenharmony_ci    this._feedbackVectorNode = this.$('#feedbackVector');
211cb0ef41Sopenharmony_ci    this._selectionHandler = new SelectionHandler(this._disassemblyNode);
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci    this._codeSelectNode.onchange = this._handleSelectCode.bind(this);
241cb0ef41Sopenharmony_ci    this.$('#selectedRelatedButton').onclick =
251cb0ef41Sopenharmony_ci        this._handleSelectRelated.bind(this)
261cb0ef41Sopenharmony_ci  }
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci  set timeline(timeline) {
291cb0ef41Sopenharmony_ci    this._timeline = timeline;
301cb0ef41Sopenharmony_ci    this.$('.panel').style.display = timeline.isEmpty() ? 'none' : 'inherit';
311cb0ef41Sopenharmony_ci    this.requestUpdate();
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci  set selectedEntries(entries) {
351cb0ef41Sopenharmony_ci    this._selectedEntries = entries;
361cb0ef41Sopenharmony_ci    this.entry = entries.first();
371cb0ef41Sopenharmony_ci  }
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  set entry(entry) {
401cb0ef41Sopenharmony_ci    this._entry = entry;
411cb0ef41Sopenharmony_ci    if (!entry) {
421cb0ef41Sopenharmony_ci      this._propertiesNode.propertyDict = {};
431cb0ef41Sopenharmony_ci    } else {
441cb0ef41Sopenharmony_ci      this._propertiesNode.propertyDict = {
451cb0ef41Sopenharmony_ci        '__this__': entry,
461cb0ef41Sopenharmony_ci        functionName: entry.functionName,
471cb0ef41Sopenharmony_ci        size: formatBytes(entry.size),
481cb0ef41Sopenharmony_ci        creationTime: formatMicroSeconds(entry.time / 1000),
491cb0ef41Sopenharmony_ci        sourcePosition: entry.sourcePosition,
501cb0ef41Sopenharmony_ci        script: entry.script,
511cb0ef41Sopenharmony_ci        type: entry.type,
521cb0ef41Sopenharmony_ci        kind: entry.kindName,
531cb0ef41Sopenharmony_ci        variants: entry.variants.length > 1 ? [undefined, ...entry.variants] :
541cb0ef41Sopenharmony_ci                                              undefined,
551cb0ef41Sopenharmony_ci      };
561cb0ef41Sopenharmony_ci    }
571cb0ef41Sopenharmony_ci    this.requestUpdate();
581cb0ef41Sopenharmony_ci  }
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci  _update() {
611cb0ef41Sopenharmony_ci    this._updateSelect();
621cb0ef41Sopenharmony_ci    this._updateDisassembly();
631cb0ef41Sopenharmony_ci    this._updateFeedbackVector();
641cb0ef41Sopenharmony_ci  }
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  _updateFeedbackVector() {
671cb0ef41Sopenharmony_ci    if (!this._entry?.feedbackVector) {
681cb0ef41Sopenharmony_ci      this._feedbackVectorNode.propertyDict = {};
691cb0ef41Sopenharmony_ci    } else {
701cb0ef41Sopenharmony_ci      const dict = this._entry.feedbackVector.toolTipDict;
711cb0ef41Sopenharmony_ci      delete dict.title;
721cb0ef41Sopenharmony_ci      delete dict.code;
731cb0ef41Sopenharmony_ci      this._feedbackVectorNode.propertyDict = dict;
741cb0ef41Sopenharmony_ci    }
751cb0ef41Sopenharmony_ci  }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  _updateDisassembly() {
781cb0ef41Sopenharmony_ci    this._disassemblyNode.innerText = '';
791cb0ef41Sopenharmony_ci    if (!this._entry?.code) return;
801cb0ef41Sopenharmony_ci    try {
811cb0ef41Sopenharmony_ci      this._disassemblyNode.appendChild(
821cb0ef41Sopenharmony_ci          new AssemblyFormatter(this._entry).fragment);
831cb0ef41Sopenharmony_ci    } catch (e) {
841cb0ef41Sopenharmony_ci      console.error(e);
851cb0ef41Sopenharmony_ci      this._disassemblyNode.innerText = this._entry.code;
861cb0ef41Sopenharmony_ci    }
871cb0ef41Sopenharmony_ci  }
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci  _updateSelect() {
901cb0ef41Sopenharmony_ci    const select = this._codeSelectNode;
911cb0ef41Sopenharmony_ci    if (select.data === this._selectedEntries) return;
921cb0ef41Sopenharmony_ci    select.data = this._selectedEntries;
931cb0ef41Sopenharmony_ci    select.options.length = 0;
941cb0ef41Sopenharmony_ci    const sorted =
951cb0ef41Sopenharmony_ci        this._selectedEntries.slice().sort((a, b) => a.time - b.time);
961cb0ef41Sopenharmony_ci    for (const code of this._selectedEntries) {
971cb0ef41Sopenharmony_ci      const option = DOM.element('option');
981cb0ef41Sopenharmony_ci      option.text = this._entrySummary(code);
991cb0ef41Sopenharmony_ci      option.data = code;
1001cb0ef41Sopenharmony_ci      select.add(option);
1011cb0ef41Sopenharmony_ci    }
1021cb0ef41Sopenharmony_ci  }
1031cb0ef41Sopenharmony_ci  _entrySummary(code) {
1041cb0ef41Sopenharmony_ci    if (code.isBuiltinKind) {
1051cb0ef41Sopenharmony_ci      return `${code.functionName}(...) t=${
1061cb0ef41Sopenharmony_ci          formatMicroSeconds(code.time)} size=${formatBytes(code.size)}`;
1071cb0ef41Sopenharmony_ci    }
1081cb0ef41Sopenharmony_ci    return `${code.functionName}(...) t=${formatMicroSeconds(code.time)} size=${
1091cb0ef41Sopenharmony_ci        formatBytes(code.size)} script=${code.script?.toString()}`;
1101cb0ef41Sopenharmony_ci  }
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci  _handleSelectCode() {
1131cb0ef41Sopenharmony_ci    this.entry = this._codeSelectNode.selectedOptions[0].data;
1141cb0ef41Sopenharmony_ci  }
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci  _handleSelectRelated(e) {
1171cb0ef41Sopenharmony_ci    if (!this._entry) return;
1181cb0ef41Sopenharmony_ci    this.dispatchEvent(new SelectRelatedEvent(this._entry));
1191cb0ef41Sopenharmony_ci  }
1201cb0ef41Sopenharmony_ci});
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ciconst kRegisters = ['rsp', 'rbp', 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi'];
1231cb0ef41Sopenharmony_ci// Make sure we dont match register on bytecode: Star1 or Star2
1241cb0ef41Sopenharmony_ciconst kAvoidBytecodeOpsRegexpSource = '(.*?[^a-zA-Z])'
1251cb0ef41Sopenharmony_ci// Look for registers in strings like:  movl rbx,[rcx-0x30]
1261cb0ef41Sopenharmony_ciconst kRegisterRegexpSource = `(?<register>${kRegisters.join('|')}|r[0-9]+)`
1271cb0ef41Sopenharmony_ciconst kRegisterSplitRegexp =
1281cb0ef41Sopenharmony_ci    new RegExp(`${kAvoidBytecodeOpsRegexpSource}${kRegisterRegexpSource}`)
1291cb0ef41Sopenharmony_ciconst kIsRegisterRegexp = new RegExp(`^${kRegisterRegexpSource}$`);
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ciconst kFullAddressRegexp = /(0x[0-9a-f]{8,})/;
1321cb0ef41Sopenharmony_ciconst kRelativeAddressRegexp = /([+-]0x[0-9a-f]+)/;
1331cb0ef41Sopenharmony_ciconst kAnyAddressRegexp = /(?<address>[+-]?0x[0-9a-f]+)/;
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ciconst kJmpRegexp = new RegExp(`jmp ${kRegisterRegexpSource}`);
1361cb0ef41Sopenharmony_ciconst kMovRegexp =
1371cb0ef41Sopenharmony_ci    new RegExp(`mov. ${kRegisterRegexpSource},${kAnyAddressRegexp.source}`);
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ciclass AssemblyFormatter {
1401cb0ef41Sopenharmony_ci  constructor(codeLogEntry) {
1411cb0ef41Sopenharmony_ci    this._fragment = new DocumentFragment();
1421cb0ef41Sopenharmony_ci    this._entry = codeLogEntry;
1431cb0ef41Sopenharmony_ci    this._lines = new Map();
1441cb0ef41Sopenharmony_ci    this._previousLine = undefined;
1451cb0ef41Sopenharmony_ci    this._parseLines();
1461cb0ef41Sopenharmony_ci    this._format();
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci  get fragment() {
1501cb0ef41Sopenharmony_ci    return this._fragment;
1511cb0ef41Sopenharmony_ci  }
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  _format() {
1541cb0ef41Sopenharmony_ci    let block = DOM.div(['basicBlock', 'header']);
1551cb0ef41Sopenharmony_ci    this._lines.forEach(line => {
1561cb0ef41Sopenharmony_ci      if (!block || line.isBlockStart) {
1571cb0ef41Sopenharmony_ci        this._fragment.appendChild(block);
1581cb0ef41Sopenharmony_ci        block = DOM.div('basicBlock');
1591cb0ef41Sopenharmony_ci      }
1601cb0ef41Sopenharmony_ci      block.appendChild(line.format())
1611cb0ef41Sopenharmony_ci    });
1621cb0ef41Sopenharmony_ci    this._fragment.appendChild(block);
1631cb0ef41Sopenharmony_ci  }
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci  _parseLines() {
1661cb0ef41Sopenharmony_ci    this._entry.code.split('\n').forEach(each => this._parseLine(each));
1671cb0ef41Sopenharmony_ci    this._findBasicBlocks();
1681cb0ef41Sopenharmony_ci  }
1691cb0ef41Sopenharmony_ci
1701cb0ef41Sopenharmony_ci  _parseLine(line) {
1711cb0ef41Sopenharmony_ci    const parts = line.split(' ');
1721cb0ef41Sopenharmony_ci    // Use unique placeholder for address:
1731cb0ef41Sopenharmony_ci    let lineAddress = -this._lines.size;
1741cb0ef41Sopenharmony_ci    for (let part of parts) {
1751cb0ef41Sopenharmony_ci      if (kFullAddressRegexp.test(part)) {
1761cb0ef41Sopenharmony_ci        lineAddress = parseInt(part);
1771cb0ef41Sopenharmony_ci        break;
1781cb0ef41Sopenharmony_ci      }
1791cb0ef41Sopenharmony_ci    }
1801cb0ef41Sopenharmony_ci    const newLine = new AssemblyLine(lineAddress, parts);
1811cb0ef41Sopenharmony_ci    // special hack for: mov reg 0x...; jmp reg;
1821cb0ef41Sopenharmony_ci    if (lineAddress <= 0 && this._previousLine) {
1831cb0ef41Sopenharmony_ci      const jmpMatch = line.match(kJmpRegexp);
1841cb0ef41Sopenharmony_ci      if (jmpMatch) {
1851cb0ef41Sopenharmony_ci        const register = jmpMatch.groups.register;
1861cb0ef41Sopenharmony_ci        const movMatch = this._previousLine.line.match(kMovRegexp);
1871cb0ef41Sopenharmony_ci        if (movMatch.groups.register === register) {
1881cb0ef41Sopenharmony_ci          newLine.outgoing.push(movMatch.groups.address);
1891cb0ef41Sopenharmony_ci        }
1901cb0ef41Sopenharmony_ci      }
1911cb0ef41Sopenharmony_ci    }
1921cb0ef41Sopenharmony_ci    this._lines.set(lineAddress, newLine);
1931cb0ef41Sopenharmony_ci    this._previousLine = newLine;
1941cb0ef41Sopenharmony_ci  }
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci  _findBasicBlocks() {
1971cb0ef41Sopenharmony_ci    const lines = Array.from(this._lines.values());
1981cb0ef41Sopenharmony_ci    for (let i = 0; i < lines.length; i++) {
1991cb0ef41Sopenharmony_ci      const line = lines[i];
2001cb0ef41Sopenharmony_ci      let forceBasicBlock = i == 0;
2011cb0ef41Sopenharmony_ci      if (i > 0 && i < lines.length - 1) {
2021cb0ef41Sopenharmony_ci        const prevHasAddress = lines[i - 1].address > 0;
2031cb0ef41Sopenharmony_ci        const currentHasAddress = lines[i].address > 0;
2041cb0ef41Sopenharmony_ci        const nextHasAddress = lines[i + 1].address > 0;
2051cb0ef41Sopenharmony_ci        if (prevHasAddress !== currentHasAddress &&
2061cb0ef41Sopenharmony_ci            currentHasAddress == nextHasAddress) {
2071cb0ef41Sopenharmony_ci          forceBasicBlock = true;
2081cb0ef41Sopenharmony_ci        }
2091cb0ef41Sopenharmony_ci      }
2101cb0ef41Sopenharmony_ci      if (forceBasicBlock) {
2111cb0ef41Sopenharmony_ci        // Add fake-incoming address to mark a block start.
2121cb0ef41Sopenharmony_ci        line.addIncoming(0);
2131cb0ef41Sopenharmony_ci      }
2141cb0ef41Sopenharmony_ci      line.outgoing.forEach(address => {
2151cb0ef41Sopenharmony_ci        const outgoing = this._lines.get(address);
2161cb0ef41Sopenharmony_ci        if (outgoing) outgoing.addIncoming(line.address);
2171cb0ef41Sopenharmony_ci      })
2181cb0ef41Sopenharmony_ci    }
2191cb0ef41Sopenharmony_ci  }
2201cb0ef41Sopenharmony_ci}
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ciclass AssemblyLine {
2231cb0ef41Sopenharmony_ci  constructor(address, parts) {
2241cb0ef41Sopenharmony_ci    this.address = address;
2251cb0ef41Sopenharmony_ci    this.outgoing = [];
2261cb0ef41Sopenharmony_ci    this.incoming = [];
2271cb0ef41Sopenharmony_ci    parts.forEach(part => {
2281cb0ef41Sopenharmony_ci      const fullMatch = part.match(kFullAddressRegexp);
2291cb0ef41Sopenharmony_ci      if (fullMatch) {
2301cb0ef41Sopenharmony_ci        let inlineAddress = parseInt(fullMatch[0]);
2311cb0ef41Sopenharmony_ci        if (inlineAddress != this.address) this.outgoing.push(inlineAddress);
2321cb0ef41Sopenharmony_ci        if (Number.isNaN(inlineAddress)) throw 'invalid address';
2331cb0ef41Sopenharmony_ci      } else if (kRelativeAddressRegexp.test(part)) {
2341cb0ef41Sopenharmony_ci        this.outgoing.push(this._toAbsoluteAddress(part));
2351cb0ef41Sopenharmony_ci      }
2361cb0ef41Sopenharmony_ci    });
2371cb0ef41Sopenharmony_ci    this.line = parts.join(' ');
2381cb0ef41Sopenharmony_ci  }
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci  get isBlockStart() {
2411cb0ef41Sopenharmony_ci    return this.incoming.length > 0;
2421cb0ef41Sopenharmony_ci  }
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci  addIncoming(address) {
2451cb0ef41Sopenharmony_ci    this.incoming.push(address);
2461cb0ef41Sopenharmony_ci  }
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ci  format() {
2491cb0ef41Sopenharmony_ci    const content = DOM.span({textContent: this.line + '\n'});
2501cb0ef41Sopenharmony_ci    let formattedCode = content.innerHTML.split(kRegisterSplitRegexp)
2511cb0ef41Sopenharmony_ci                            .map(part => this._formatRegisterPart(part))
2521cb0ef41Sopenharmony_ci                            .join('');
2531cb0ef41Sopenharmony_ci    formattedCode =
2541cb0ef41Sopenharmony_ci        formattedCode.split(kAnyAddressRegexp)
2551cb0ef41Sopenharmony_ci            .map((part, index) => this._formatAddressPart(part, index))
2561cb0ef41Sopenharmony_ci            .join('');
2571cb0ef41Sopenharmony_ci    // Let's replace the base-address since it doesn't add any value.
2581cb0ef41Sopenharmony_ci    // TODO
2591cb0ef41Sopenharmony_ci    content.innerHTML = formattedCode;
2601cb0ef41Sopenharmony_ci    return content;
2611cb0ef41Sopenharmony_ci  }
2621cb0ef41Sopenharmony_ci
2631cb0ef41Sopenharmony_ci  _formatRegisterPart(part) {
2641cb0ef41Sopenharmony_ci    if (!kIsRegisterRegexp.test(part)) return part;
2651cb0ef41Sopenharmony_ci    return `<span class="reg ${part}">${part}</span>`
2661cb0ef41Sopenharmony_ci  }
2671cb0ef41Sopenharmony_ci
2681cb0ef41Sopenharmony_ci  _formatAddressPart(part, index) {
2691cb0ef41Sopenharmony_ci    if (kFullAddressRegexp.test(part)) {
2701cb0ef41Sopenharmony_ci      // The first or second address must be the line address
2711cb0ef41Sopenharmony_ci      if (index <= 1) {
2721cb0ef41Sopenharmony_ci        return `<span class="addr line" data-addr="${part}">${part}</span>`;
2731cb0ef41Sopenharmony_ci      }
2741cb0ef41Sopenharmony_ci      return `<span class=addr data-addr="${part}">${part}</span>`;
2751cb0ef41Sopenharmony_ci    } else if (kRelativeAddressRegexp.test(part)) {
2761cb0ef41Sopenharmony_ci      return `<span class=addr data-addr="0x${
2771cb0ef41Sopenharmony_ci          this._toAbsoluteAddress(part).toString(16)}">${part}</span>`;
2781cb0ef41Sopenharmony_ci    } else {
2791cb0ef41Sopenharmony_ci      return part;
2801cb0ef41Sopenharmony_ci    }
2811cb0ef41Sopenharmony_ci  }
2821cb0ef41Sopenharmony_ci
2831cb0ef41Sopenharmony_ci  _toAbsoluteAddress(part) {
2841cb0ef41Sopenharmony_ci    return this.address + parseInt(part);
2851cb0ef41Sopenharmony_ci  }
2861cb0ef41Sopenharmony_ci}
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ciclass SelectionHandler {
2891cb0ef41Sopenharmony_ci  _currentRegisterHovered;
2901cb0ef41Sopenharmony_ci  _currentRegisterClicked;
2911cb0ef41Sopenharmony_ci
2921cb0ef41Sopenharmony_ci  constructor(node) {
2931cb0ef41Sopenharmony_ci    this._node = node;
2941cb0ef41Sopenharmony_ci    this._node.onmousemove = this._handleMouseMove.bind(this);
2951cb0ef41Sopenharmony_ci    this._node.onclick = this._handleClick.bind(this);
2961cb0ef41Sopenharmony_ci  }
2971cb0ef41Sopenharmony_ci
2981cb0ef41Sopenharmony_ci  $(query) {
2991cb0ef41Sopenharmony_ci    return this._node.querySelectorAll(query);
3001cb0ef41Sopenharmony_ci  }
3011cb0ef41Sopenharmony_ci
3021cb0ef41Sopenharmony_ci  _handleClick(event) {
3031cb0ef41Sopenharmony_ci    const target = event.target;
3041cb0ef41Sopenharmony_ci    if (target.classList.contains('addr')) {
3051cb0ef41Sopenharmony_ci      return this._handleClickAddress(target);
3061cb0ef41Sopenharmony_ci    } else if (target.classList.contains('reg')) {
3071cb0ef41Sopenharmony_ci      this._handleClickRegister(target);
3081cb0ef41Sopenharmony_ci    } else {
3091cb0ef41Sopenharmony_ci      this._clearRegisterSelection();
3101cb0ef41Sopenharmony_ci    }
3111cb0ef41Sopenharmony_ci  }
3121cb0ef41Sopenharmony_ci
3131cb0ef41Sopenharmony_ci  _handleClickAddress(target) {
3141cb0ef41Sopenharmony_ci    let targetAddress = target.getAttribute('data-addr') ?? target.innerText;
3151cb0ef41Sopenharmony_ci    // Clear any selection
3161cb0ef41Sopenharmony_ci    for (let addrNode of this.$('.addr.selected')) {
3171cb0ef41Sopenharmony_ci      addrNode.classList.remove('selected');
3181cb0ef41Sopenharmony_ci    }
3191cb0ef41Sopenharmony_ci    // Highlight all matching addresses
3201cb0ef41Sopenharmony_ci    let lineAddrNode;
3211cb0ef41Sopenharmony_ci    for (let addrNode of this.$(`.addr[data-addr="${targetAddress}"]`)) {
3221cb0ef41Sopenharmony_ci      addrNode.classList.add('selected');
3231cb0ef41Sopenharmony_ci      if (addrNode.classList.contains('line') && lineAddrNode == undefined) {
3241cb0ef41Sopenharmony_ci        lineAddrNode = addrNode;
3251cb0ef41Sopenharmony_ci      }
3261cb0ef41Sopenharmony_ci    }
3271cb0ef41Sopenharmony_ci    // Jump to potential target address.
3281cb0ef41Sopenharmony_ci    if (lineAddrNode) {
3291cb0ef41Sopenharmony_ci      lineAddrNode.scrollIntoView({behavior: 'smooth', block: 'nearest'});
3301cb0ef41Sopenharmony_ci    }
3311cb0ef41Sopenharmony_ci  }
3321cb0ef41Sopenharmony_ci
3331cb0ef41Sopenharmony_ci  _handleClickRegister(target) {
3341cb0ef41Sopenharmony_ci    this._setRegisterSelection(target.innerText);
3351cb0ef41Sopenharmony_ci    this._currentRegisterClicked = this._currentRegisterHovered;
3361cb0ef41Sopenharmony_ci  }
3371cb0ef41Sopenharmony_ci
3381cb0ef41Sopenharmony_ci  _handleMouseMove(event) {
3391cb0ef41Sopenharmony_ci    if (this._currentRegisterClicked) return;
3401cb0ef41Sopenharmony_ci    const target = event.target;
3411cb0ef41Sopenharmony_ci    if (!target.classList.contains('reg')) {
3421cb0ef41Sopenharmony_ci      this._clearRegisterSelection();
3431cb0ef41Sopenharmony_ci    } else {
3441cb0ef41Sopenharmony_ci      this._setRegisterSelection(target.innerText);
3451cb0ef41Sopenharmony_ci    }
3461cb0ef41Sopenharmony_ci  }
3471cb0ef41Sopenharmony_ci
3481cb0ef41Sopenharmony_ci  _clearRegisterSelection() {
3491cb0ef41Sopenharmony_ci    if (!this._currentRegisterHovered) return;
3501cb0ef41Sopenharmony_ci    for (let node of this.$('.reg.selected')) {
3511cb0ef41Sopenharmony_ci      node.classList.remove('selected');
3521cb0ef41Sopenharmony_ci    }
3531cb0ef41Sopenharmony_ci    this._currentRegisterClicked = undefined;
3541cb0ef41Sopenharmony_ci    this._currentRegisterHovered = undefined;
3551cb0ef41Sopenharmony_ci  }
3561cb0ef41Sopenharmony_ci
3571cb0ef41Sopenharmony_ci  _setRegisterSelection(register) {
3581cb0ef41Sopenharmony_ci    if (register == this._currentRegisterHovered) return;
3591cb0ef41Sopenharmony_ci    this._clearRegisterSelection();
3601cb0ef41Sopenharmony_ci    this._currentRegisterHovered = register;
3611cb0ef41Sopenharmony_ci    for (let node of this.$(`.reg.${register}`)) {
3621cb0ef41Sopenharmony_ci      node.classList.add('selected');
3631cb0ef41Sopenharmony_ci    }
3641cb0ef41Sopenharmony_ci  }
3651cb0ef41Sopenharmony_ci}
366