UNPKG

@abaplint/transpiler

Version:
237 lines • 12.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MethodImplementationTranspiler = void 0; /* eslint-disable max-len */ const abaplint = require("@abaplint/core"); const transpile_types_1 = require("../transpile_types"); const traversal_1 = require("../traversal"); const expressions_1 = require("../expressions"); const chunk_1 = require("../chunk"); const unique_identifier_1 = require("../unique_identifier"); const feature_flags_1 = require("../feature_flags"); class MethodImplementationTranspiler { transpile(node, traversal) { const token = node.findFirstExpression(abaplint.Expressions.MethodName).getFirstToken(); let methodName = token.getStr(); const scope = traversal.findCurrentScopeByToken(token); if (scope === undefined) { throw new Error("MethodTranspiler, scope not found, " + methodName + ", " + traversal.getFilename()); } else if (scope.getIdentifier().sname !== methodName) { throw new Error("MethodTranspiler, wrong scope found, " + scope.getIdentifier().sname + ", " + methodName + ", " + scope.getIdentifier().filename); } let after = ""; const classDef = traversal.getClassDefinition(token); let unique = ""; if (methodName.toUpperCase() === "CONSTRUCTOR" && classDef) { // note that all ABAP identifiers are lower cased, sometimes the kernel does magic, so it needs to know the method input name unique = "INPUT"; // after = traversal.buildConstructorContents(scope.getParent(), classDef); methodName = "constructor_"; } const methodDef = this.findMethodParameters(scope); const vars = scope.getData().vars; for (const n in vars) { const identifier = vars[n]; const varName = n.toLowerCase(); const varPrefixed = traversal_1.Traversal.prefixVariable(varName); if (identifier.getMeta().includes("importing" /* abaplint.IdentifierMeta.MethodImporting */) || identifier.getMeta().includes("changing" /* abaplint.IdentifierMeta.MethodChanging */) || identifier.getMeta().includes("event_parameter" /* abaplint.IdentifierMeta.EventParameter */) || identifier.getMeta().includes("exporting" /* abaplint.IdentifierMeta.MethodExporting */)) { if (unique === "") { unique = "INPUT"; } const parameterDefault = methodDef?.getParameterDefault(varName); const isOptional = methodDef?.getOptional().includes(varName.toUpperCase()); const passByValue = identifier.getMeta().includes("pass_by_value" /* abaplint.IdentifierMeta.PassByValue */); const type = identifier.getType(); if (identifier.getMeta().includes("exporting" /* abaplint.IdentifierMeta.MethodExporting */)) { after += `let ${varPrefixed} = ${unique}?.${varName} || ${new transpile_types_1.TranspileTypes().toType(identifier.getType())};\n`; } else if (identifier.getMeta().includes("importing" /* abaplint.IdentifierMeta.MethodImporting */) && parameterDefault === undefined && passByValue === false && isOptional === false && type.isGeneric() === false) { after += `let ${varPrefixed} = ${unique}?.${varName};\n`; if (identifier.getType().getQualifiedName() !== undefined && identifier.getType().getQualifiedName() !== "") { after += `if (${varPrefixed}?.getQualifiedName === undefined || ${varPrefixed}.getQualifiedName() !== "${identifier.getType().getQualifiedName()?.toUpperCase()}") { ${varPrefixed} = undefined; }\n`; } after += `if (${varPrefixed} === undefined) { ${varPrefixed} = ${new transpile_types_1.TranspileTypes().toType(identifier.getType())}.set(${unique}.${varName}); }\n`; } else if (identifier.getMeta().includes("importing" /* abaplint.IdentifierMeta.MethodImporting */) && type.isGeneric() === true) { if (isOptional === true) { after += `let ${varPrefixed} = ${unique}?.${varName} || ${new transpile_types_1.TranspileTypes().toType(identifier.getType())};\n`; } else { after += `let ${varPrefixed} = ${unique}?.${varName};\n`; } if (type instanceof abaplint.BasicTypes.NumericGenericType) { after += `if (${varPrefixed}.constructor.name === "Character") { ${varPrefixed} = ${new transpile_types_1.TranspileTypes().toType(identifier.getType())}; ${varPrefixed}.set(${unique}?.${varName}); }\n`; } } else if (identifier.getMeta().includes("importing" /* abaplint.IdentifierMeta.MethodImporting */) && type.isGeneric() === false) { after += new transpile_types_1.TranspileTypes().declare(identifier) + "\n"; // note: it might be nessesary to do a type conversion, eg char is passed to xstring parameter after += "if (" + unique + " && " + unique + "." + varName + ") {" + varPrefixed + ".set(" + unique + "." + varName + ");}\n"; } else { after += new transpile_types_1.TranspileTypes().declare(identifier) + "\n"; after += "if (" + unique + " && " + unique + "." + varName + ") {" + varPrefixed + " = " + unique + "." + varName + ";}\n"; } if (parameterDefault) { const val = this.buildDefault(parameterDefault, traversal, methodDef?.getFilename()); if (passByValue === true || identifier.getMeta().includes("changing" /* abaplint.IdentifierMeta.MethodChanging */)) { after += "if (" + unique + " === undefined || " + unique + "." + varName + " === undefined) {" + varPrefixed + ".set(" + val + ");}\n"; } else { after += "if (" + unique + " === undefined || " + unique + "." + varName + " === undefined) {" + varPrefixed + " = " + val + ";}\n"; } } } else if (identifier.getMeta().includes("returning" /* abaplint.IdentifierMeta.MethodReturning */)) { after = after + new transpile_types_1.TranspileTypes().declare(identifier) + "\n"; } } if (after.length > 0) { // argh after = "\n" + after; after = after.substring(0, after.length - 1); } const method = this.findMethod(methodName, classDef, traversal); let staticMethod = ""; methodName = methodName.replace("~", "$").toLowerCase(); if (methodName === "then") { // todo, this should be a checked in the validation step, but no abaplint rule for it? // it messes up promises when "this" is returned throw new Error(`Method name "then" not allowed`); } const superDef = traversal.findClassDefinition(classDef?.getSuperClass(), scope); for (const a of superDef?.getAliases() || []) { if (a.getName().toLowerCase() === methodName) { methodName = a.getComponent().replace("~", "$").toLowerCase(); } } // https://github.com/tc39/proposal-class-fields let isPrivate = ""; if (feature_flags_1.FEATURE_FLAGS.private === true && method?.getVisibility() === abaplint.Visibility.Private && method.isStatic() === false) { isPrivate = "#"; } if (method && method.isStatic()) { // in ABAP static methods can be called with instance arrows, "->" const className = scope.getParent()?.getIdentifier().sname?.toLowerCase(); staticMethod = "async " + isPrivate + traversal_1.Traversal.escapeNamespace(methodName) + "(" + unique + ") {\n" + "return " + traversal_1.Traversal.escapeNamespace(className) + "." + traversal_1.Traversal.escapeNamespace(methodName) + "(" + unique + ");\n" + "}\n" + "static "; } unique_identifier_1.UniqueIdentifier.resetIndexBackup(); const str = staticMethod + "async " + isPrivate + traversal_1.Traversal.escapeNamespace(methodName) + "(" + unique + ") {" + after; return new chunk_1.Chunk().append(str, node, traversal); } ///////////////////////////// buildDefault(parameterDefault, traversal, filename) { let val = ""; if (parameterDefault.get() instanceof abaplint.Expressions.Constant) { val = new expressions_1.ConstantTranspiler().transpile(parameterDefault, traversal).getCode(); } else if (parameterDefault.get() instanceof abaplint.Expressions.FieldChain) { val = this.buildDefaultFallback(parameterDefault, traversal, filename); } else { throw new Error("MethodImplementationTranspiler, unknown default param type"); } return val; } buildDefaultFallback(parameterDefault, traversal, filename) { let val = ""; const firstTokenLower = parameterDefault.getFirstToken().getStr().toLowerCase(); const concat = parameterDefault.concatTokens().toLowerCase(); if (firstTokenLower === "abap_true") { val = "abap.builtin.abap_true"; } else if (firstTokenLower === "abap_false") { val = "abap.builtin.abap_false"; } else if (firstTokenLower === "abap_undefined") { val = "abap.builtin.abap_undefined"; } else if (firstTokenLower === "space") { val = "abap.builtin.space"; } else if (concat === "sy-langu") { val = "abap.builtin.sy.get().langu"; } else if (concat === "sy-mandt") { val = "abap.builtin.sy.get().mandt"; } else if (concat === "sy-uname") { val = "abap.builtin.sy.get().uname"; } else if (concat === "sy-sysid") { val = "abap.builtin.sy.get().sysid"; } else if (concat === "sy-msgid") { val = "abap.builtin.sy.get().msgid"; } else if (concat === "sy-msgty") { val = "abap.builtin.sy.get().msgty"; } else if (concat === "sy-msgno") { val = "abap.builtin.sy.get().msgno"; } else if (concat === "sy-msgv1") { val = "abap.builtin.sy.get().msgv1"; } else if (concat === "sy-msgv2") { val = "abap.builtin.sy.get().msgv2"; } else if (concat === "sy-msgv3") { val = "abap.builtin.sy.get().msgv3"; } else if (concat === "sy-msgv4") { val = "abap.builtin.sy.get().msgv4"; } else { // note: this can be difficult, the "def" might be from an interface, ie. a different scope than the method val = new expressions_1.FieldChainTranspiler().transpile(parameterDefault, traversal, true, filename, true).getCode(); if (val.startsWith(parameterDefault.getFirstToken().getStr().toLowerCase()) === true) { val = "this." + val; } } return val; } findMethod(name, cdef, traversal) { if (cdef === undefined) { return undefined; } if (name.includes("~")) { const split = name.split("~"); const intfName = split[0]; name = split[1]; const scope = traversal.findCurrentScopeByToken(cdef.getToken()); const intf = traversal.findInterfaceDefinition(intfName, scope); return intf?.getMethodDefinitions()?.getByName(name); } else { return cdef.getMethodDefinitions()?.getByName(name); } } findMethodParameters(scope) { for (const r of scope.getData().references) { if (r.referenceType === abaplint.ReferenceType.MethodImplementationReference && r.resolved instanceof abaplint.Types.MethodDefinition) { return r.resolved.getParameters(); } } return undefined; } } exports.MethodImplementationTranspiler = MethodImplementationTranspiler; //# sourceMappingURL=method_implementation.js.map