@abaplint/runtime 2.13.96 → 2.13.97

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.
@@ -14,9 +14,19 @@ export declare class Context {
14
14
  RFCDestinations: {
15
15
  [name: string]: RFC.RFCClient;
16
16
  };
17
+ updateTask: {
18
+ register(name: string, param: {
19
+ exporting?: any;
20
+ tables?: any;
21
+ }): Promise<void> | void;
22
+ } | undefined;
17
23
  dataset: DatasetHost | undefined;
18
24
  datasets: {
19
25
  [name: string]: OpenDataset;
20
26
  };
27
+ runTime: {
28
+ start: number;
29
+ last: number;
30
+ } | undefined;
21
31
  defaultDB(): DatabaseClient;
22
32
  }
@@ -8,10 +8,14 @@ class Context {
8
8
  // DEFAULT and secondary database connections
9
9
  databaseConnections = {};
10
10
  RFCDestinations = {};
11
+ // CALL FUNCTION IN UPDATE TASK hands the copied parameters to the host, if one is set
12
+ updateTask = undefined;
11
13
  // the file system OPEN DATASET and friends read and write; none by default,
12
14
  // and then every DATASET statement throws
13
15
  dataset = undefined;
14
16
  datasets = {};
17
+ // GET RUN TIME, the clock reading of the first call, and the last value returned
18
+ runTime = undefined;
15
19
  defaultDB() {
16
20
  if (this.databaseConnections["DEFAULT"] === undefined) {
17
21
  throw new Error("Runtime, database not initialized");
@@ -2,3 +2,6 @@ import { Float, Integer, Integer8 } from "../types";
2
2
  import { ICharacter } from "../types/_character";
3
3
  import { INumeric } from "../types/_numeric";
4
4
  export declare function divide(left: INumeric | ICharacter | Integer8 | string | number, right: INumeric | ICharacter | Integer8 | string | number): Float | Integer8 | Integer;
5
+ /** "/" in calculation type int8, which the transpiler knows from an int8 target: the
6
+ * operands are int8 and the quotient is rounded half away from zero, 7 / 2 = 4, -7 / 2 = -4 */
7
+ export declare function divideInt8(left: INumeric | ICharacter | Integer8 | string | number, right: INumeric | ICharacter | Integer8 | string | number): Integer8;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.divide = divide;
4
+ exports.divideInt8 = divideInt8;
4
5
  const throw_error_1 = require("../throw_error");
5
6
  const types_1 = require("../types");
6
7
  const _parse_1 = require("./_parse");
@@ -42,4 +43,22 @@ function isIntegerOperand(val) {
42
43
  }
43
44
  return val instanceof types_1.Integer8;
44
45
  }
46
+ /** "/" in calculation type int8, which the transpiler knows from an int8 target: the
47
+ * operands are int8 and the quotient is rounded half away from zero, 7 / 2 = 4, -7 / 2 = -4 */
48
+ function divideInt8(left, right) {
49
+ const l = left instanceof types_1.Integer8 ? left.get() : new types_1.Integer8().set(left).get();
50
+ const r = right instanceof types_1.Integer8 ? right.get() : new types_1.Integer8().set(right).get();
51
+ if (r === 0n) {
52
+ if (l === 0n) {
53
+ return new types_1.Integer8().set(0n);
54
+ }
55
+ (0, throw_error_1.throwError)("CX_SY_ZERODIVIDE");
56
+ }
57
+ let q = l / r;
58
+ const rem = l % r;
59
+ if ((rem < 0n ? -rem : rem) * 2n >= (r < 0n ? -r : r)) {
60
+ q = (l < 0n) === (r < 0n) ? q + 1n : q - 1n;
61
+ }
62
+ return new types_1.Integer8().set(q);
63
+ }
45
64
  //# sourceMappingURL=divide.js.map
@@ -1,6 +1,7 @@
1
1
  import { Context } from "../context";
2
2
  export interface ICallFunctionOptions {
3
3
  name: string;
4
+ updateTask?: boolean;
4
5
  destination?: string;
5
6
  calling?: (INPUT: any) => any;
6
7
  exporting?: any;
@@ -9,7 +9,7 @@ class CallFunction {
9
9
  constructor(context) {
10
10
  this.context = context;
11
11
  }
12
- // note: this is only called if DESTINIATION is supplied
12
+ // called for DESTINATION, CALLING or IN UPDATE TASK
13
13
  async callFunction(options) {
14
14
  const param = {
15
15
  exporting: options.exporting,
@@ -20,7 +20,32 @@ class CallFunction {
20
20
  };
21
21
  options.name = options.name.trimEnd();
22
22
  const fm = abap.FunctionModules[options.name];
23
- if (options.destination) {
23
+ if (options.updateTask) {
24
+ if (fm === undefined) {
25
+ (0, throw_error_1.throwError)("CX_SY_DYN_CALL_ILLEGAL_FUNC");
26
+ }
27
+ const copy = (values) => {
28
+ if (values === undefined) {
29
+ return undefined;
30
+ }
31
+ const result = {};
32
+ for (const key of Object.keys(values)) {
33
+ const value = values[key];
34
+ result[key] = value instanceof types_1.FieldSymbol ? value.getPointer().clone() : value.clone();
35
+ }
36
+ return result;
37
+ };
38
+ // IMPORTING and CHANGING do not apply to update modules.
39
+ const copied = { exporting: copy(options.exporting), tables: copy(options.tables) };
40
+ if (this.context.updateTask) {
41
+ await this.context.updateTask.register(options.name, copied);
42
+ }
43
+ else {
44
+ await fm(copied);
45
+ }
46
+ return;
47
+ }
48
+ else if (options.destination) {
24
49
  if (options.destination.trim() === "") {
25
50
  if (fm === undefined) {
26
51
  (0, throw_error_1.throwError)("CX_SY_DYN_CALL_ILLEGAL_FUNC");
@@ -1,2 +1,3 @@
1
+ import { Context } from "../context";
1
2
  import { INumeric } from "../types/_numeric";
2
- export declare function getRunTime(value: INumeric): void;
3
+ export declare function getRunTime(context: Context, value: INumeric): void;
@@ -1,16 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getRunTime = getRunTime;
4
- let prev = undefined;
5
- function getRunTime(value) {
6
- if (prev === undefined) {
7
- value.set(0);
8
- prev = new Date().getTime();
4
+ // milliseconds with a fraction, from a monotonic clock where there is one:
5
+ // performance.now() in Node, Bun and browsers, Date.now() otherwise
6
+ function now() {
7
+ if (typeof performance === "object" && typeof performance?.now === "function") {
8
+ return performance.now();
9
9
  }
10
- else {
11
- const now = new Date().getTime();
12
- value.set(now - prev);
13
- prev = now;
10
+ return Date.now();
11
+ }
12
+ // GET RUN TIME FIELD: microseconds since the first call in the internal
13
+ // session, which gives 0. The first call is per context, so per ABAP instance
14
+ function getRunTime(context, value) {
15
+ const t = now();
16
+ if (context.runTime === undefined) {
17
+ context.runTime = { start: t, last: 0 };
18
+ }
19
+ let micro = Math.floor((t - context.runTime.start) * 1000);
20
+ // never backwards, also when the clock is Date.now()
21
+ if (micro < context.runTime.last) {
22
+ micro = context.runTime.last;
14
23
  }
24
+ context.runTime.last = micro;
25
+ value.set(micro);
15
26
  }
16
27
  //# sourceMappingURL=get_run_time.js.map
@@ -23,7 +23,6 @@ import { getLocale } from "./get_locale";
23
23
  import { unpack } from "./unpack";
24
24
  import { getParameter } from "./get_parameter";
25
25
  import { setLocale } from "./set_locale";
26
- import { getRunTime } from "./get_run_time";
27
26
  import { getTime } from "./get_time";
28
27
  import { IInsertDatabaseOptions } from "./insert_database";
29
28
  import { insertInternal } from "./insert_internal";
@@ -73,7 +72,6 @@ export declare class Statements {
73
72
  getBit: typeof getBit;
74
73
  getLocale: typeof getLocale;
75
74
  getParameter: typeof getParameter;
76
- getRunTime: typeof getRunTime;
77
75
  getTime: typeof getTime;
78
76
  insertInternal: typeof insertInternal;
79
77
  loop: typeof loop;
@@ -96,6 +94,7 @@ export declare class Statements {
96
94
  private readonly context;
97
95
  constructor(context: Context);
98
96
  _setTrace(min?: number, totals?: boolean): Trace;
97
+ getRunTime(value: INumeric): void;
99
98
  openCursor(target: INumeric, select: string, options: IOpenCursorDatabaseOptions): Promise<void>;
100
99
  fetchNextCursor(cursor: INumeric, target: any, packageSize?: INumeric): Promise<void>;
101
100
  closeCursor(cursor: INumeric): Promise<void>;
@@ -76,7 +76,6 @@ class Statements {
76
76
  getBit = get_bit_1.getBit;
77
77
  getLocale = get_locale_1.getLocale;
78
78
  getParameter = get_parameter_1.getParameter;
79
- getRunTime = get_run_time_1.getRunTime;
80
79
  getTime = get_time_1.getTime;
81
80
  insertInternal = insert_internal_1.insertInternal;
82
81
  loop = loop_1.loop;
@@ -103,6 +102,9 @@ class Statements {
103
102
  _setTrace(min = 10, totals = false) {
104
103
  return new trace_1.Trace().setTrace(min, totals, this);
105
104
  }
105
+ getRunTime(value) {
106
+ (0, get_run_time_1.getRunTime)(this.context, value);
107
+ }
106
108
  async openCursor(target, select, options) {
107
109
  const num = await (0, open_cursor_1.openCursor)(this.context, select, options);
108
110
  target.set(num);
@@ -152,11 +152,12 @@ async function* loop(table, options) {
152
152
  let loopTo = options?.to && options.to.get() < length ? options.to.get() : length;
153
153
  let array = [];
154
154
  let block = undefined;
155
- // a dynamic key name may come in any case, USING KEY ('PRIMARY_KEY') is the primary key too
156
- const isPrimaryKey = options?.usingKey === undefined || options.usingKey.toLowerCase() === "primary_key";
157
- if (options?.usingKey && isPrimaryKey === false) {
158
- array = table.getSecondaryIndex(options.usingKey);
159
- const { from, to } = determineFromTo(array, options.topEquals, table.getKeyByName(options.usingKey));
155
+ // the secondary key the loop runs over; undefined is the primary key, also
156
+ // when it is named - USING KEY PRIMARY_KEY, or a dynamic name holding it
157
+ const usingKey = (0, types_1.secondaryKeyName)(options?.usingKey);
158
+ if (usingKey !== undefined) {
159
+ array = table.getSecondaryIndex(usingKey);
160
+ const { from, to } = determineFromTo(array, options?.topEquals, table.getKeyByName(usingKey));
160
161
  loopFrom = Math.max(loopFrom, from) - 1;
161
162
  loopTo = Math.min(loopTo, to);
162
163
  }
@@ -195,9 +196,7 @@ async function* loop(table, options) {
195
196
  // The same goes for a loop that reads an index table through a hash
196
197
  // secondary key. Handing out 1, 2, 3 there looks helpful and is a lie the
197
198
  // caller cannot tell from the truth.
198
- const usedKey = options?.usingKey === undefined || isPrimaryKey
199
- ? undefined
200
- : table.getKeyByName(options.usingKey);
199
+ const usedKey = usingKey === undefined ? undefined : table.getKeyByName(usingKey);
201
200
  const hasRowNumber = usedKey !== undefined
202
201
  ? usedKey.type !== types_1.TableAccessType.hashed
203
202
  : !(table instanceof types_1.HashedTable);
@@ -226,7 +225,7 @@ async function* loop(table, options) {
226
225
  options?.atLast?.(loopController.index + 1 >= loopController.loopTo);
227
226
  yield current;
228
227
  loopController.index++;
229
- if (options?.to === undefined && options?.usingKey === undefined) {
228
+ if (options?.to === undefined && usingKey === undefined) {
230
229
  // extra rows might have been inserted inside the loop
231
230
  loopController.loopTo = array.length;
232
231
  }
@@ -169,6 +169,11 @@ function readTable(table, options) {
169
169
  // return readTable(table.getPointer(), options);
170
170
  table = table.getPointer();
171
171
  }
172
+ // WITH [TABLE] KEY primary_key COMPONENTS reads as the same READ without
173
+ // the key name; any other name, as ABAP reads it (secondaryKeyName)
174
+ if (options?.keyName !== undefined && (0, types_1.secondaryKeyName)(options.keyName) !== options.keyName) {
175
+ options = { ...options, keyName: (0, types_1.secondaryKeyName)(options.keyName) };
176
+ }
172
177
  // check if it is a primary index read specified with WITH KEY instead of WITH TABLE KEY
173
178
  if (options?.withTableKey === undefined
174
179
  && options?.withKeySimple
@@ -45,6 +45,11 @@ interface ITable {
45
45
  export declare class TableFactory {
46
46
  static construct(rowType: TableRowType, options?: ITableOptions, qualifiedName?: string): Table | HashedTable;
47
47
  }
48
+ /** The secondary key a key name selects, as ABAP reads the name: without the
49
+ * blanks a c field pads it with, in any case - and undefined for the primary
50
+ * key, which primary_key names however it is written, and so does an initial
51
+ * name (a dynamic USING KEY ( name ) that is empty). */
52
+ export declare function secondaryKeyName(name: string | undefined): string | undefined;
48
53
  export declare class HashedTable implements ITable {
49
54
  private value;
50
55
  private readonly header;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Table = exports.HashedTable = exports.TableFactory = exports.LoopController = exports.TableKeyType = exports.TableAccessType = void 0;
4
+ exports.secondaryKeyName = secondaryKeyName;
4
5
  const integer_1 = require("./integer");
5
6
  const float_1 = require("./float");
6
7
  const abap_object_1 = require("./abap_object");
@@ -75,6 +76,14 @@ export class SortedTable {
75
76
  // todo
76
77
  }
77
78
  */
79
+ /** The secondary key a key name selects, as ABAP reads the name: without the
80
+ * blanks a c field pads it with, in any case - and undefined for the primary
81
+ * key, which primary_key names however it is written, and so does an initial
82
+ * name (a dynamic USING KEY ( name ) that is empty). */
83
+ function secondaryKeyName(name) {
84
+ const trimmed = name?.trimEnd();
85
+ return trimmed === undefined || trimmed === "" || trimmed.toLowerCase() === "primary_key" ? undefined : trimmed;
86
+ }
78
87
  class HashedTable {
79
88
  value;
80
89
  header;
@@ -107,11 +116,11 @@ class HashedTable {
107
116
  return Object.keys(this.value).length;
108
117
  }
109
118
  getKeyByName(name) {
110
- return this.getOptions()?.secondary?.find(s => s.name.toUpperCase() === name.toUpperCase());
119
+ return this.getOptions()?.secondary?.find(s => s.name.toUpperCase() === name.trimEnd().toUpperCase());
111
120
  }
112
121
  getSecondaryIndex(name) {
113
- if (this.secondaryIndexes[name.toUpperCase()]) {
114
- return this.secondaryIndexes[name.toUpperCase()];
122
+ if (this.secondaryIndexes[name.trimEnd().toUpperCase()]) {
123
+ return this.secondaryIndexes[name.trimEnd().toUpperCase()];
115
124
  }
116
125
  const secondary = this.getKeyByName(name);
117
126
  if (secondary === undefined) {
@@ -120,7 +129,7 @@ class HashedTable {
120
129
  // note, array() already is a copy, so it can be used,
121
130
  const copy = secondaryDuplicatesFirst(this.array(), secondary);
122
131
  (0, sort_1.sort)(copy, { by: secondary.keyFields.map(k => { return { component: k.toLowerCase() }; }) });
123
- this.secondaryIndexes[name.toUpperCase()] = copy;
132
+ this.secondaryIndexes[name.trimEnd().toUpperCase()] = copy;
124
133
  return copy;
125
134
  }
126
135
  buildHashFromData(data) {
@@ -364,11 +373,11 @@ class Table {
364
373
  return this.value.length;
365
374
  }
366
375
  getKeyByName(name) {
367
- return this.getOptions()?.secondary?.find(s => s.name.toUpperCase() === name.toUpperCase());
376
+ return this.getOptions()?.secondary?.find(s => s.name.toUpperCase() === name.trimEnd().toUpperCase());
368
377
  }
369
378
  getSecondaryIndex(name) {
370
- if (this.secondaryIndexes[name.toUpperCase()]) {
371
- return this.secondaryIndexes[name.toUpperCase()];
379
+ if (this.secondaryIndexes[name.trimEnd().toUpperCase()]) {
380
+ return this.secondaryIndexes[name.trimEnd().toUpperCase()];
372
381
  }
373
382
  const secondary = this.getKeyByName(name);
374
383
  if (secondary === undefined) {
@@ -376,7 +385,7 @@ class Table {
376
385
  }
377
386
  const copy = secondaryDuplicatesFirst([...this.value], secondary);
378
387
  (0, sort_1.sort)(copy, { by: secondary.keyFields.map(k => { return { component: k.toLowerCase() }; }), skipSortedCheck: true });
379
- this.secondaryIndexes[name.toUpperCase()] = copy;
388
+ this.secondaryIndexes[name.trimEnd().toUpperCase()] = copy;
380
389
  return copy;
381
390
  }
382
391
  getQualifiedName() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/runtime",
3
- "version": "2.13.96",
3
+ "version": "2.13.97",
4
4
  "description": "Transpiler - Runtime",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/src/index.d.ts",