@specs-feup/kadabra
Version:
A Java source-to-source compiler written in Typescript
225 lines (203 loc) • 6.81 kB
text/typescript
import Query from "@specs-feup/lara/api/weaver/Query.js";
import { Class, Field, FileJp, Method } from "../../Joinpoints.js";
import { getOrNewClass } from "../Factory.js";
export function CreateClassGenerator(
adapterMethod: string,
$interfaceMethod: Method,
adapterClass: string = ".*",
$storingClass?: Class
) {
let $adaptMethod: Method | undefined;
let generate: ((...args: string[]) => string) | undefined;
let generateQualified: ((...args: string[]) => string) | undefined;
for (const $method of Query.search(FileJp)
.search(Class, (c: Class) => RegExp(adapterClass).exec(c.name) !== null)
.search(Method, adapterMethod)) {
if ($adaptMethod != undefined) {
throw new Error(
"More than one target method generator was found, please define a finer selection"
);
}
const adapter = FunctionGenerator(
$method,
$interfaceMethod,
$storingClass
);
$adaptMethod = adapter.$adaptMethod;
generate = adapter.generate;
generateQualified = adapter.generateQualified;
}
if ($adaptMethod == undefined) {
throw new Error(
"Could not find given method generator: " +
adapterMethod +
" in " +
adapterClass
);
}
return {
$adaptMethod: $adaptMethod,
generate: generate,
generateQualified: generateQualified,
};
}
export function FunctionGenerator(
$adapterMethod: Method,
$interfaceMethod: Method,
$storingClass: Class = getOrNewClass("kadabra.utils.Adapters")
): {
$adaptMethod: Method;
generate: (...args: string[]) => string;
generateQualified: (...args: string[]) => string;
} {
console.log(
"[FunctionGenerator] Creating new functional class with " +
$interfaceMethod +
" and " +
$adapterMethod
);
const $adaptMethod = $storingClass.newFunctionalClass(
$interfaceMethod,
$adapterMethod
);
const generate = function (...args: string[]) {
let invoke = $adaptMethod + "(";
const _args = args.slice();
invoke += _args.join(", ");
invoke += ")";
return invoke;
};
const generateQualified = function (...args: string[]) {
let invoke = $storingClass.qualifiedName + "." + $adaptMethod + "(";
const _args = args.slice();
invoke += _args.join(", ");
invoke += ")";
return invoke;
};
return {
$adaptMethod: $adapterMethod,
generate: generate,
generateQualified: generateQualified,
};
}
/**
*
*/
export function CreateAdapter(
target: string,
adapter: string,
name: string,
targetClass: string = ".*",
adapterClass: string = ".*"
) {
let $adaptClass: Class | undefined;
let addField;
const $methods = Query.search(FileJp)
.search(Class, (c: Class) => RegExp(targetClass).exec(c.name) !== null)
.search(Method, { name: target });
for (const $adaptMethod of Query.search(FileJp)
.search(Class, (c: Class) => RegExp(adapterClass).exec(c.name) != null)
.search(Method, { name: adapter })) {
for (const $method of $methods) {
if ($method.equals($adaptMethod)) {
if ($adaptClass != undefined) {
throw new Error(
"More than one target class/adapter method was found, please define a finer selection"
);
}
name ??=
$method.name.charAt(0).toUpperCase() +
$method.name.substring(1) +
"_" +
adapter +
"_" +
$adaptMethod.name.charAt(0).toUpperCase() +
$adaptMethod.name.substring(1) +
"_Adapter";
const _adapter = TransformMethod($method, $adaptMethod, name); //, $target.name.firstCharToUpper()+"Adapter");
$adaptClass = _adapter.$adaptClass;
addField = _adapter.addField;
}
}
}
return { $adaptClass: $adaptClass, addField: addField };
}
/**
* Create an adapter based on the target class and the method that transforms the class bytecodes.
*
*/
export function TransformMethod(
$target: Method,
$adaptMethod: Method,
name: string = defaultTransformMethodName($target, $adaptMethod)
): {
$adaptClass: Class;
addField: (
$class?: Class,
name?: string,
init?: boolean
) => {
name: string;
$field: Field;
addAdapter: string;
adapt: (...args: string[]) => string;
};
} {
let $adaptClass: Class | undefined;
for (const $class of Query.search(FileJp).search(Class, { name: name })) {
$adaptClass = $class;
}
if ($adaptClass === undefined) {
try {
$adaptClass = $target.createAdapter($adaptMethod, name);
} catch (e) {
if (e instanceof Error) console.log(e.message);
}
}
if ($adaptClass === undefined) {
throw new Error("[TransformMethod] couldn't create $adaptClass");
}
//this method returns information regarding the field and class, as well as the methods that can be invoked in the field
const addField = (
$class: Class = $adaptClass,
fieldName: string = $adaptClass.name.charAt(0).toLowerCase() +
$adaptClass.name.substring(1),
init: boolean = false
) => {
const $newField = $class.newField(
["public", "static"],
$adaptClass.qualifiedName,
fieldName,
"new " + $adaptClass.name + "()"
);
const field = {
name: fieldName,
$field: $newField,
addAdapter:
"weaver.kadabra.agent.AgentUtils.addAdapter(" +
fieldName +
");",
adapt: (...args: string[]) => {
let invoke = $newField.staticAccess + ".adapt(";
const _args = args.slice();
invoke += _args.join(", ");
invoke += ");";
return invoke;
},
};
if (init) {
$class.insertStatic(`${field.addAdapter}`);
}
return field;
};
return { $adaptClass: $adaptClass, addField: addField };
}
function defaultTransformMethodName($target: Method, $adaptMethod: Method) {
return (
$target.name.charAt(0).toUpperCase() +
$target.name.substring(1) +
$adaptMethod.name.charAt(0).toUpperCase +
$adaptMethod.name.substring(1) +
"Adapter"
);
}