@abaplint/transpiler-cli 2.14.11 → 2.14.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/build/bundle.js +274 -33
  2. package/package.json +3 -3
package/build/bundle.js CHANGED
@@ -29854,6 +29854,7 @@ const expressions_1 = __webpack_require__(/*! ../2_statements/expressions */ "./
29854
29854
  const _builtin_1 = __webpack_require__(/*! ./_builtin */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_builtin.js");
29855
29855
  const position_1 = __webpack_require__(/*! ../../position */ "./node_modules/@abaplint/core/build/src/position.js");
29856
29856
  const _syntax_input_1 = __webpack_require__(/*! ./_syntax_input */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_syntax_input.js");
29857
+ const version_1 = __webpack_require__(/*! ../../version */ "./node_modules/@abaplint/core/build/src/version.js");
29857
29858
  class BasicTypes {
29858
29859
  constructor(input) {
29859
29860
  this.input = input;
@@ -30079,7 +30080,14 @@ class BasicTypes {
30079
30080
  }
30080
30081
  const chainText = typeName.concatTokens().toUpperCase();
30081
30082
  const f = this.input.scope.getDDIC().lookupBuiltinType(chainText, length, decimals, qualifiedName);
30082
- if (f !== undefined) {
30083
+ if (f instanceof Types.Integer8Type
30084
+ && !(0, version_1.releaseAtLeast)(this.input.scope.getRelease(), version_1.Release.v750)
30085
+ && !this.input.scope.getOpenABAP()) {
30086
+ const message = "Type INT8 requires 7.50";
30087
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, typeName.getFirstToken(), message));
30088
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
30089
+ }
30090
+ else if (f !== undefined) {
30083
30091
  return f;
30084
30092
  }
30085
30093
  const typ = this.input.scope.findType(chainText);
@@ -44563,6 +44571,10 @@ class InsertInternal {
44563
44571
  if (sourceType instanceof basic_1.IntegerType && targetType instanceof basic_1.Integer8Type) {
44564
44572
  error = true;
44565
44573
  }
44574
+ else if (targetType instanceof basic_1.TableType && targetType.isWithHeader() && sourceType instanceof basic_1.TableType) {
44575
+ // LINES OF into a table with header line, compare the row types
44576
+ error = new _type_utils_1.TypeUtils(input.scope).isAssignable(sourceType.getRowType(), targetType.getRowType()) === false;
44577
+ }
44566
44578
  else if (new _type_utils_1.TypeUtils(input.scope).isAssignable(sourceType, targetType) === false) {
44567
44579
  error = true;
44568
44580
  }
@@ -73714,7 +73726,7 @@ class Registry {
73714
73726
  }
73715
73727
  static abaplintVersion() {
73716
73728
  // magic, see build script "version.js"
73717
- return "2.120.86";
73729
+ return "2.120.87";
73718
73730
  }
73719
73731
  getDDICReferences() {
73720
73732
  return this.ddicReferences;
@@ -77371,6 +77383,7 @@ const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/ex
77371
77383
  const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
77372
77384
  const Structures = __importStar(__webpack_require__(/*! ../abap/3_structures/structures */ "./node_modules/@abaplint/core/build/src/abap/3_structures/structures/index.js"));
77373
77385
  const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
77386
+ const tokens_1 = __webpack_require__(/*! ../abap/1_lexer/tokens */ "./node_modules/@abaplint/core/build/src/abap/1_lexer/tokens/index.js");
77374
77387
  class AlignTypeExpressionsConf extends _basic_rule_config_1.BasicRuleConfig {
77375
77388
  constructor() {
77376
77389
  super(...arguments);
@@ -77538,12 +77551,18 @@ ENDINTERFACE.`,
77538
77551
  let column = 0;
77539
77552
  const st = t.findDirectStatements(Statements.Type);
77540
77553
  for (const s of st) {
77541
- const name = s.getChildren()[1];
77554
+ const children = s.getChildren();
77555
+ const length = s.findDirectExpression(Expressions.ConstantFieldLength);
77556
+ const nameEnd = (length !== null && length !== void 0 ? length : children[1]).getLastToken().getEnd();
77557
+ const after = children[length ? 3 : 2];
77558
+ if (after === undefined || after.getFirstToken() instanceof tokens_1.Punctuation) {
77559
+ continue; // no TYPE clause, eg. "foo(1)," with implicit type
77560
+ }
77542
77561
  fields.push({
77543
- nameEnd: name.getLastToken().getEnd(),
77544
- after: s.getChildren()[2].getFirstToken().getStart()
77562
+ nameEnd: nameEnd,
77563
+ after: after.getFirstToken().getStart()
77545
77564
  });
77546
- column = Math.max(column, name.getFirstToken().getEnd().getCol() + 1);
77565
+ column = Math.max(column, nameEnd.getCol() + 1);
77547
77566
  }
77548
77567
  issues.push(...this.check(fields, column, file));
77549
77568
  }
@@ -85489,6 +85508,17 @@ Make sure to test the downported code, it might not always be completely correct
85489
85508
  }
85490
85509
  }
85491
85510
  }
85511
+ // INSERT with a 702 built-in function as the operand parses in the low version, but 702
85512
+ // reads the function as a method call, "Method SUBSTRING does not exist"
85513
+ if (ret.length === 0) {
85514
+ for (let i = 0; i < lowStatements.length; i++) {
85515
+ const issue = this.outlineInsertBuiltin(lowStatements[i], highStatements[i], lowFile, highSyntax);
85516
+ if (issue) {
85517
+ ret.push(issue);
85518
+ break;
85519
+ }
85520
+ }
85521
+ }
85492
85522
  // INTO after WHERE parses as a valid SELECT in the low version, but is only allowed in
85493
85523
  // strict SQL syntax, so move it also when the statement is no longer Unknown
85494
85524
  if (ret.length === 0) {
@@ -86321,6 +86351,40 @@ ${indentation}`);
86321
86351
  }
86322
86352
  return undefined;
86323
86353
  }
86354
+ /** INSERT substring( ... ) INTO TABLE tab, the operand is outlined into a variable */
86355
+ outlineInsertBuiltin(low, high, lowFile, highSyntax) {
86356
+ var _a;
86357
+ if (!(high.get() instanceof Statements.InsertInternal)) {
86358
+ return undefined;
86359
+ }
86360
+ else if (this.lowReg.getConfig().isOpenABAP()) {
86361
+ return undefined;
86362
+ }
86363
+ else if (low.getFirstToken().getStart() instanceof virtual_position_1.VirtualPosition) {
86364
+ return undefined;
86365
+ }
86366
+ // the whole operand is the call, a longer expression is outlined by replaceInsertExpression
86367
+ const source = high.getChildren()[1];
86368
+ if (!(source instanceof nodes_1.ExpressionNode)
86369
+ || !(source.get() instanceof Expressions.SimpleSource4 || source.get() instanceof Expressions.Source)
86370
+ || source.getChildren().length !== 1
86371
+ || !(((_a = source.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.MethodCallChain)) {
86372
+ return undefined;
86373
+ }
86374
+ const type = this.builtinReturnType(source, lowFile, highSyntax);
86375
+ if (type === undefined) {
86376
+ return undefined;
86377
+ }
86378
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
86379
+ const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
86380
+ const code = `DATA ${uniqueName} TYPE ${type}.\n` +
86381
+ indentation + `${uniqueName} = ${source.concatTokens()}.\n` +
86382
+ indentation;
86383
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getFirstToken().getStart(), code);
86384
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, source.getFirstToken().getStart(), source.getLastToken().getEnd(), uniqueName);
86385
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
86386
+ return issue_1.Issue.atToken(lowFile, source.getFirstToken(), "Outline built-in function in INSERT", this.getMetadata().key, this.conf.severity, fix);
86387
+ }
86324
86388
  /** the ABAP type returned by the built-in function this Source starts with, or undefined
86325
86389
  * if it does not start with one, or with one that predates 702 */
86326
86390
  builtinReturnType(source, lowFile, highSyntax) {
@@ -115623,6 +115687,7 @@ exports.PopulateTables = PopulateTables;
115623
115687
 
115624
115688
  Object.defineProperty(exports, "__esModule", ({ value: true }));
115625
115689
  exports.DatabaseSchemaReuse = void 0;
115690
+ const OPERATORS = { EQ: "=", NE: "<>", LT: "<", LE: "<=", GT: ">", GE: ">=" };
115626
115691
  class DatabaseSchemaReuse {
115627
115692
  myQuote;
115628
115693
  constructor(quote) {
@@ -115632,31 +115697,68 @@ class DatabaseSchemaReuse {
115632
115697
  return this.myQuote + name.toLowerCase() + this.myQuote;
115633
115698
  }
115634
115699
  buildVIEW(view) {
115635
- const fields = view.getFields();
115636
- let firstTabname = "";
115637
- const columns = fields?.map((f) => {
115638
- firstTabname = this.quote(f.TABNAME.toLowerCase());
115639
- return firstTabname + "." + f.FIELDNAME.toLowerCase() + " AS " + f.VIEWFIELD.toLowerCase();
115700
+ const fields = view.getFields() || [];
115701
+ const columns = fields.map((f) => {
115702
+ return this.quote(f.TABNAME) + "." + f.FIELDNAME.toLowerCase() + " AS " + f.VIEWFIELD.toLowerCase();
115640
115703
  }).join(", ");
115641
- let from = "";
115642
- let previous = "";
115643
- for (const j of view.getJoin() || []) {
115644
- if (previous === "") {
115645
- from += this.quote(j.LTAB.toLowerCase()) + " INNER JOIN " + this.quote(j.RTAB.toLowerCase()) + " ON " + this.quote(j.LTAB.toLowerCase()) + "." + j.LFIELD.toLowerCase() + " = " + this.quote(j.RTAB.toLowerCase()) + "." + j.RFIELD.toLowerCase();
115646
- }
115647
- else if (previous === j.LTAB + "," + j.RTAB) {
115648
- from += " AND " + this.quote(j.LTAB.toLowerCase()) + "." + j.LFIELD.toLowerCase() + " = " + this.quote(j.RTAB.toLowerCase()) + "." + j.RFIELD.toLowerCase();
115649
- }
115650
- else {
115651
- from += " INNER JOIN " + this.quote(j.RTAB.toLowerCase()) + " ON " + this.quote(j.LTAB.toLowerCase()) + "." + j.LFIELD.toLowerCase() + " = " + this.quote(j.RTAB.toLowerCase()) + "." + j.RFIELD.toLowerCase();
115652
- }
115653
- previous = j.LTAB + "," + j.RTAB;
115654
- }
115655
- from = from.trim();
115656
- if (from === "") {
115657
- from = firstTabname;
115658
- }
115659
- return `CREATE VIEW ${this.quote(view.getName().toLowerCase())} AS SELECT ${columns} FROM ${from};\n`;
115704
+ // a DD28J row compares two fields, or without RFIELD a field to the constant in RTAB, eg. 'EN'
115705
+ const joins = view.getJoin() || [];
115706
+ let pending = joins.filter(j => j.RFIELD !== undefined && j.RFIELD !== "");
115707
+ const constants = joins.filter(j => j.RFIELD === undefined || j.RFIELD === "");
115708
+ // start from the table of the first view field, typically the root table, then repeatedly
115709
+ // join a table that has conditions on the tables already joined, LTAB is not always the new one
115710
+ const tables = [];
115711
+ const conditions = {};
115712
+ const add = (table) => {
115713
+ tables.push(table);
115714
+ const on = pending.filter(j => tables.includes(j.LTAB) && tables.includes(j.RTAB));
115715
+ pending = pending.filter(j => on.includes(j) === false);
115716
+ conditions[table] = [...on, ...constants.filter(j => j.LTAB === table)].map(j => this.condition(view, j));
115717
+ };
115718
+ const first = fields[0]?.TABNAME;
115719
+ const root = first !== undefined && (pending.length === 0 || pending.some(j => j.LTAB === first || j.RTAB === first))
115720
+ ? first : (pending[0] || constants[0])?.LTAB;
115721
+ if (root === undefined) {
115722
+ throw new Error(`View ${view.getName()}: no tables`);
115723
+ }
115724
+ add(root);
115725
+ while (pending.length > 0) {
115726
+ // tables without conditions on the joined ones are cross joined
115727
+ const next = pending.find(j => tables.includes(j.LTAB) || tables.includes(j.RTAB)) || pending[0];
115728
+ add(tables.includes(next.LTAB) ? next.RTAB : next.LTAB);
115729
+ }
115730
+ for (const c of constants) {
115731
+ if (tables.includes(c.LTAB) === false) {
115732
+ add(c.LTAB);
115733
+ }
115734
+ }
115735
+ let from = this.quote(tables[0]);
115736
+ for (const table of tables.slice(1)) {
115737
+ from += conditions[table].length === 0
115738
+ ? " CROSS JOIN " + this.quote(table)
115739
+ : " INNER JOIN " + this.quote(table) + " ON " + conditions[table].join(" AND ");
115740
+ }
115741
+ if (conditions[tables[0]].length > 0) {
115742
+ from += " WHERE " + conditions[tables[0]].join(" AND ");
115743
+ }
115744
+ return `CREATE VIEW ${this.quote(view.getName())} AS SELECT ${columns} FROM ${from};\n`;
115745
+ }
115746
+ condition(view, j) {
115747
+ const operator = OPERATORS[j.OPERATOR];
115748
+ if (operator === undefined) {
115749
+ throw new Error(`View ${view.getName()}: join operator ${j.OPERATOR} not supported`);
115750
+ }
115751
+ const right = j.RFIELD === undefined || j.RFIELD === ""
115752
+ ? this.escape(j.RTAB)
115753
+ : this.quote(j.RTAB) + "." + j.RFIELD.toLowerCase();
115754
+ return this.quote(j.LTAB) + "." + j.LFIELD.toLowerCase() + " " + operator + " " + right;
115755
+ }
115756
+ // the constant keeps its single quotes, statements are inside a javascript string template
115757
+ escape(value) {
115758
+ let ret = value.replace(/\\/g, "\\\\");
115759
+ ret = ret.replace(/`/g, "\\`");
115760
+ ret = ret.replace(/\${/g, "\\${");
115761
+ return ret;
115660
115762
  }
115661
115763
  }
115662
115764
  exports.DatabaseSchemaReuse = DatabaseSchemaReuse;
@@ -118863,6 +118965,7 @@ const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_
118863
118965
  const chunk_1 = __webpack_require__(/*! ../chunk */ "./node_modules/@abaplint/transpiler/build/src/chunk.js");
118864
118966
  const type_name_or_infer_1 = __webpack_require__(/*! ./type_name_or_infer */ "./node_modules/@abaplint/transpiler/build/src/expressions/type_name_or_infer.js");
118865
118967
  const types_1 = __webpack_require__(/*! ../types */ "./node_modules/@abaplint/transpiler/build/src/types.js");
118968
+ const field_assignment_1 = __webpack_require__(/*! ./field_assignment */ "./node_modules/@abaplint/transpiler/build/src/expressions/field_assignment.js");
118866
118969
  class NewObjectTranspiler {
118867
118970
  transpile(node, traversal) {
118868
118971
  const ret = new chunk_1.Chunk();
@@ -118907,10 +119010,48 @@ class NewObjectTranspiler {
118907
119010
  ret.appendString(traversal.toType(type) + ".set(await (new " + clas + "()).constructor_(" + para + "))");
118908
119011
  }
118909
119012
  else {
118910
- const source = node.findFirstExpression(abaplint.Expressions.Source);
119013
+ // a new data object with a copy of the value, never the source itself; async, as sources can await
118911
119014
  const typeCode = traversal.toType(type);
118912
- const sourceCode = source === undefined ? typeCode : traversal.traverse(source).getCode();
118913
- ret.appendString("((() => {const r = new abap.types.DataReference(" + typeCode + "); r.assign(" + sourceCode + "); return r; })())");
119015
+ const setCode = this.buildValue(node, type, traversal);
119016
+ ret.appendString("(await (async () => {const r = new abap.types.DataReference(" + typeCode + "); r.assign(" + typeCode + ");" + setCode + " return r; })())");
119017
+ }
119018
+ return ret;
119019
+ }
119020
+ buildValue(node, type, traversal) {
119021
+ let ret = "";
119022
+ const parameters = node.findDirectExpression(core_1.Expressions.ParameterListS);
119023
+ if (parameters) {
119024
+ // NEW ty( a = 1 b = 2 ), components of a structure
119025
+ for (const p of parameters.findDirectExpressions(core_1.Expressions.ParameterS)) {
119026
+ const name = p.findDirectExpression(core_1.Expressions.ParameterName).concatTokens().toLowerCase();
119027
+ const source = traversal.traverse(p.findDirectExpression(core_1.Expressions.Source)).getCode();
119028
+ ret += ` r.getPointer().setField("${name}", ${source});`;
119029
+ }
119030
+ return ret;
119031
+ }
119032
+ const single = node.getChildren()[3];
119033
+ if (single instanceof core_1.Nodes.ExpressionNode && single.get() instanceof core_1.Expressions.Source) {
119034
+ return ` r.getPointer().set(${traversal.traverse(single).getCode()});`;
119035
+ }
119036
+ else if (!(type instanceof abaplint.BasicTypes.TableType)) {
119037
+ return ret;
119038
+ }
119039
+ // NEW tab( ( 1 ) ( a = 2 ) ), table lines
119040
+ const rowType = traversal.toType(type.getRowType());
119041
+ let row = "";
119042
+ for (const c of node.getChildren().slice(3)) {
119043
+ if (c instanceof core_1.Nodes.TokenNode) {
119044
+ if (c.getFirstToken().getStr() === ")" && row !== "") {
119045
+ ret += ` r.getPointer().appendThis(${rowType}${row});`;
119046
+ row = "";
119047
+ }
119048
+ }
119049
+ else if (c.get() instanceof core_1.Expressions.Source) {
119050
+ row += `.set(${traversal.traverse(c).getCode()})`;
119051
+ }
119052
+ else if (c.get() instanceof core_1.Expressions.FieldAssignment && c instanceof core_1.Nodes.ExpressionNode) {
119053
+ row += new field_assignment_1.FieldAssignmentTranspiler().transpile(c, traversal).getCode();
119054
+ }
118914
119055
  }
118915
119056
  return ret;
118916
119057
  }
@@ -135039,6 +135180,9 @@ class ClassImplementationTranspiler {
135039
135180
  transpile(node, traversal) {
135040
135181
  const ret = new chunk_1.Chunk();
135041
135182
  for (const c of node.getChildren()) {
135183
+ if (c.get() instanceof abaplint.Statements.EndClass) {
135184
+ ret.appendString(this.buildPartiallyImplementedStubs(node, traversal));
135185
+ }
135042
135186
  ret.appendChunk(traversal.traverse(c));
135043
135187
  if (c.get() instanceof abaplint.Statements.ClassImplementation) {
135044
135188
  ret.appendString(this.buildConstructor(node.getFirstStatement(), traversal));
@@ -135054,6 +135198,103 @@ class ClassImplementationTranspiler {
135054
135198
  return ret;
135055
135199
  }
135056
135200
  ///////////////////////////////
135201
+ /** INTERFACES ... PARTIALLY IMPLEMENTED: the interface methods the test class
135202
+ * does not implement exist on a system and raise CX_SY_DYN_CALL_ILLEGAL_METHOD
135203
+ * when called. Emits a method of that shape under the name the implementation
135204
+ * would have had, so the call ends in that exception and not in a TypeError */
135205
+ buildPartiallyImplementedStubs(node, traversal) {
135206
+ const token = node.findFirstExpression(abaplint.Expressions.ClassName)?.getFirstToken();
135207
+ if (token === undefined) {
135208
+ return "";
135209
+ }
135210
+ const cdef = traversal.getClassDefinition(token);
135211
+ const scope = traversal.findCurrentScopeByToken(token);
135212
+ if (cdef === undefined || scope === undefined) {
135213
+ return "";
135214
+ }
135215
+ const partial = cdef.getImplementing().filter(i => i.partial === true);
135216
+ if (partial.length === 0) {
135217
+ return "";
135218
+ }
135219
+ // the methods the class implements itself, by the emitted name
135220
+ const implemented = new Set();
135221
+ for (const m of node.findAllStatements(abaplint.Statements.MethodImplementation)) {
135222
+ const name = m.findFirstExpression(abaplint.Expressions.MethodName)?.getFirstToken().getStr();
135223
+ if (name !== undefined) {
135224
+ implemented.add(traversal_1.Traversal.escapeNamespace(name.replace("~", "$").toLowerCase()));
135225
+ }
135226
+ }
135227
+ // an interface a superclass implements is inherited, with the real implementations,
135228
+ // a stub for one of its methods would override those
135229
+ const inherited = new Set();
135230
+ let sup = cdef.getSuperClass();
135231
+ while (sup !== undefined) {
135232
+ const sdef = traversal.findClassDefinition(sup, scope);
135233
+ for (const name of this.findIncludedInterfaces(sdef?.getImplementing().map(i => i.name) || [], scope, traversal)) {
135234
+ inherited.add(name);
135235
+ }
135236
+ sup = sdef?.getSuperClass();
135237
+ }
135238
+ let ret = "";
135239
+ for (const name of this.findIncludedInterfaces(partial.map(i => i.name), scope, traversal)) {
135240
+ if (inherited.has(name)) {
135241
+ continue;
135242
+ }
135243
+ const intf = traversal.findInterfaceDefinition(name, scope);
135244
+ if (intf === undefined) {
135245
+ continue;
135246
+ }
135247
+ const prefix = traversal_1.Traversal.escapeNamespace(intf.getName().toLowerCase()) + "$";
135248
+ for (const m of intf.getMethodDefinitions().getAll()) {
135249
+ const emitted = prefix + traversal_1.Traversal.escapeNamespace(m.getName().toLowerCase());
135250
+ if (implemented.has(emitted)) {
135251
+ continue;
135252
+ }
135253
+ implemented.add(emitted);
135254
+ ret += this.buildNotImplementedMethod(emitted, m, token.getStr().toLowerCase(), intf.getName(), token, traversal);
135255
+ }
135256
+ }
135257
+ return ret;
135258
+ }
135259
+ /** the interfaces, upper cased, with the interfaces they include, recursively */
135260
+ findIncludedInterfaces(names, scope, traversal) {
135261
+ const ret = [];
135262
+ const queue = [...names];
135263
+ while (queue.length > 0) {
135264
+ const name = queue.shift().toUpperCase();
135265
+ if (ret.includes(name)) {
135266
+ continue;
135267
+ }
135268
+ ret.push(name);
135269
+ const intf = traversal.findInterfaceDefinition(name, scope);
135270
+ queue.push(...(intf?.getImplementing().map(i => i.name) || []));
135271
+ }
135272
+ return ret;
135273
+ }
135274
+ /** the method a system generates for an interface method the test class leaves
135275
+ * out under PARTIALLY IMPLEMENTED: calling it raises CX_SY_DYN_CALL_ILLEGAL_METHOD
135276
+ * (ABAP keyword documentation, INTERFACES - PARTIALLY IMPLEMENTED), the class and
135277
+ * method named, instead of a JavaScript TypeError no CATCH sees */
135278
+ buildNotImplementedMethod(emitted, m, className, interfaceName, token, traversal) {
135279
+ const unique = m.getParameters().getAll().length > 0 ? "INPUT" : "";
135280
+ const exception = traversal.lookupClassOrInterface("'CX_SY_DYN_CALL_ILLEGAL_METHOD'", token, true);
135281
+ const classUpper = className.toUpperCase();
135282
+ const methodUpper = interfaceName.toUpperCase() + "~" + m.getName().toUpperCase();
135283
+ const notFound = `CX_SY_DYN_CALL_ILLEGAL_METHOD not found, ${classUpper}->${methodUpper} is not implemented`;
135284
+ const body = `if (${exception} === undefined) { throw new Error("${notFound}"); }\n` +
135285
+ `throw await new ${exception}().constructor_({` +
135286
+ `classname: new abap.types.String().set("${classUpper}"), ` +
135287
+ `methodname: new abap.types.String().set("${methodUpper}")});\n`;
135288
+ let ret = "";
135289
+ if (m.isStatic() === true) {
135290
+ // in ABAP static methods can be called with instance arrows, "->"
135291
+ ret += "async " + emitted + "(" + unique + ") {\n" +
135292
+ "return " + traversal_1.Traversal.escapeNamespace(className) + "." + emitted + "(" + unique + ");\n" +
135293
+ "}\n" + "static ";
135294
+ }
135295
+ ret += "async " + emitted + "(" + unique + ") {\n" + body + "}\n";
135296
+ return ret;
135297
+ }
135057
135298
  hasConstructor(node) {
135058
135299
  for (const m of node.findAllStatements(abaplint.Statements.MethodImplementation)) {
135059
135300
  const name = m.findFirstExpression(abaplint.Expressions.MethodName)?.getFirstToken().getStr();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler-cli",
3
- "version": "2.14.11",
3
+ "version": "2.14.12",
4
4
  "description": "Transpiler - Command Line Interface",
5
5
  "funding": "https://github.com/sponsors/larshp",
6
6
  "bin": {
@@ -27,8 +27,8 @@
27
27
  "author": "abaplint",
28
28
  "license": "MIT",
29
29
  "devDependencies": {
30
- "@abaplint/core": "^2.120.86",
31
- "@abaplint/transpiler": "^2.14.11",
30
+ "@abaplint/core": "^2.120.87",
31
+ "@abaplint/transpiler": "^2.14.12",
32
32
  "@types/glob": "^9.0.0",
33
33
  "@types/node": "^26.6.5",
34
34
  "@types/progress": "^2.0.7",