UNPKG

@specs-feup/kadabra

Version:

A Java source-to-source compiler written in Typescript

170 lines 7.53 kB
import Query from "@specs-feup/lara/api/weaver/Query.js"; import { App, Class, FileJp, Method, } from "../../Joinpoints.js"; /** * Prepares a given method call by: <br> * * extracting a functional interface <br> * * create a field of that type <br> * * initialize the field with the called method <br> * * replace the call with invocation of the field method <br> * NOTE: This is an alias for "PrepareCall" aspect */ export function ExtractToField($call, $method, $fieldLocation, newFile = true, $funcInterface = null) { if ($call === undefined || $call === null) { return { $field: undefined, $interface: undefined, $interfaceMethod: undefined, defaultMethod: undefined, }; } if ($method === undefined || $method === null) { const ancestor = $call.getAncestor("method"); if (ancestor === undefined) { throw new Error("No method found for the given call."); } $method = ancestor; } return PrepareCall($method, $call, $fieldLocation, newFile, $funcInterface); } /** * Prepares a given method call by: <br> * * extracting a functional interface <br> * * create a field of that type <br> * * initialize the field with the called method <br> * * replace the call with invocation of the field method */ export function PrepareCall($method = null, $call = null, $fieldLocation = null, newFile = true, $funcInterface = null) { if ($call === null || $method === null) { return { $field: null, $interface: $funcInterface, $interfaceMethod: undefined, defaultMethod: undefined, }; } if ($funcInterface === null || $funcInterface === undefined) { const extracted = ExtractFunctionalInterface($call.name, $call.declarator, ".*", false, newFile); $funcInterface = extracted.$interface; console.log(`[LOG] Extracted a functional interface "${$funcInterface.name}" based on method "${$call.name}"`); } const defaultMethod = $call.qualifiedDecl + "::" + $call.name; $fieldLocation ??= Query.search(Class, { qualifiedName: $method.declarator, }).getFirst(); if ($fieldLocation === undefined || $fieldLocation === null) { throw new Error("Could not get a location to insert new field. please verify the input arguments of PrepareCall"); } let $interfaceMethod; let $field; for (const method of Query.searchFrom($funcInterface, Method, $call.name)) { $interfaceMethod = method; const baseName = method.name; const modifiers = $method.isStatic ? ["static"] : []; $field = $fieldLocation.newField(modifiers, $funcInterface.qualifiedName, baseName, defaultMethod); console.log(`[LOG] Extracted a field "${$field.name}", from call "${$call.name}", to ${$field.declarator}`); //Changing call of medianNeighbor to call the local variable $call.target = $field.name; $call.executable = method; } if ($funcInterface != null && $field != null) { console.log(`[LOG] Call to "${$call.name}" (in method "${$method.name}") is ready!`); } return { $field, $interface: $funcInterface, $interfaceMethod, defaultMethod, }; } /* This aspect is used to extract a functional interface for a method of a class The interface is extracted to the same package as the target class TODO: deal with overloading //Will assume method without overloading (next step) */ export function ExtractFunctionalInterface(targetMethod, targetClass = ".*", targetFile = ".*", associate = false, newFile = true) { const search = Query.search(App) .search(FileJp, (f) => RegExp(targetFile).exec(f.name) !== null) .search(Class, (c) => RegExp(targetClass).exec(c.qualifiedName) !== null) .search(Method, targetMethod) .chain(); let $interface = undefined; let tempClass = undefined; let $defaultMethod = undefined; let $method = undefined; for (const result of search) { const $class = result.class; $method = result.method; if (tempClass !== undefined) { throw new Error(`More than one method to be extracted, please redefine this aspect call. Target method: ${targetMethod}. ` + `1st Location${tempClass.qualifiedName}. 2nd Location ${$class.qualifiedName}`); } console.log(`[LOG] Extracting functional interface from ${$class.name}#${$method.name}`); const interfacePackage = $class.packageName; const interfaceName = "I" + $method.name.charAt(0).toUpperCase() + $method.name.slice(1); const newInterface = $class.extractInterface(interfaceName, interfacePackage, $method, associate, newFile); if (!newFile) { newInterface.modifiers = ["static"]; $class.addInterface(newInterface); } $interface = newInterface; $defaultMethod = $method; tempClass = $class; } if ($method === undefined || $interface === undefined) { throw new Error("Could not find the method to extract a functional interface, specified by the conditions: " + `file{"${targetFile}"}.class{"${targetClass}"}.method{"${targetMethod}"}`); } const $function = Query.searchFrom($interface, Method, $method.name).getFirst(); return { $interface, $defaultMethod, $function, targetMethodName: targetMethod, }; } const DEFAULT_PACKAGE = "pt.up.fe.specs.lara.kadabra.utils"; /* Generate class for functional mapping*/ export function NewMappingClass($interface, methodName, getterType, $target = Query.root()) { const targetMethodFirstCap = methodName.charAt(0).toUpperCase() + methodName.slice(1); const mapClass = `${DEFAULT_PACKAGE}.${targetMethodFirstCap}Caller`; console.log("[LOG] Creating new functional mapping class: " + mapClass); const $mapClass = $target.mapVersions(mapClass, getterType, $interface, methodName); return { $mapClass, put: (key, value) => `${$mapClass.qualifiedName}.put(${key},${value})`, contains: (key) => `${$mapClass.qualifiedName}.contains(${key})`, get: (param, defaultMethod) => { if (defaultMethod === undefined) { return `${$mapClass.qualifiedName}.get(${param})`; } return `${$mapClass.qualifiedName}.get(${param},${defaultMethod})`; }, }; } /* Generate class for functional mapping*/ export function NewFunctionalMethodCaller2($interface = null, methodName = null, getterType = null, defaultMethodStr = null) { if ($interface === null || methodName === null || getterType === null || defaultMethodStr === null) { return { $mapClass: undefined, put: "put", contains: "contains", get: undefined, }; } const targetMethodFirstCap = methodName.charAt(0).toUpperCase() + methodName.slice(1); const mapClass = `${DEFAULT_PACKAGE}.${targetMethodFirstCap}Caller`; console.log("[LOG] Creating new functional mapping class: " + mapClass); const app = Query.root(); const $mapClass = app.mapVersions(mapClass, getterType, $interface, methodName); return { $mapClass, put: "put", contains: "contains", get: (param) => `${$mapClass.qualifiedName}.get(${param},${defaultMethodStr})`, }; } //# sourceMappingURL=Specializer.js.map