UNPKG

@abaplint/transpiler

Version:
152 lines • 7.79 kB
"use strict"; 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 += new transpile_types_1.TranspileTypes().declareStaticSkipVoid(prefix, ty.type); } 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 + " = " + new 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"; } // 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; } // note: for CALL TRANSFORMATION, its nice that the values are initialized by the JS constructor, return "constructor() {\n" + ret + "}\n"; } } exports.ClassImplementationTranspiler = ClassImplementationTranspiler; //# sourceMappingURL=class_implementation.js.map