@specs-feup/lara
Version:
A js port of the popular framework for building source-to-source compilers
240 lines (198 loc) • 6.52 kB
JavaScript
import fs from "fs";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { convertSpecification } from "./convert-joinpoint-specification.js";
import { generateJoinpoints, generateEnums } from "./generate-ts-joinpoints.js";
function buildLaraJoinPoint(inputFileName, outputFileName) {
console.log("Hello from build-LaraJoinPoint.js");
console.log("inputFile:", inputFileName);
console.log("outputFile:", outputFileName);
const jsonSpecification = fs.readFileSync(inputFileName, "utf8");
const specification = convertSpecification(JSON.parse(jsonSpecification));
// Create output file if it doesn't exist
const outputFile = fs.openSync(outputFileName, "w");
fs.writeSync(
outputFile,
`//////////////////////////////////////////////////////
// This file is generated by build-LaraJoinPoint.js //
//////////////////////////////////////////////////////
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-duplicate-type-constituents */
import java from "java";
import JavaTypes, { Engine, engine, NodeJavaPrefix } from "./lara/util/JavaTypes.js";
/**
* Type for type equality assertion. If T is equal to U, return Y, otherwise return N.
* Source: https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650
* @example
* type A = Equals<string, string, "Y", "N">; // "Y"
* type B = Equals<string, number, "Y", "N">; // "N"
*/
type Equals<T, U, Y = unknown, N = never> =
(<G>() => G extends T ? 1 : 2) extends
(<G>() => G extends U ? 1 : 2) ? Y : N;
type DefaultAttributeHelper<
T extends typeof LaraJoinPoint,
DefaultAttributeMap,
PrivateMapper,
> = {
[K in keyof DefaultAttributeMap]: K extends keyof PrivateMapper
? Equals<T, PrivateMapper[K], DefaultAttributeMap[K], never>
: never;
}[keyof DefaultAttributeMap];
// Extract the type A from A | undefined
type ExtractedType<T> = T extends undefined ? never : T;
export type DefaultAttribute<T extends typeof LaraJoinPoint> = DefaultAttributeHelper<
T,
ExtractedType<T["_defaultAttributeInfo"]["map"]>,
ExtractedType<T["_defaultAttributeInfo"]["type"]>
>;
type NameFromWrapperClassHelper<T extends typeof LaraJoinPoint, U> = {
[K in keyof U]: Equals<T, U[K], K, never>;
}[keyof U];
export type NameFromWrapperClass<T extends typeof LaraJoinPoint> = NameFromWrapperClassHelper<
T,
ExtractedType<T["_defaultAttributeInfo"]["jpMapper"]>
>;\n\n`
);
generateJoinpoints(specification.joinpoints, outputFile);
generateEnums(specification.enums, outputFile);
generateJoinpointWrapper(specification.joinpoints, outputFile);
fs.closeSync(outputFile);
}
function generateJoinpointWrapper(joinpoints, outputFile) {
fs.writeSync(
outputFile,
`\nexport type JoinpointMapperType = { [key: string]: typeof LaraJoinPoint };\n
const JoinpointMappers: JoinpointMapperType[] = [];\n`
);
fs.writeSync(
outputFile,
`\nexport function registerJoinpointMapper(mapper: JoinpointMapperType): void {
JoinpointMappers.push(mapper);
}\n`
);
fs.writeSync(
outputFile,
`\n/**
* This function is for internal use only. DO NOT USE IT!
*/
export function clearJoinpointMappers(): void {
JoinpointMappers.length = 0;
}
/**
* This function is for internal use only. DO NOT USE IT!
*/
export function getJoinpointMappers(): JoinpointMapperType[] {
return JoinpointMappers;
}\n`
);
fs.writeSync(
outputFile,
`\nexport function wrapJoinPoint(obj: any): any {
if (JoinpointMappers.length === 0) {
return obj;
}
if (obj === undefined) {
return obj;
}
if (obj instanceof LaraJoinPoint) {
return obj;
}
if (ArrayBuffer.isView(obj)) {
return Array.from(obj as any).map(wrapJoinPoint);
}
if (typeof obj !== "object") {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(wrapJoinPoint);
}
if (!JavaTypes.isJavaObject(obj)) {
return obj;
}
if (
JavaTypes.instanceOf(obj, "pt.up.fe.specs.jsengine.node.UndefinedValue")
) {
return undefined;
}
if (
JavaTypes.instanceOf(obj, "org.suikasoft.jOptions.DataStore.DataClass") &&
!JavaTypes.instanceOf(obj, "pt.up.fe.specs.clava.ClavaNode")
) {
return obj;
}
if (obj.getClass().isEnum()) {
return obj.toString();
}
const isJavaJoinPoint = JavaTypes.JoinPoint.isJoinPoint(obj);
if (!isJavaJoinPoint) {
throw new Error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
\`Given Java join point is a Java class but is not a JoinPoint: \${obj.getClass()}\`
);
}
const jpType: string = obj.getJoinPointType();
for (const mapper of JoinpointMappers) {
if (mapper[jpType]) {
return new mapper[jpType](obj);
}
}
throw new Error("No mapper found for join point type: " + jpType);
}\n`
);
fs.writeSync(
outputFile,
`\nexport function unwrapJoinPoint(obj: any): any {
if (obj instanceof LaraJoinPoint) {
return obj._javaObject;
}
if (Array.isArray(obj)) {
if (engine == Engine.NodeJS) {
const isJpArray = obj.reduce((prev, curr) => {
return prev && curr instanceof LaraJoinPoint;
}, true);
const getClassName = (jp: LaraJoinPoint) =>
Object.getPrototypeOf(jp._javaObject).constructor.name;
if (isJpArray) {
const clazz = (
obj.map(getClassName).reduce((prev, curr) => {
if (prev != curr) {
return undefined;
}
return prev;
}) ?? "java.lang.Object"
)
.replace(NodeJavaPrefix, "")
.replaceAll("_", ".");
return java.newArray(clazz, obj.map(unwrapJoinPoint));
}
}
return obj.map(unwrapJoinPoint);
}
return obj;
}\n`
);
}
const args = yargs(hideBin(process.argv))
.scriptName("build-LaraJoinPoint")
.option("i", {
alias: "input",
describe: "Path to JSON config file",
type: "string",
})
.option("o", {
alias: "output",
describe: "Path to the output file",
type: "string",
})
.help()
.showHelpOnFail(true)
.strict()
.parse();
buildLaraJoinPoint(args.input, args.output);