@specs-feup/clava
Version:
A C/C++ source-to-source compiler written in Typescript
163 lines • 6.81 kB
JavaScript
import Io from "@specs-feup/lara/api/lara/Io.js";
import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js";
import Query from "@specs-feup/lara/api/weaver/Query.js";
import { FileJp, FunctionJp, If, Loop, StorageClass, Vardecl, } from "../Joinpoints.js";
import Clava from "./Clava.js";
/**
* Utility methods related with the source code.
*
*/
export default class ClavaCode {
/**
* Writes the code corresponding to the current AST as a single file.
*
*/
static toSingleFile(fileOrBaseFolder, optionalFile) {
if (fileOrBaseFolder === undefined) {
fileOrBaseFolder = Clava.getWeavingFolder();
const extension = Clava.isCxx() ? "cpp" : "c";
optionalFile = "main." + extension;
}
const singleFileCode = ClavaCode.toSingleFileCode();
let outputFile = Io.getPath(fileOrBaseFolder, optionalFile);
Io.writeFile(outputFile, singleFileCode);
// Copy includes
const baseFolder = outputFile.getParentFile();
for (const $include of Clava.getAvailableIncludes()) {
outputFile = Io.getPath(baseFolder, $include.name);
Io.writeFile(outputFile, Io.readFile($include.filepath));
}
}
/**
* Generates code for a single fime corresponding to the current AST.
*
* @returns The code of the current AST as a single file.
*/
static toSingleFileCode() {
const staticVerification = true;
const includes = new Set();
let bodyCode = "";
for (const $file of Query.search(FileJp)) {
if ($file.isHeader) {
continue;
}
// Deal with static declarations
const codeChanged = ClavaCode.renameStaticDeclarations($file, staticVerification);
if (codeChanged) {
bodyCode += $file.code + "\n";
console.log("Generated file '" +
$file.filepath +
"' from AST, macros have disappeared");
}
else {
bodyCode += Io.readFile($file.filepath) + "\n";
}
// Collects all includes from input files, in order to put them at the beginning of the file
for (const $child of $file.astChildren) {
if ($child.astName === "IncludeDecl") {
includes.add($child.code);
}
}
}
const singleFileCode = Array.from(includes).join("\n") + "\n" + bodyCode;
return singleFileCode;
}
static renameStaticDeclarations($file, staticVerification) {
if (!staticVerification) {
return false;
}
let changedCode = false;
// Look for static declarations
for (const child of $file.children) {
if (child instanceof FunctionJp &&
child.storageClass === StorageClass.STATIC) {
const newName = child.name + "_static_rename";
child.name = newName;
changedCode = true;
}
if (child instanceof Vardecl &&
child.storageClass === StorageClass.STATIC) {
console.log(child.code);
throw "Not yet supported for static variable declarations";
}
}
return changedCode;
}
/**
* Tries to statically detect if a statement is executed only once.
*
* Restrictions:
* - Does not take into account runtime execution problems, such as exceptions;
* - Does not detect if the function is called indirectly through a function pointer;
*
* @returns True if it could be detected, within the restrictions, that the statement is only executed once.
*/
static isExecutedOnce($statement) {
// Go back until it finds the function body
let $currentScope = undefined;
if ($statement !== undefined) {
$currentScope = $statement.getAncestor("scope");
}
while ($currentScope !== undefined) {
const $scopeOwner = $currentScope.owner;
// If finds a scope that is part of a loop or if/else, return false immediately
if ($scopeOwner instanceof Loop || $scopeOwner instanceof If) {
debug("ClavaCode.isExecutedOnce: failed because scope is part of loop or if");
return false;
}
// If function, check if main function
if ($scopeOwner instanceof FunctionJp) {
const $function = $scopeOwner;
// If main, passes check
if ($function.name === "main") {
return true;
}
// Verify if function is called only once
const calls = $function.calls;
if (calls.length !== 1) {
debug("ClavaCode.isExecutedOnce: failed because function '" +
$function.name +
"' is called " +
calls.length +
" times");
return false;
}
const $singleCall = calls[0];
// Recursively call the function on the call statement
return ClavaCode.isExecutedOnce($singleCall.getAncestor("statement"));
}
$currentScope = $currentScope.getAncestor("scope");
}
// Could not find the scope of the statement. Is it outside of a function?
debug("ClavaCode.isExecutedOnce: failed because scope could not be found");
return false;
}
/**
* Returns the function definitions in the program with the given name.
*
* @param functionName - The name of the function to find
* @param isSingleFunction - If true, ensures there is a single definition with the given name
* @returns An array of function definitions, or a single function is 'isSingleFunction' is true
*/
static getFunctionDefinition(functionName, isSingleFunction) {
// Locate function
const functionSearch = Clava.getProgram()
.getDescendants("function")
.filter(($f) => {
const $function = $f;
return $function.name === functionName && $function.hasDefinition;
});
// If single function false, return search results
if (!isSingleFunction) {
return functionSearch;
}
if (functionSearch.length === 0) {
throw `Could not find function with name '${functionName}'`;
}
if (functionSearch.length > 1) {
throw `Found more than one definition for function with name '${functionName}'`;
}
return functionSearch[0];
}
}
//# sourceMappingURL=ClavaCode.js.map