• 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
38  private constructor(rollupObject: Object) {
39    this.logger = rollupObject.share.getLogger(GEN_ABC_PLUGIN_NAME);
40    this.throwArkTsCompilerError = rollupObject.share.throwArkTsCompilerError;
41    this.hvigorConsoleLoggerMap = this.initializeHvigorConsoleLoggers(rollupObject);
42  }
43
44  private initializeHvigorConsoleLoggers(rollupObject: Object): { [key in SubsystemCode]?: Object } {
45    const loggerMap: { [key in SubsystemCode]?: Object } = {};
46    if (typeof rollupObject.share.getHvigorConsoleLogger === 'function') {
47      loggerMap[SubsystemCode.ETS2BUNDLE] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ETS2BUNDLE);
48      loggerMap[SubsystemCode.ABC2PROGRAM] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ABC2PROGRAM);
49      loggerMap[SubsystemCode.ES2ABC] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ES2ABC);
50    }
51    return loggerMap;
52  }
53
54  static getInstance(rollupObject: Object): CommonLogger {
55    if (!CommonLogger.instance) {
56      CommonLogger.instance = new CommonLogger(rollupObject);
57    }
58    return CommonLogger.instance;
59  }
60
61  static destroyInstance(): void {
62    CommonLogger.instance = undefined;
63  }
64
65  info(...args: string[]): void {
66    this.logger.info(...args);
67  }
68
69  debug(...args: string[]): void {
70    this.logger.debug(...args);
71  }
72
73  warn(...args: string[]): void {
74    this.logger.warn(...args);
75  }
76
77  error(...args: string[]): void {
78    this.logger.error(...args);
79  }
80
81  printError(error: LogData | string): void {
82    if (typeof error === 'string') {
83      this.logger.error(error);
84      return;
85    }
86    const hvigorConsoleLogger = this.getLoggerFromErrorCode(error.code);
87    if (hvigorConsoleLogger) {
88      hvigorConsoleLogger.printError(error);
89    } else {
90      this.logger.error(error.toString());
91    }
92  }
93
94  printErrorAndExit(error: LogData | string): void {
95    if (typeof error === 'string') {
96      this.throwArkTsCompilerError(error);
97      return;
98    }
99    const hvigorConsoleLogger = this.getLoggerFromErrorCode(error.code);
100    if (hvigorConsoleLogger) {
101      hvigorConsoleLogger.printErrorAndExit(error);
102    } else {
103      this.throwArkTsCompilerError(error.toString());
104    }
105  }
106
107  private isValidErrorCode(errorCode: string): boolean {
108    return /^\d{8}$/.test(errorCode);
109  }
110
111  private getLoggerFromErrorCode(errorCode: string): Object | undefined {
112    if (!this.isValidErrorCode(errorCode)) {
113      return undefined;
114    }
115    const subsystemCode = errorCode.slice(0, 3);
116    return this.hvigorConsoleLoggerMap[subsystemCode];
117  }
118}
119
120export class LogDataFactory {
121
122  static newInstance(
123    code: ErrorCode,
124    description: string,
125    cause: string = '',
126    position: string = '',
127    solutions: string[] = []
128  ): LogData {
129    const data: LogData = new LogData(code, description, cause, position, solutions);
130    return data;
131  }
132
133  /**
134 * Parses an es2abc error string and returns a LogData instance.
135 * @param error The es2abc error string to parse.
136 * @returns A LogData instance or undefined if no matching error is found.
137 */
138  static newInstanceFromEs2AbcError(error: string): LogData | undefined {
139    for (const prefix in ES2ABC_ERROR_MAPPING) {
140      if (error.startsWith(prefix)) {
141        const { code, description, solutions } = ES2ABC_ERROR_MAPPING[prefix];
142        const cause = error.replace(prefix, '');
143        return LogDataFactory.newInstance(code, description, cause, '', solutions);
144      }
145    }
146    return undefined;
147  }
148
149  static newInstanceFromBytecodeObfuscation(errorOutput: string, statusCode: number): LogData | undefined {
150    const trimmedOutput = errorOutput?.trim();
151    if (!trimmedOutput) {
152      return LogDataFactory.newInstance(
153        ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR,
154        'Bytecode program terminated abnormally', `Status code: ${statusCode}`);
155    }
156
157    const parseErrorLines = (output: string): Record<string, string> =>
158      output.split('\n').reduce((acc: Record<string, string>, line) => {
159          const [key, ...values] = line.split(':').map(part => part.trim());
160          return key && values.length ? { ...acc, [key]: values.join(':').trim() } : acc;
161        }, {});
162
163    const parsedErrors = parseErrorLines(trimmedOutput);
164
165    const getErrorInfo = (): ErrorInfo => {
166      if (Object.keys(parsedErrors).length === 0) {
167        return {
168          code: ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR,
169          description: trimmedOutput,
170          cause: '',
171          position: '',
172          solutions: [],
173          details: `Status code: ${statusCode}`,
174        };
175      }
176
177      return {
178        code: parsedErrors['[ErrorCode]'] ?? ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR,
179        description: parsedErrors['[Description]'] ?? parsedErrors['[Cause]'] ?? 'Unknown error',
180        cause: parsedErrors['[Cause]'] ?? '',
181        position: parsedErrors['[Position]'] ?? '',
182        solutions: parsedErrors['[Solutions]']?.split(',').filter(Boolean) ?? [],
183        details: `Status code: ${statusCode}`,
184      };
185    };
186
187    const { code, description, cause, position, solutions, details }: ErrorInfo = getErrorInfo();
188    return LogDataFactory.newInstance(
189      code,
190      description,
191      details ?? cause,
192      position,
193      solutions
194    );
195  }
196}
197
198export class LogData {
199
200  code: string;
201  description: string;
202  cause: string;
203  position: string;
204  solutions: string[];
205
206  constructor(
207    code: ErrorCode,
208    description: string,
209    cause: string = '',
210    position: string = '',
211    solutions: string[] = []
212  ) {
213    this.code = code;
214    this.description = description;
215    this.cause = cause;
216    this.position = position;
217    this.solutions = solutions;
218  }
219
220  toString(): string {
221    let errorString = `ERROR Code: ${this.code} ${this.description}\n`;
222
223    if (this.cause || this.position) {
224      errorString += `Error Message: ${this.cause}`;
225      if (this.position) {
226        errorString += ` ${this.position}`;
227      }
228      errorString += '\n\n';
229    }
230
231    if (this.solutions.length > 0 && this.solutions[0] !== '') {
232      errorString += `* Try the following: \n${this.solutions.map(str => `  > ${str}`).join('\n')}\n`;
233    }
234
235    return errorString;
236  }
237}