UNPKG

@specs-feup/clava

Version:

A C/C++ source-to-source compiler written in Typescript

242 lines 8.61 kB
import LoggerBase from "@specs-feup/lara/api/lara/code/LoggerBase.js"; import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js"; import Clava from "../../clava/Clava.js"; import { FunctionJp, Scope, } from "../../Joinpoints.js"; export default class Logger extends LoggerBase { _isCxx = false; constructor(isGlobal = false, filename) { super(isGlobal, filename); // Adds C/C++ specific types this.Type.set("LONGLONG", 100); // 64-bit int this.printfFormat[this.Type.get("LONGLONG")] = "%I64lld"; } /** * Adds code that prints the message built up to that point with the append() functions. * */ log($jp, insertBefore = false) { if ($jp === undefined) { this._warn("Given join point is undefined"); return; } const $function = this._logSetup($jp, insertBefore); if ($function === undefined) { return; } const $file = $function.getAncestor("file"); this._isCxx = $file.isCxx; let code = undefined; if ($file.isCxx) { code = this._log_cxx($file, $function); } else { code = this._log_c($file, $function); } if (code === undefined) { return; } this._insert($jp, insertBefore, code); return this; } /** * Appends an expression that represents a long long. * * @param expr - the expression to append * @returns The current logger instance */ appendLongLong(expr) { return this._append_private(expr, this.Type.get("LONGLONG")); } /** * Appends an expression that represents a long long. * * @param expr - the expression to append * @returns The current logger instance */ longLong(expr) { return this.appendLongLong(expr); } /**** PRIVATE METHODS ****/ /** * Checks the initial constrains before executing the actual log (ancestor function, minimum of elements to log, defines the value of insertBefore) * Should be called on the beggining of each implementation of log * * @returns Undefined on failure and a $function instance if successful */ _logSetup($jp, insertBefore = false) { // Validate join point if (!this._validateJp($jp, "function")) { return undefined; } if (this.currentElements.length === 0) { this._info("Nothing to log, call append() first"); return undefined; } return $jp.getAncestor("function"); } _log_cxx($file, $function) { if (Clava.useSpecsLogger) { return this._log_cxx_specslogger($file, $function); } else { return this._log_cxx_stdcpp($file, $function); } } _log_cxx_specslogger($file, $function) { const loggerName = this._setup_cxx_specslogger($file, $function); // Create code from elements const code = loggerName + ".msg(" + this.currentElements .map((element) => { return this._getPrintableContent(element); }) .join(", ") + ");"; return code; } /** * Sets up the code for the Logger in the file and function that is called */ _setup_cxx_specslogger($file, $function) { // Warn user about dependency to SpecsLogger library //Clava.infoProjectDependency("SpecsLogger", "https://github.com/specs-feup/specs-c-libs"); PrintOnce.message("Woven code has dependency to project SpecsLogger, which can be found at https://github.com/specs-feup/specs-c-libs"); const declaredName = this._declareName($function.getDeclaration(true), function () { return IdGenerator.next("clava_logger_"); }); const loggerName = declaredName.name; if (declaredName.alreadyDeclared) { return loggerName; } // Add include to Logger for Cpp only $file.addInclude("SpecsLogger.h", false); // Get correct logger let loggerDecl = undefined; // If filename use FileLogger if (this.filename !== undefined) { loggerDecl = "FileLogger " + loggerName + '("' + this.filename + '");'; } // Otherwise, use ConsoleLogger else { loggerDecl = "ConsoleLogger " + loggerName + ";"; } // Add declaration of correct logger $function.body.insertBegin(loggerDecl); return loggerName; } _log_cxx_stdcpp($file, $function) { let streamName; if (this.filename === undefined) { streamName = this._setup_cxx_stdcpp_console($file, $function); } else { streamName = this._setup_cxx_stdcpp_file($file, $function); } // Create code from elements. const code = streamName + " << " + this.currentElements .map((element) => { if (element.type === this.Type.get("NORMAL")) { return '"' + element.content + '"'; } return element.content; }) .join(" << ") + ";"; return code; } _setup_cxx_stdcpp_console($file, $function) { const streamName = "std::cout"; // Add include $file.addInclude("iostream", true); return streamName; } _setup_cxx_stdcpp_file($file, $function) { const declaredName = this._declareName($function.getDeclaration(true), function () { return IdGenerator.next("log_file_"); }); const streamName = declaredName.name; if (declaredName.alreadyDeclared) { return streamName; } // Add include $file.addInclude("fstream", true); // Declare file stream and open file $function.body.insertBegin(this._clava_logger_filename_declaration_cpp(streamName, this.filename)); return streamName; } _log_c($file, $function) { if (this.filename === undefined) { return this._log_c_console($file, $function); } else { return this._log_c_file($file, $function); } } _log_c_console($file, $function) { // Setup $file.addInclude("stdio.h", true); return this._printfFormat("printf"); } _log_c_file($file, $function) { const fileVar = this._log_c_file_setup($file, $function); return this._printfFormat("fprintf", "(" + fileVar + ", "); } _log_c_file_setup($file, $function) { const declaredName = this._declareName($function.getDeclaration(true), function () { return IdGenerator.next("log_file_"); }); const varname = declaredName.name; if (declaredName.alreadyDeclared) { return varname; } // Setup $file.addInclude("stdio.h", true); $file.addInclude("stdlib.h", true); // Declare and open file const code = this._clava_logger_filename_declaration(varname, this.filename); // Add code at beginning of the function $function.body.insertBegin(code); // Close file at the return points of the function $function.insertReturn("fclose(" + varname + ");"); return varname; } _insertCode($jp, insertBefore, code) { const insertBeforeString = insertBefore ? "before" : "after"; if (insertBefore) { $jp.insert(insertBeforeString, code); this.afterJp = $jp; } else { // If $jp is a 'scope' with a 'function' parent, insert before return instead if ($jp instanceof Scope && $jp.parent !== undefined && $jp.parent instanceof FunctionJp) { this.afterJp = $jp.parent.insertReturn(code); } else { this.afterJp = $jp.insertAfter(code); } } } _clava_logger_filename_declaration(varname, filename) { return ` FILE *${varname} = fopen("${filename}", "w+"); if (${varname} == NULL) { printf("Error opening file ${filename}\\n"); exit(1); } `; } _clava_logger_filename_declaration_cpp(streamName, filename) { return ` std::ofstream ${streamName}; ${streamName}.open("${filename}", std::ios_base::app); `; } } //# sourceMappingURL=Logger.js.map