13af6ab5fSopenharmony_ci/*
23af6ab5fSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License.
53af6ab5fSopenharmony_ci * You may obtain a copy of the License at
63af6ab5fSopenharmony_ci *
73af6ab5fSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
83af6ab5fSopenharmony_ci *
93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and
133af6ab5fSopenharmony_ci * limitations under the License.
143af6ab5fSopenharmony_ci */
153af6ab5fSopenharmony_ci
163af6ab5fSopenharmony_ciimport * as fs from 'fs';
173af6ab5fSopenharmony_ci
183af6ab5fSopenharmony_ciexport enum EventList {
193af6ab5fSopenharmony_ci  OBFUSCATION_INITIALIZATION = 'Obfuscation initialization',
203af6ab5fSopenharmony_ci  ALL_FILES_OBFUSCATION = 'All files obfuscation',
213af6ab5fSopenharmony_ci  CREATE_AST = 'Create AST',
223af6ab5fSopenharmony_ci  OBFUSCATE_AST = 'Obfuscate AST',
233af6ab5fSopenharmony_ci  VIRTUAL_CONSTRUCTOR_OBFUSCATION = 'Virtual constructor obfuscation',
243af6ab5fSopenharmony_ci  SHORT_HAND_OBFUSCATION = 'Shorthand obfuscation',
253af6ab5fSopenharmony_ci  REMOVE_CONSOLE = 'Remove console',
263af6ab5fSopenharmony_ci  PROPERTY_OBFUSCATION = 'Property obfuscation',
273af6ab5fSopenharmony_ci  IDENTIFIER_OBFUSCATION = 'Identifier obfuscation',
283af6ab5fSopenharmony_ci  CREATE_CHECKER = 'Create checker',
293af6ab5fSopenharmony_ci  SCOPE_ANALYZE = 'Scope analyze',
303af6ab5fSopenharmony_ci  CREATE_OBFUSCATED_NAMES = 'Create obfuscated names',
313af6ab5fSopenharmony_ci  OBFUSCATE_NODES = 'Obfuscate nodes',
323af6ab5fSopenharmony_ci  FILENAME_OBFUSCATION = 'Filename obfuscation',
333af6ab5fSopenharmony_ci  CREATE_PRINTER = 'Create Printer'
343af6ab5fSopenharmony_ci}
353af6ab5fSopenharmony_ci
363af6ab5fSopenharmony_ciexport enum EventIndentation {
373af6ab5fSopenharmony_ci  TWOSPACE = 2,
383af6ab5fSopenharmony_ci  ONESPACE = 1,
393af6ab5fSopenharmony_ci  NOSPACE = 0,
403af6ab5fSopenharmony_ci};
413af6ab5fSopenharmony_ci
423af6ab5fSopenharmony_ciexport const eventList = new Map<string, number>([
433af6ab5fSopenharmony_ci  [EventList.OBFUSCATION_INITIALIZATION, EventIndentation.NOSPACE],
443af6ab5fSopenharmony_ci  [EventList.ALL_FILES_OBFUSCATION, EventIndentation.NOSPACE],
453af6ab5fSopenharmony_ci  [EventList.CREATE_AST, EventIndentation.ONESPACE],
463af6ab5fSopenharmony_ci  [EventList.OBFUSCATE_AST, EventIndentation.ONESPACE],
473af6ab5fSopenharmony_ci  [EventList.VIRTUAL_CONSTRUCTOR_OBFUSCATION, EventIndentation.TWOSPACE],
483af6ab5fSopenharmony_ci  [EventList.SHORT_HAND_OBFUSCATION, EventIndentation.TWOSPACE],
493af6ab5fSopenharmony_ci  [EventList.REMOVE_CONSOLE, EventIndentation.TWOSPACE],
503af6ab5fSopenharmony_ci  [EventList.PROPERTY_OBFUSCATION, EventIndentation.TWOSPACE],
513af6ab5fSopenharmony_ci  [EventList.IDENTIFIER_OBFUSCATION, EventIndentation.TWOSPACE],
523af6ab5fSopenharmony_ci  [EventList.CREATE_CHECKER, EventIndentation.TWOSPACE],
533af6ab5fSopenharmony_ci  [EventList.SCOPE_ANALYZE, EventIndentation.TWOSPACE],
543af6ab5fSopenharmony_ci  [EventList.CREATE_OBFUSCATED_NAMES, EventIndentation.TWOSPACE],
553af6ab5fSopenharmony_ci  [EventList.OBFUSCATE_NODES, EventIndentation.TWOSPACE],
563af6ab5fSopenharmony_ci  [EventList.FILENAME_OBFUSCATION, EventIndentation.TWOSPACE],
573af6ab5fSopenharmony_ci  [EventList.CREATE_PRINTER, EventIndentation.ONESPACE],
583af6ab5fSopenharmony_ci]);
593af6ab5fSopenharmony_ci
603af6ab5fSopenharmony_ciexport interface TimeAndMemInfo {
613af6ab5fSopenharmony_ci  start: number;
623af6ab5fSopenharmony_ci  duration: number;
633af6ab5fSopenharmony_ci  startMemory: number;
643af6ab5fSopenharmony_ci  endMemory: number;
653af6ab5fSopenharmony_ci  memoryUsage: number;
663af6ab5fSopenharmony_ci}
673af6ab5fSopenharmony_ci
683af6ab5fSopenharmony_ciconst MILLISECOND_TO_SECOND = 1000;
693af6ab5fSopenharmony_ciconst BYTE_TO_MB = 1024 * 1024;
703af6ab5fSopenharmony_ciconst SIG_FIGS = 3;// 有效数字位数
713af6ab5fSopenharmony_ciconst INDENT = '    ';
723af6ab5fSopenharmony_ci
733af6ab5fSopenharmony_ciabstract class BasePrinter {
743af6ab5fSopenharmony_ci  protected outputPath: string | undefined;
753af6ab5fSopenharmony_ci
763af6ab5fSopenharmony_ci  protected abstract getCurrentEventData(): string;
773af6ab5fSopenharmony_ci
783af6ab5fSopenharmony_ci  constructor(outputPath: string = "") {
793af6ab5fSopenharmony_ci    this.outputPath = outputPath;
803af6ab5fSopenharmony_ci  }
813af6ab5fSopenharmony_ci
823af6ab5fSopenharmony_ci  setOutputPath(outputPath: string | undefined) {
833af6ab5fSopenharmony_ci    this.outputPath = outputPath;
843af6ab5fSopenharmony_ci  }
853af6ab5fSopenharmony_ci
863af6ab5fSopenharmony_ci  print(message: string): void {
873af6ab5fSopenharmony_ci    if (this.outputPath && this.outputPath !== "") {
883af6ab5fSopenharmony_ci      fs.appendFileSync(`${this.outputPath}`, message + "\n");
893af6ab5fSopenharmony_ci    } else {
903af6ab5fSopenharmony_ci      console.log(message);
913af6ab5fSopenharmony_ci    }
923af6ab5fSopenharmony_ci  }
933af6ab5fSopenharmony_ci
943af6ab5fSopenharmony_ci  outputData(): void {
953af6ab5fSopenharmony_ci    const eventData = this.getCurrentEventData();
963af6ab5fSopenharmony_ci    this.print(eventData);
973af6ab5fSopenharmony_ci  }
983af6ab5fSopenharmony_ci
993af6ab5fSopenharmony_ci  // Only used for ut
1003af6ab5fSopenharmony_ci  getOutputPath(): string {
1013af6ab5fSopenharmony_ci    return this.outputPath;
1023af6ab5fSopenharmony_ci  }
1033af6ab5fSopenharmony_ci}
1043af6ab5fSopenharmony_ci
1053af6ab5fSopenharmony_ciexport class TimeTracker extends BasePrinter {
1063af6ab5fSopenharmony_ci  private eventStack: Map<string, TimeAndMemInfo> = new Map<string, TimeAndMemInfo>();
1073af6ab5fSopenharmony_ci  private filesTimeSum: number = 0;
1083af6ab5fSopenharmony_ci  private maxTimeUsage = 0;
1093af6ab5fSopenharmony_ci  private maxTimeFile = "";
1103af6ab5fSopenharmony_ci  private maxMemoryUsage: number = 0;
1113af6ab5fSopenharmony_ci  private maxMemoryFile: string = '';
1123af6ab5fSopenharmony_ci
1133af6ab5fSopenharmony_ci  startEvent(eventName: string, timeSumPrinter?: TimeSumPrinter, currentFile?: string): void {
1143af6ab5fSopenharmony_ci    this.eventStack.set(eventName, {start: Date.now(), duration: 0, startMemory: process.memoryUsage().heapUsed,
1153af6ab5fSopenharmony_ci      endMemory: 0, memoryUsage: 0});
1163af6ab5fSopenharmony_ci    timeSumPrinter?.addEventDuration(eventName, 0);
1173af6ab5fSopenharmony_ci    if (eventName === EventList.CREATE_AST) {
1183af6ab5fSopenharmony_ci      this.print(currentFile);
1193af6ab5fSopenharmony_ci    }
1203af6ab5fSopenharmony_ci  }
1213af6ab5fSopenharmony_ci
1223af6ab5fSopenharmony_ci  endEvent(eventName: string, timeSumPrinter?: TimeSumPrinter, isFilesPrinter?: boolean): void {
1233af6ab5fSopenharmony_ci    if (!this.eventStack.get(eventName)) {
1243af6ab5fSopenharmony_ci      throw new Error(`Event "${eventName}" not started`);
1253af6ab5fSopenharmony_ci    }
1263af6ab5fSopenharmony_ci
1273af6ab5fSopenharmony_ci    const eventStartTime = this.eventStack.get(eventName).start;
1283af6ab5fSopenharmony_ci    const duration = (Date.now() - eventStartTime)/MILLISECOND_TO_SECOND;
1293af6ab5fSopenharmony_ci    const eventEndMemory = process.memoryUsage().heapUsed;
1303af6ab5fSopenharmony_ci    const eventStartMemory = this.eventStack.get(eventName).startMemory;
1313af6ab5fSopenharmony_ci    const memoryUsage = eventEndMemory - eventStartMemory;
1323af6ab5fSopenharmony_ci
1333af6ab5fSopenharmony_ci    if (isFilesPrinter) {
1343af6ab5fSopenharmony_ci      this.filesTimeSum += duration;
1353af6ab5fSopenharmony_ci
1363af6ab5fSopenharmony_ci      if (duration > this.maxTimeUsage) {
1373af6ab5fSopenharmony_ci        this.maxTimeUsage = duration;
1383af6ab5fSopenharmony_ci        this.maxTimeFile = eventName;
1393af6ab5fSopenharmony_ci      }
1403af6ab5fSopenharmony_ci
1413af6ab5fSopenharmony_ci      if (eventStartMemory > this.maxMemoryUsage) {
1423af6ab5fSopenharmony_ci        this.maxMemoryUsage = eventStartMemory;
1433af6ab5fSopenharmony_ci        this.maxMemoryFile = eventName;
1443af6ab5fSopenharmony_ci      }
1453af6ab5fSopenharmony_ci
1463af6ab5fSopenharmony_ci      if (eventEndMemory > this.maxMemoryUsage) {
1473af6ab5fSopenharmony_ci        this.maxMemoryUsage = eventEndMemory;
1483af6ab5fSopenharmony_ci        this.maxMemoryFile = eventName;
1493af6ab5fSopenharmony_ci      }
1503af6ab5fSopenharmony_ci    }
1513af6ab5fSopenharmony_ci
1523af6ab5fSopenharmony_ci    this.eventStack.get(eventName).duration = duration;
1533af6ab5fSopenharmony_ci    this.eventStack.get(eventName).endMemory = eventEndMemory;
1543af6ab5fSopenharmony_ci    this.eventStack.get(eventName).memoryUsage = memoryUsage;
1553af6ab5fSopenharmony_ci
1563af6ab5fSopenharmony_ci    timeSumPrinter?.addEventDuration(eventName, duration);
1573af6ab5fSopenharmony_ci
1583af6ab5fSopenharmony_ci    if ((eventName === EventList.ALL_FILES_OBFUSCATION)) {
1593af6ab5fSopenharmony_ci      this.eventStack.get(eventName).duration = this.filesTimeSum;
1603af6ab5fSopenharmony_ci      this.outputData();
1613af6ab5fSopenharmony_ci      const maxTimeUsage = this.maxTimeUsage.toFixed(SIG_FIGS);
1623af6ab5fSopenharmony_ci      const maxMemoryUsage = (this.maxMemoryUsage/BYTE_TO_MB).toFixed(SIG_FIGS);
1633af6ab5fSopenharmony_ci      this.print(`Max time cost: ${this.maxTimeFile}: ${maxTimeUsage}s`)
1643af6ab5fSopenharmony_ci      this.print(`Max memory usage: ${this.maxMemoryFile}: ${maxMemoryUsage}MB\n`)
1653af6ab5fSopenharmony_ci    }
1663af6ab5fSopenharmony_ci
1673af6ab5fSopenharmony_ci    if ((eventName === EventList.CREATE_PRINTER)) {
1683af6ab5fSopenharmony_ci      this.outputData();
1693af6ab5fSopenharmony_ci    }
1703af6ab5fSopenharmony_ci  }
1713af6ab5fSopenharmony_ci
1723af6ab5fSopenharmony_ci  getCurrentEventData(): string {
1733af6ab5fSopenharmony_ci    let eventData = "";
1743af6ab5fSopenharmony_ci    for (const eventName of this.eventStack.keys()) {
1753af6ab5fSopenharmony_ci      let depth = eventList.get(eventName)?? 0;
1763af6ab5fSopenharmony_ci      let eventInfo = this.eventStack.get(eventName);
1773af6ab5fSopenharmony_ci      const duration = eventInfo.duration;
1783af6ab5fSopenharmony_ci      const startMemory = eventInfo.startMemory/BYTE_TO_MB;
1793af6ab5fSopenharmony_ci      const endMemory = eventInfo.endMemory/BYTE_TO_MB;
1803af6ab5fSopenharmony_ci      const memoryUsage = eventInfo.memoryUsage/BYTE_TO_MB;
1813af6ab5fSopenharmony_ci      eventData += this.formatEvent(eventName, duration, startMemory, endMemory, memoryUsage, depth);
1823af6ab5fSopenharmony_ci    }
1833af6ab5fSopenharmony_ci    return eventData;
1843af6ab5fSopenharmony_ci  }
1853af6ab5fSopenharmony_ci
1863af6ab5fSopenharmony_ci  private formatEvent(eventName: string, duration: number, startMemory: number, endMemory: number,
1873af6ab5fSopenharmony_ci    memoryUsage: number, depth: number): string {
1883af6ab5fSopenharmony_ci    const indent = INDENT.repeat(depth);
1893af6ab5fSopenharmony_ci    const formattedDuration = duration.toFixed(SIG_FIGS) + 's';
1903af6ab5fSopenharmony_ci    const formatttedStartMemory = startMemory.toFixed(SIG_FIGS) + 'MB';
1913af6ab5fSopenharmony_ci    const formatttedEndMemory  = endMemory.toFixed(SIG_FIGS) + 'MB';
1923af6ab5fSopenharmony_ci    const formatttedMemoryUsage  = memoryUsage.toFixed(SIG_FIGS) + 'MB';
1933af6ab5fSopenharmony_ci    return `${indent}${eventName}: timeCost:${formattedDuration} startMemory:${formatttedStartMemory} `+
1943af6ab5fSopenharmony_ci    `endMemory:${formatttedEndMemory} memoryUsage:${formatttedMemoryUsage}\n`;
1953af6ab5fSopenharmony_ci  }
1963af6ab5fSopenharmony_ci
1973af6ab5fSopenharmony_ci  // Only used for ut
1983af6ab5fSopenharmony_ci  getEventStack(): Map<string, TimeAndMemInfo> {
1993af6ab5fSopenharmony_ci    return this.eventStack;
2003af6ab5fSopenharmony_ci  }
2013af6ab5fSopenharmony_ci
2023af6ab5fSopenharmony_ci  getFilesTimeSum(): number {
2033af6ab5fSopenharmony_ci    return this.filesTimeSum;
2043af6ab5fSopenharmony_ci  }
2053af6ab5fSopenharmony_ci
2063af6ab5fSopenharmony_ci  getMaxTimeUsage(): number {
2073af6ab5fSopenharmony_ci    return this.maxTimeUsage;
2083af6ab5fSopenharmony_ci  }
2093af6ab5fSopenharmony_ci
2103af6ab5fSopenharmony_ci  getMaxTimeFile(): string {
2113af6ab5fSopenharmony_ci    return this.maxTimeFile;
2123af6ab5fSopenharmony_ci  }
2133af6ab5fSopenharmony_ci
2143af6ab5fSopenharmony_ci  getMaxMemoryUsage(): number {
2153af6ab5fSopenharmony_ci    return this.maxMemoryUsage;
2163af6ab5fSopenharmony_ci  }
2173af6ab5fSopenharmony_ci
2183af6ab5fSopenharmony_ci  getMaxMemoryFile(): string {
2193af6ab5fSopenharmony_ci    return this.maxMemoryFile;
2203af6ab5fSopenharmony_ci  }
2213af6ab5fSopenharmony_ci}
2223af6ab5fSopenharmony_ci
2233af6ab5fSopenharmony_ciexport class TimeSumPrinter extends BasePrinter {
2243af6ab5fSopenharmony_ci  private eventSum: Map<string, number> = new Map<string, number>();
2253af6ab5fSopenharmony_ci  addEventDuration(eventName: string, duration: number): void {
2263af6ab5fSopenharmony_ci    const currentValue = this.eventSum.get(eventName) ?? 0;
2273af6ab5fSopenharmony_ci    this.eventSum.set(eventName, currentValue + duration);
2283af6ab5fSopenharmony_ci  }
2293af6ab5fSopenharmony_ci
2303af6ab5fSopenharmony_ci  summarizeEventDuration(): void {
2313af6ab5fSopenharmony_ci    const eventData = this.getCurrentEventData();
2323af6ab5fSopenharmony_ci    this.print(eventData);
2333af6ab5fSopenharmony_ci  }
2343af6ab5fSopenharmony_ci
2353af6ab5fSopenharmony_ci  getCurrentEventData(): string {
2363af6ab5fSopenharmony_ci    let eventData = "";
2373af6ab5fSopenharmony_ci    for (const eventName of this.eventSum.keys()) {
2383af6ab5fSopenharmony_ci      let depth = eventList.get(eventName)?? 0;
2393af6ab5fSopenharmony_ci      const duration = this.eventSum.get(eventName);
2403af6ab5fSopenharmony_ci      eventData += this.formatEvent(eventName, duration, depth);
2413af6ab5fSopenharmony_ci    }
2423af6ab5fSopenharmony_ci    return eventData;
2433af6ab5fSopenharmony_ci  }
2443af6ab5fSopenharmony_ci
2453af6ab5fSopenharmony_ci  private formatEvent(eventName: string, duration: number, depth: number): string {
2463af6ab5fSopenharmony_ci    const indent = INDENT.repeat(depth);
2473af6ab5fSopenharmony_ci    const formattedDuration = duration.toFixed(SIG_FIGS) + 's';
2483af6ab5fSopenharmony_ci    return `${indent}${eventName}: ${formattedDuration}\n`;
2493af6ab5fSopenharmony_ci  }
2503af6ab5fSopenharmony_ci
2513af6ab5fSopenharmony_ci  getEventSum(): Map<string, number> {
2523af6ab5fSopenharmony_ci    return this.eventSum;
2533af6ab5fSopenharmony_ci  }
2543af6ab5fSopenharmony_ci}