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}