• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { GEN_ABC_PLUGIN_NAME } from './common/ark_define';
17import {
18  ErrorCode,
19  SubsystemCode,
20  ES2ABC_ERROR_MAPPING
21} from './error_code';
22
23interface ErrorInfo {
24  code: ErrorCode;
25  description: string;
26  cause: string;
27  position: string;
28  solutions: string[];
29  details: string;
30}
31
32export class CommonLogger {
33  private static instance: CommonLogger;
34  private logger: Object;
35  private hvigorConsoleLoggerMap: { [key in SubsystemCode]?: Object };
36  throwArkTsCompilerError: Object;
37  recordErrorFileId?: Object;
38
39  private constructor(rollupObject: Object) {
40    this.logger = rollupObject.share.getLogger(GEN_ABC_PLUGIN_NAME);
41    this.throwArkTsCompilerError = rollupObject.share.throwArkTsCompilerError;
42    this.recordErrorFileId = rollupObject.share?.recordErrorFileId;
43    this.hvigorConsoleLoggerMap = this.initializeHvigorConsoleLoggers(rollupObject);
44  }
45
46  private initializeHvigorConsoleLoggers(rollupObject: Object): { [key in SubsystemCode]?: Object } {
47    const loggerMap: { [key in SubsystemCode]?: Object } = {};
48    if (typeof rollupObject.share.getHvigorConsoleLogger === 'function') {
49      loggerMap[SubsystemCode.ETS2BUNDLE] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ETS2BUNDLE);
50      loggerMap[SubsystemCode.ABC2PROGRAM] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ABC2PROGRAM);
51      loggerMap[SubsystemCode.ES2ABC] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ES2ABC);
52    }
53    return loggerMap;
54  }
55
56  static getInstance(rollupObject: Object): CommonLogger {
57    if (!CommonLogger.instance) {
58      CommonLogger.instance = new CommonLogger(rollupObject);
59    }
60    return CommonLogger.instance;
61  }
62
63  static destroyInstance(): void {
64    CommonLogger.instance = undefined;
65  }
66
67  info(...args: string[]): void {
68    this.logger.info(...args);
69  }
70
71  debug(...args: string[]): void {
72    this.logger.debug(...args);
73  }
74
75  warn(...args: string[]): void {
76    this.logger.warn(...args);
77  }
78
79  error(...args: string[]): void {
80    this.logger.error(...args);
81  }
82
83  printError(error: LogData | string): void {
84    if (typeof error === 'string') {
85      this.logger.error(error);
86      return;
87    }
88    const hvigorConsoleLogger = this.getLoggerFromErrorCode(error.code);
89    if (hvigorConsoleLogger) {
90      hvigorConsoleLogger.printError(error);
91    } else {
92      this.logger.error(error.toString());
93    }
94  }
95
96  printErrorAndExit(error: LogData | string): void {
97    if (typeof error === 'string') {
98      this.throwArkTsCompilerError(error);
99      return;
100    }
101    const hvigorConsoleLogger = this.getLoggerFromErrorCode(error.code);
102    if (hvigorConsoleLogger) {
103      hvigorConsoleLogger.printErrorAndExit(error);
104    } else {
105      this.throwArkTsCompilerError(error.toString());
106    }
107  }
108
109  // return the file name that failed to process ohmurl during compilation.
110  returnErrorFileId(filePath?: string): void {
111    if (filePath) {
112      this?.recordErrorFileId?.(filePath);
113    }
114  }
115
116  private isValidErrorCode(errorCode: string): boolean {
117    return /^\d{8}$/.test(errorCode);
118  }
119
120  private getLoggerFromErrorCode(errorCode: string): Object | undefined {
121    if (!this.isValidErrorCode(errorCode)) {
122      return undefined;
123    }
124    const subsystemCode = errorCode.slice(0, 3);
125    return this.hvigorConsoleLoggerMap[subsystemCode];
126  }
127}
128
129export class LogDataFactory {
130
131  static newInstance(
132    code: ErrorCode,
133    description: string,
134    cause: string = '',
135    position: string = '',
136    solutions: string[] = []
137  ): LogData {
138    const data: LogData = new LogData(code, description, cause, position, solutions);
139    return data;
140  }
141
142  /**
143 * Parses an es2abc error string and returns a LogData instance.
144 * @param error The es2abc error string to parse.
145 * @returns A LogData instance or undefined if no matching error is found.
146 */
147  static newInstanceFromEs2AbcError(error: string): LogData | undefined {
148    for (const prefix in ES2ABC_ERROR_MAPPING) {
149      if (error.startsWith(prefix)) {
150        const { code, description, solutions } = ES2ABC_ERROR_MAPPING[prefix];
151        const cause = error.replace(prefix, '');
152        return LogDataFactory.newInstance(code, description, cause, '', solutions);
153      }
154    }
155    return undefined;
156  }
157
158  static newInstanceFromBytecodeObfuscation(errorOutput: string, statusCode: number): LogData | undefined {
159    const trimmedOutput = errorOutput?.trim();
160    if (!trimmedOutput) {
161      return LogDataFactory.newInstance(
162        ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR,
163        'Bytecode program terminated abnormally', `Status code: ${statusCode}`);
164    }
165
166    const parseErrorLines = (output: string): Record<string, string> =>
167      output.split('\n').reduce((acc: Record<string, string>, line) => {
168          const [key, ...values] = line.split(':').map(part => part.trim());
169          return key && values.length ? { ...acc, [key]: values.join(':').trim() } : acc;
170        }, {});
171
172    const parsedErrors = parseErrorLines(trimmedOutput);
173
174    const getErrorInfo = (): ErrorInfo => {
175      if (Object.keys(parsedErrors).length === 0) {
176        return {
177          code: ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR,
178          description: trimmedOutput,
179          cause: '',
180          position: '',
181          solutions: [],
182          details: `Status code: ${statusCode}`,
183        };
184      }
185
186      return {
187        code: parsedErrors['[ErrorCode]'] ?? ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR,
188        description: parsedErrors['[Description]'] ?? parsedErrors['[Cause]'] ?? 'Unknown error',
189        cause: parsedErrors['[Cause]'] ?? '',
190        position: parsedErrors['[Position]'] ?? '',
191        solutions: parsedErrors['[Solutions]']?.split(',').filter(Boolean) ?? [],
192        details: `Status code: ${statusCode}`,
193      };
194    };
195
196    const { code, description, cause, position, solutions, details }: ErrorInfo = getErrorInfo();
197    return LogDataFactory.newInstance(
198      code,
199      description,
200      details ?? cause,
201      position,
202      solutions
203    );
204  }
205}
206
207export class LogData {
208
209  code: string;
210  description: string;
211  cause: string;
212  position: string;
213  solutions: string[];
214
215  constructor(
216    code: ErrorCode,
217    description: string,
218    cause: string = '',
219    position: string = '',
220    solutions: string[] = []
221  ) {
222    this.code = code;
223    this.description = description;
224    this.cause = cause;
225    this.position = position;
226    this.solutions = solutions;
227  }
228
229  toString(): string {
230    let errorString = `ERROR Code: ${this.code} ${this.description}\n`;
231
232    if (this.cause || this.position) {
233      errorString += `Error Message: ${this.cause}`;
234      if (this.position) {
235        errorString += ` ${this.position}`;
236      }
237      errorString += '\n\n';
238    }
239
240    if (this.solutions.length > 0 && this.solutions[0] !== '') {
241      errorString += `* Try the following: \n${this.solutions.map(str => `  > ${str}`).join('\n')}\n`;
242    }
243
244    return errorString;
245  }
246}