@abaplint/transpiler
Version:
177 lines • 8.95 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClassImplementationTranspiler = void 0;
const abaplint = require("@abaplint/core");
const traversal_1 = require("../traversal");
const transpile_types_1 = require("../transpile_types");
const chunk_1 = require("../chunk");
class ClassImplementationTranspiler {
transpile(node, traversal) {
const ret = new chunk_1.Chunk();
for (const c of node.getChildren()) {
ret.appendChunk(traversal.traverse(c));
if (c.get() instanceof abaplint.Statements.ClassImplementation) {
ret.appendString(this.buildConstructor(node.getFirstStatement(), traversal));
}
if (c instanceof abaplint.Nodes.StatementNode
&& c.get() instanceof abaplint.Statements.ClassImplementation
&& this.hasConstructor(node) === false) {
ret.appendString("async constructor_(INPUT) {\nif (super.constructor_) { await super.constructor_(INPUT); }\nreturn this;\n}\n");
}
}
ret.appendString(this.buildStatic(node.findFirstExpression(abaplint.Expressions.ClassName), traversal));
ret.appendString(this.buildTypes(node.findFirstExpression(abaplint.Expressions.ClassName), traversal));
return ret;
}
///////////////////////////////
hasConstructor(node) {
for (const m of node.findAllStatements(abaplint.Statements.MethodImplementation)) {
const name = m.findFirstExpression(abaplint.Expressions.MethodName)?.getFirstToken().getStr();
if (name?.toUpperCase() === "CONSTRUCTOR") {
return true;
}
}
return false;
}
/** Finds static attributes + constants including those from interfaces (from superclass is ingored) */
findStaticAttributes(cdef, scope, traversal) {
const ret = [];
ret.push(...cdef.getAttributes().getStatic().map(a => { return { identifier: a, prefix: "" }; }));
ret.push(...cdef.getAttributes().getConstants().map(a => { return { identifier: a, prefix: "" }; }));
const implementing = [...cdef.getImplementing()];
while (implementing.length > 0) {
const i = implementing.shift();
if (i === undefined) {
break;
}
const intf = traversal.findInterfaceDefinition(i.name, scope);
if (intf === undefined) {
continue;
}
implementing.push(...intf.getImplementing());
ret.push(...intf.getAttributes().getStatic().map(a => {
return { identifier: a, prefix: intf.getName().toLowerCase() + "$" };
}));
ret.push(...intf.getAttributes().getConstants().map(a => {
return { identifier: a, prefix: intf.getName().toLowerCase() + "$" };
}));
}
return ret;
}
buildTypes(node, traversal) {
if (node === undefined) {
return "";
}
const cdef = traversal.getClassDefinition(node.getFirstToken());
if (cdef === undefined) {
return "ERROR_CDEF_NOT_FOUND";
}
const prefix = traversal_1.Traversal.escapeNamespace(cdef.getName().toLowerCase()) + ".";
let ret = "";
for (const ty of cdef.getTypeDefinitions().getAll()) {
ret += transpile_types_1.TranspileTypes.declareStaticSkipVoid(prefix, ty.type);
}
return ret;
}
buildPrivate(node, traversal) {
if (node === undefined) {
return "";
}
const cdef = traversal.getClassDefinition(node.getFirstToken());
if (cdef === undefined) {
return "ERROR_CDEF_NOT_FOUND";
}
let ret = "";
for (const attr of cdef.getAttributes().getAll()) {
if (attr.getVisibility() === abaplint.Visibility.Private) {
ret += "#" + traversal_1.Traversal.escapeNamespace(attr.getName().toLowerCase()) + ";\n";
}
}
return ret;
}
/** this builds the part after the class, containing the static variables/constants */
buildStatic(node, traversal) {
if (node === undefined) {
return "";
}
const cdef = traversal.getClassDefinition(node.getFirstToken());
if (cdef === undefined) {
return "ERROR_CDEF_NOT_FOUND";
}
const scope = traversal.findCurrentScopeByToken(node.getFirstToken());
if (scope === undefined) {
return "ERROR_SCOPE_NOT_FOUND";
}
let ret = "";
const clasName = node.getFirstToken().getStr().toLowerCase();
const staticAttributes = this.findStaticAttributes(cdef, scope, traversal);
for (const attr of staticAttributes) {
const name = traversal_1.Traversal.escapeNamespace(clasName) + "." + traversal_1.Traversal.escapeNamespace(attr.prefix) + traversal_1.Traversal.escapeNamespace(attr.identifier.getName().toLowerCase());
ret += name + " = " + transpile_types_1.TranspileTypes.toType(attr.identifier.getType()) + ";\n";
ret += traversal.setValues(attr.identifier, name);
}
for (const alias of cdef.getAliases()) {
const isStatic = staticAttributes.some(s => s.prefix.replace("$", "~") + s.identifier.getName() === alias.getComponent());
if (isStatic === false) {
continue;
}
ret += traversal_1.Traversal.escapeNamespace(clasName) + "." + alias.getName().toLowerCase() + " = " + traversal_1.Traversal.escapeNamespace(clasName) + "." + traversal_1.Traversal.escapeNamespace(alias.getComponent().replace("~", "$")) + ";\n";
}
for (const e of cdef.getEvents()) {
if (e.isStatic() === false) {
continue;
}
const fname = traversal_1.Traversal.escapeNamespace(e.getName().toLowerCase());
const name = traversal.buildInternalName(clasName, cdef);
ret += traversal_1.Traversal.escapeNamespace(clasName) + "." + fname + " = {\"EVENT_NAME\": \"" + e.getName().toUpperCase() + "\", \"EVENT_CLASS\": \"" + name + "\"};\n";
}
// this is not correct, ABAP does not invocate the class constructor at require time,
// but this will probably work
if (traversal.getCurrentObject().getType() === "CLAS") {
// gather call of all class constructors together for a global class, also local classes
// as they might use the global class, except for local testclass constructors
if (cdef.isGlobal() === true) {
for (const f of traversal.getCurrentObject().getABAPFiles()) {
for (const def of f.getInfo().listClassDefinitions()) {
if (def.isForTesting === true) {
continue;
}
else if (def.methods.some(m => m.name.toLowerCase() === "class_constructor")) {
ret += "await " + traversal.lookupClassOrInterface(def.name, node.getFirstToken()) + ".class_constructor();\n";
}
}
}
}
if (cdef.isGlobal() === false && cdef.isForTesting() === true && cdef.getMethodDefinitions().getByName("class_constructor")) {
ret += "await " + traversal_1.Traversal.escapeNamespace(node.getFirstToken().getStr().toLowerCase()) + ".class_constructor();\n";
}
}
else if (cdef.getMethodDefinitions().getByName("class_constructor")) {
ret += "await " + traversal_1.Traversal.escapeNamespace(node.getFirstToken().getStr().toLowerCase()) + ".class_constructor();\n";
}
return ret;
}
buildConstructor(node, traversal) {
if (node === undefined) {
throw new Error("buildConstructor node undefined");
}
const scope = traversal.findCurrentScopeByToken(node.getFirstToken());
const token = node.findFirstExpression(abaplint.Expressions.ClassName)?.getFirstToken();
if (token === undefined) {
throw "buildConstructorTokenNotFound";
}
const cdef = traversal.getClassDefinition(token);
if (cdef === undefined) {
throw "buildConstructorCDEFNotFound, " + token.getStr();
}
const ret = traversal.buildConstructorContents(scope, cdef);
if (ret === "") {
return ret;
}
const privates = this.buildPrivate(node.findFirstExpression(abaplint.Expressions.ClassName), traversal);
// note: for CALL TRANSFORMATION, its nice that the values are initialized by the JS constructor,
return privates + "constructor() {\n" + ret + "}\n";
}
}
exports.ClassImplementationTranspiler = ClassImplementationTranspiler;
//# sourceMappingURL=class_implementation.js.map