/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { GEN_ABC_PLUGIN_NAME } from './common/ark_define'; import { ErrorCode, SubsystemCode, ES2ABC_ERROR_MAPPING } from './error_code'; interface ErrorInfo { code: ErrorCode; description: string; cause: string; position: string; solutions: string[]; details: string; } export class CommonLogger { private static instance: CommonLogger; private logger: Object; private hvigorConsoleLoggerMap: { [key in SubsystemCode]?: Object }; throwArkTsCompilerError: Object; recordErrorFileId?: Object; private constructor(rollupObject: Object) { this.logger = rollupObject.share.getLogger(GEN_ABC_PLUGIN_NAME); this.throwArkTsCompilerError = rollupObject.share.throwArkTsCompilerError; this.recordErrorFileId = rollupObject.share?.recordErrorFileId; this.hvigorConsoleLoggerMap = this.initializeHvigorConsoleLoggers(rollupObject); } private initializeHvigorConsoleLoggers(rollupObject: Object): { [key in SubsystemCode]?: Object } { const loggerMap: { [key in SubsystemCode]?: Object } = {}; if (typeof rollupObject.share.getHvigorConsoleLogger === 'function') { loggerMap[SubsystemCode.ETS2BUNDLE] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ETS2BUNDLE); loggerMap[SubsystemCode.ABC2PROGRAM] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ABC2PROGRAM); loggerMap[SubsystemCode.ES2ABC] = rollupObject.share.getHvigorConsoleLogger(SubsystemCode.ES2ABC); } return loggerMap; } static getInstance(rollupObject: Object): CommonLogger { if (!CommonLogger.instance) { CommonLogger.instance = new CommonLogger(rollupObject); } return CommonLogger.instance; } static destroyInstance(): void { CommonLogger.instance = undefined; } info(...args: string[]): void { this.logger.info(...args); } debug(...args: string[]): void { this.logger.debug(...args); } warn(...args: string[]): void { this.logger.warn(...args); } error(...args: string[]): void { this.logger.error(...args); } printError(error: LogData | string): void { if (typeof error === 'string') { this.logger.error(error); return; } const hvigorConsoleLogger = this.getLoggerFromErrorCode(error.code); if (hvigorConsoleLogger) { hvigorConsoleLogger.printError(error); } else { this.logger.error(error.toString()); } } printErrorAndExit(error: LogData | string): void { if (typeof error === 'string') { this.throwArkTsCompilerError(error); return; } const hvigorConsoleLogger = this.getLoggerFromErrorCode(error.code); if (hvigorConsoleLogger) { hvigorConsoleLogger.printErrorAndExit(error); } else { this.throwArkTsCompilerError(error.toString()); } } // return the file name that failed to process ohmurl during compilation. returnErrorFileId(filePath?: string): void { if (filePath) { this?.recordErrorFileId?.(filePath); } } private isValidErrorCode(errorCode: string): boolean { return /^\d{8}$/.test(errorCode); } private getLoggerFromErrorCode(errorCode: string): Object | undefined { if (!this.isValidErrorCode(errorCode)) { return undefined; } const subsystemCode = errorCode.slice(0, 3); return this.hvigorConsoleLoggerMap[subsystemCode]; } } export class LogDataFactory { static newInstance( code: ErrorCode, description: string, cause: string = '', position: string = '', solutions: string[] = [] ): LogData { const data: LogData = new LogData(code, description, cause, position, solutions); return data; } /** * Parses an es2abc error string and returns a LogData instance. * @param error The es2abc error string to parse. * @returns A LogData instance or undefined if no matching error is found. */ static newInstanceFromEs2AbcError(error: string): LogData | undefined { for (const prefix in ES2ABC_ERROR_MAPPING) { if (error.startsWith(prefix)) { const { code, description, solutions } = ES2ABC_ERROR_MAPPING[prefix]; const cause = error.replace(prefix, ''); return LogDataFactory.newInstance(code, description, cause, '', solutions); } } return undefined; } static newInstanceFromBytecodeObfuscation(errorOutput: string, statusCode: number): LogData | undefined { const trimmedOutput = errorOutput?.trim(); if (!trimmedOutput) { return LogDataFactory.newInstance( ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR, 'Bytecode program terminated abnormally', `Status code: ${statusCode}`); } const parseErrorLines = (output: string): Record => output.split('\n').reduce((acc: Record, line) => { const [key, ...values] = line.split(':').map(part => part.trim()); return key && values.length ? { ...acc, [key]: values.join(':').trim() } : acc; }, {}); const parsedErrors = parseErrorLines(trimmedOutput); const getErrorInfo = (): ErrorInfo => { if (Object.keys(parsedErrors).length === 0) { return { code: ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR, description: trimmedOutput, cause: '', position: '', solutions: [], details: `Status code: ${statusCode}`, }; } return { code: parsedErrors['[ErrorCode]'] ?? ErrorCode.BYTECODE_OBFUSCATION_COMMON_ERROR, description: parsedErrors['[Description]'] ?? parsedErrors['[Cause]'] ?? 'Unknown error', cause: parsedErrors['[Cause]'] ?? '', position: parsedErrors['[Position]'] ?? '', solutions: parsedErrors['[Solutions]']?.split(',').filter(Boolean) ?? [], details: `Status code: ${statusCode}`, }; }; const { code, description, cause, position, solutions, details }: ErrorInfo = getErrorInfo(); return LogDataFactory.newInstance( code, description, details ?? cause, position, solutions ); } } export class LogData { code: string; description: string; cause: string; position: string; solutions: string[]; constructor( code: ErrorCode, description: string, cause: string = '', position: string = '', solutions: string[] = [] ) { this.code = code; this.description = description; this.cause = cause; this.position = position; this.solutions = solutions; } toString(): string { let errorString = `ERROR Code: ${this.code} ${this.description}\n`; if (this.cause || this.position) { errorString += `Error Message: ${this.cause}`; if (this.position) { errorString += ` ${this.position}`; } errorString += '\n\n'; } if (this.solutions.length > 0 && this.solutions[0] !== '') { errorString += `* Try the following: \n${this.solutions.map(str => ` > ${str}`).join('\n')}\n`; } return errorString; } }