* 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());
}
}
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<string, string> =>
output.split('\n').reduce((acc: Record<string, string>, 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;
}
}