@sap/cds-compiler 2.10.4 → 2.12.0

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 (103) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +58 -35
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +16 -0
  9. package/lib/api/.eslintrc.json +2 -0
  10. package/lib/api/main.js +10 -36
  11. package/lib/api/options.js +17 -8
  12. package/lib/api/validate.js +30 -3
  13. package/lib/backends.js +12 -13
  14. package/lib/base/dictionaries.js +2 -1
  15. package/lib/base/keywords.js +3 -2
  16. package/lib/base/message-registry.js +64 -11
  17. package/lib/base/messages.js +38 -18
  18. package/lib/base/model.js +6 -4
  19. package/lib/base/optionProcessorHelper.js +148 -86
  20. package/lib/checks/.eslintrc.json +2 -0
  21. package/lib/checks/actionsFunctions.js +2 -1
  22. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  23. package/lib/checks/foreignKeys.js +4 -4
  24. package/lib/checks/managedInType.js +4 -4
  25. package/lib/checks/queryNoDbArtifacts.js +1 -3
  26. package/lib/checks/selectItems.js +4 -0
  27. package/lib/checks/sql-snippets.js +93 -0
  28. package/lib/checks/unknownMagic.js +6 -3
  29. package/lib/checks/validator.js +8 -0
  30. package/lib/compiler/assert-consistency.js +14 -5
  31. package/lib/compiler/base.js +64 -0
  32. package/lib/compiler/builtins.js +62 -16
  33. package/lib/compiler/checks.js +34 -10
  34. package/lib/compiler/definer.js +91 -112
  35. package/lib/compiler/index.js +30 -30
  36. package/lib/compiler/propagator.js +8 -4
  37. package/lib/compiler/resolver.js +279 -63
  38. package/lib/compiler/shared.js +65 -230
  39. package/lib/compiler/utils.js +191 -0
  40. package/lib/edm/annotations/genericTranslation.js +35 -18
  41. package/lib/edm/annotations/preprocessAnnotations.js +1 -1
  42. package/lib/edm/csn2edm.js +4 -3
  43. package/lib/edm/edm.js +8 -8
  44. package/lib/edm/edmPreprocessor.js +61 -59
  45. package/lib/edm/edmUtils.js +14 -15
  46. package/lib/gen/Dictionary.json +82 -40
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +19 -1
  49. package/lib/gen/language.tokens +80 -73
  50. package/lib/gen/languageLexer.interp +27 -1
  51. package/lib/gen/languageLexer.js +925 -826
  52. package/lib/gen/languageLexer.tokens +72 -65
  53. package/lib/gen/languageParser.js +4817 -4102
  54. package/lib/json/from-csn.js +57 -26
  55. package/lib/json/to-csn.js +244 -51
  56. package/lib/language/antlrParser.js +12 -1
  57. package/lib/language/docCommentParser.js +1 -1
  58. package/lib/language/errorStrategy.js +26 -8
  59. package/lib/language/genericAntlrParser.js +106 -30
  60. package/lib/language/language.g4 +200 -70
  61. package/lib/language/multiLineStringParser.js +536 -0
  62. package/lib/main.d.ts +220 -21
  63. package/lib/main.js +6 -3
  64. package/lib/model/api.js +2 -2
  65. package/lib/model/csnRefs.js +218 -86
  66. package/lib/model/csnUtils.js +99 -178
  67. package/lib/model/enrichCsn.js +84 -43
  68. package/lib/model/revealInternalProperties.js +25 -8
  69. package/lib/model/sortViews.js +8 -1
  70. package/lib/modelCompare/compare.js +2 -1
  71. package/lib/optionProcessor.js +33 -18
  72. package/lib/render/.eslintrc.json +1 -2
  73. package/lib/render/DuplicateChecker.js +2 -2
  74. package/lib/render/manageConstraints.js +1 -1
  75. package/lib/render/toCdl.js +202 -82
  76. package/lib/render/toHdbcds.js +194 -135
  77. package/lib/render/toRename.js +7 -10
  78. package/lib/render/toSql.js +91 -51
  79. package/lib/render/utils/common.js +24 -5
  80. package/lib/render/utils/sql.js +6 -4
  81. package/lib/transform/braceExpression.js +4 -2
  82. package/lib/transform/db/applyTransformations.js +189 -0
  83. package/lib/transform/db/associations.js +389 -0
  84. package/lib/transform/db/cdsPersistence.js +150 -0
  85. package/lib/transform/db/constraints.js +275 -119
  86. package/lib/transform/db/draft.js +6 -4
  87. package/lib/transform/db/expansion.js +10 -9
  88. package/lib/transform/db/flattening.js +23 -8
  89. package/lib/transform/db/temporal.js +236 -0
  90. package/lib/transform/db/transformExists.js +106 -25
  91. package/lib/transform/db/views.js +485 -0
  92. package/lib/transform/forHanaNew.js +90 -1036
  93. package/lib/transform/forOdataNew.js +11 -3
  94. package/lib/transform/localized.js +5 -14
  95. package/lib/transform/odata/generateForeignKeyElements.js +2 -2
  96. package/lib/transform/transformUtilsNew.js +34 -20
  97. package/lib/transform/translateAssocsToJoins.js +15 -23
  98. package/lib/transform/universalCsnEnricher.js +217 -47
  99. package/lib/utils/file.js +13 -6
  100. package/lib/utils/term.js +65 -42
  101. package/lib/utils/timetrace.js +55 -27
  102. package/package.json +1 -1
  103. package/lib/transform/db/helpers.js +0 -58
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const { setProp } = require('../base/model');
4
3
  const { csnRefs } = require('../model/csnRefs');
4
+ const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
5
+ const { isBuiltinType } = require('../compiler/builtins.js')
5
6
  const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
6
7
  const version = require('../../package.json').version;
7
8
 
@@ -11,8 +12,8 @@ const version = require('../../package.json').version;
11
12
  * Generic Callback
12
13
  *
13
14
  * @callback genericCallback
14
- * @param {CSN.Artifact} art
15
- * @param {CSN.FQN} name Artifact Name
15
+ * @param {any} art
16
+ * @param {string} name Artifact Name
16
17
  * @param {string} prop Dictionary Property
17
18
  * @param {CSN.Path} path Location
18
19
  * @param {CSN.Artifact} [dictionary]
@@ -60,6 +61,7 @@ function getUtils(model) {
60
61
  effectiveType,
61
62
  get$combined,
62
63
  getOrigin,
64
+ getQueryPrimarySource,
63
65
  };
64
66
 
65
67
  /**
@@ -175,10 +177,10 @@ function getUtils(model) {
175
177
  }
176
178
  }
177
179
 
178
- /**
179
- * Return the name part of the artifact name - no namespace etc.
180
- * @param {string|object} name Absolute name of the artifact
181
- */
180
+ /**
181
+ * Return the name part of the artifact name - no namespace etc.
182
+ * @param {string|object} name Absolute name of the artifact
183
+ */
182
184
  function getBaseName(name) {
183
185
  if (!name)
184
186
  return name;
@@ -190,6 +192,27 @@ function getUtils(model) {
190
192
  }
191
193
  }
192
194
 
195
+ /**
196
+ * Return the left-most, primary source of the given query.
197
+ * @param {*} query Definition's query object
198
+ */
199
+ function getQueryPrimarySource(query) {
200
+ if (!query)
201
+ return undefined;
202
+ else if (query.SELECT) {
203
+ return getQueryPrimarySource(query.SELECT);
204
+ } else if (query.SET) {
205
+ return getQueryPrimarySource(query.SET);
206
+ } else if (query.from) {
207
+ return getQueryPrimarySource(query.from);
208
+ } else if (query.ref) {
209
+ return query;
210
+ } else if (query.args) {
211
+ return getQueryPrimarySource(query.args[0]);
212
+ }
213
+ return undefined;
214
+ }
215
+
193
216
 
194
217
  /**
195
218
  * Create an object to track visited objects identified by a unique string.
@@ -229,7 +252,7 @@ function getUtils(model) {
229
252
  * Returns true if an artifact is a structured type
230
253
  * or a typedef of a structured type.
231
254
  *
232
- * @param {CSN.Artifact} obj
255
+ * @param {object} obj
233
256
  */
234
257
  function isStructured(obj) {
235
258
  return obj.elements ||
@@ -396,7 +419,7 @@ function getUtils(model) {
396
419
  function getServiceName(artifactName) {
397
420
  for(;;) {
398
421
  let idx = artifactName.lastIndexOf('.');
399
- if (idx == -1) return null;
422
+ if (idx === -1) return null;
400
423
  artifactName = artifactName.substring(0, idx);
401
424
  let artifact = model.definitions[artifactName];
402
425
  if (artifact && artifact.kind === 'service') {
@@ -457,8 +480,8 @@ function getUtils(model) {
457
480
  return resultNode;
458
481
  }
459
482
  }
460
-
461
-
483
+
484
+
462
485
  /**
463
486
  * Resolve to the final type of a type, that means follow type chains, references to other types or
464
487
  * elements a.s.o
@@ -497,12 +520,12 @@ function getUtils(model) {
497
520
  * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
498
521
  * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
499
522
  * no type (e.g. expr in a view without explicit type) returns 'null'
500
- *
523
+ *
501
524
  * @param {string|object} type Type - either string or ref
502
- * @param {CSN.Path} path
525
+ * @param {CSN.Path} path
503
526
  * @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
504
- * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
505
- * @returns
527
+ * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
528
+ * @returns
506
529
  */
507
530
  function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
508
531
  if (!type)
@@ -552,16 +575,6 @@ function getUtils(model) {
552
575
  }
553
576
  }
554
577
 
555
- // Tell if a type is (directly) a builtin type
556
- // Note that in CSN builtins are not in the definition of the model, so we can only check against their absolute names.
557
- // Builtin types are "cds.<something>", i.e. they are directly in 'cds', but not for example
558
- // in 'cds.foundation'. Also note, that a type might be a ref object, that refers to something else,
559
- // so if you consider type chains don't forget first to resolve to the final type before
560
- function isBuiltinType(type) {
561
- return typeof(type) === 'string' && type.startsWith('cds.') && !type.startsWith('cds.foundation.')
562
- }
563
-
564
-
565
578
  /**
566
579
  * Deeply clone the given CSN model and return it.
567
580
  * In testMode (or with testSortCsn), definitions are sorted.
@@ -817,11 +830,25 @@ function forAllQueries(query, callback, path = []){
817
830
  }
818
831
  }
819
832
 
820
- function forAllElements(artifact, artifactName, cb){
833
+ function forAllElements(artifact, artifactName, cb, includeActions = false){
821
834
  if(artifact.elements) {
822
835
  cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
823
836
  }
824
837
 
838
+ if(includeActions && artifact.actions) {
839
+ Object.entries(artifact.actions).forEach( ([actionName, action]) => {
840
+ const path = ['definitions', artifactName, 'actions', actionName];
841
+ if(action.params) {
842
+ Object.entries(action.params).forEach( ([paramName, param]) => {
843
+ if(param.elements)
844
+ cb(param, param.elements, path.concat(['params', paramName, 'elements']));
845
+ });
846
+ }
847
+ if(action.returns && action.returns.elements)
848
+ cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
849
+ });
850
+ }
851
+
825
852
  if(artifact.query) {
826
853
  forAllQueries(artifact.query, (q, p) => {
827
854
  const s = q.SELECT;
@@ -854,11 +881,14 @@ function forAllElements(artifact, artifactName, cb){
854
881
  * @param {CSN.Artifact} artifact
855
882
  * @param {string} annotationName Name of the annotation (including the at-sign)
856
883
  * @param {any} expected
884
+ * @param {boolean} caseInsensitive
857
885
  * @returns {boolean}
858
886
  */
859
- function hasAnnotationValue(artifact, annotationName, expected = true) {
887
+ function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
860
888
  if(expected === false)
861
889
  return artifact[annotationName] === expected || artifact[annotationName] === null;
890
+ else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
891
+ return artifact[annotationName].toLowerCase() === expected.toLowerCase();
862
892
  else
863
893
  return artifact[annotationName] === expected;
864
894
  }
@@ -871,7 +901,7 @@ function hasAnnotationValue(artifact, annotationName, expected = true) {
871
901
  * function accepts EDM internal and external options
872
902
  *
873
903
  * @param {CSN.Element} elementCsn
874
- * @param {CSN.Options & CSN.ODataOptions} options EDM specific options
904
+ * @param {ODataOptions} options EDM specific options
875
905
  */
876
906
  function isEdmPropertyRendered(elementCsn, options) {
877
907
  if(options.toOdata)
@@ -921,10 +951,10 @@ function getArtifactDatabaseNameOf(artifactName, namingConvention, csn) {
921
951
  throw new Error('Unknown naming convention: ' + namingConvention);
922
952
  }
923
953
  else {
924
- const namespace = csn;
925
954
  console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
926
955
  if (namingConvention === 'hdbcds') {
927
- if (namespace) {
956
+ if (csn) {
957
+ const namespace = String(csn);
928
958
  return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
929
959
  }
930
960
  return artifactName;
@@ -1048,112 +1078,6 @@ function getElementDatabaseNameOf(elemName, namingConvention) {
1048
1078
  }
1049
1079
  }
1050
1080
 
1051
-
1052
- /**
1053
- * Loop through the model, applying the custom transformations on the node's matching.
1054
- *
1055
- * Each transformer gets:
1056
- * - the parent having the property
1057
- * - the name of the property
1058
- * - the value of the property
1059
- * - the path to the property
1060
- *
1061
- * @param {object} csn CSN to enrich in-place
1062
- * @param {object} customTransformers Map of prop to transform and function to apply
1063
- * @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
1064
- * @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
1065
- * @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
1066
- * @returns {object} CSN with transformations applied
1067
- */
1068
- function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
1069
- const transformers = {
1070
- elements: dictionary,
1071
- definitions: dictionary,
1072
- actions: dictionary,
1073
- params: dictionary,
1074
- enum: dictionary,
1075
- mixin: dictionary,
1076
- ref: pathRef,
1077
- //type: simpleRef,
1078
- //target: simpleRef,
1079
- //includes: simpleRef,
1080
- }
1081
-
1082
- const csnPath = [];
1083
- if (csn.definitions)
1084
- definitions( csn, 'definitions', csn.definitions );
1085
- return csn;
1086
-
1087
- function standard( parent, prop, node ) {
1088
- if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ) || (typeof prop === 'string' && prop.startsWith('@')) || (skipIgnore && node._ignore))
1089
- return;
1090
-
1091
- csnPath.push( prop );
1092
-
1093
- if (Array.isArray(node)) {
1094
- node.forEach( (n, i) => standard( node, i, n ) );
1095
- }
1096
-
1097
- else {
1098
- for (let name of Object.getOwnPropertyNames( node )) {
1099
- const trans = transformers[name] || standard;
1100
- if(customTransformers[name])
1101
- customTransformers[name](node, name, node[name], csnPath, parent, prop);
1102
-
1103
- trans( node, name, node[name], csnPath );
1104
- }
1105
- }
1106
- csnPath.pop();
1107
- }
1108
-
1109
- function dictionary( node, prop, dict ) {
1110
- // Allow skipping dicts like actions in forHanaNew
1111
- if(options.skipDict && options.skipDict[prop])
1112
- return;
1113
- csnPath.push( prop );
1114
- for (let name of Object.getOwnPropertyNames( dict )) {
1115
- standard( dict, name, dict[name] );
1116
- }
1117
- if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
1118
- setProp(node, '$' + prop, dict);
1119
- csnPath.pop();
1120
- }
1121
-
1122
- function definitions( node, prop, dict ) {
1123
- csnPath.push( prop );
1124
- for (let name of Object.getOwnPropertyNames( dict )) {
1125
- const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
1126
- if(!skip) {
1127
- artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
1128
- standard( dict, name, dict[name] );
1129
- }
1130
- }
1131
- if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
1132
- setProp(node, '$' + prop, dict);
1133
- csnPath.pop();
1134
- }
1135
-
1136
- //Keep looping through the pathRef
1137
- function pathRef( node, prop, path ) {
1138
- csnPath.push( prop );
1139
- path.forEach( function step( s, i ) {
1140
- if (s && typeof s === 'object') {
1141
- csnPath.push( i );
1142
- if(options.drillRef) {
1143
- standard(path, i, s);
1144
- } else {
1145
- if (s.args)
1146
- standard( s, 'args', s.args );
1147
- if (s.where)
1148
- standard( s, 'where', s.where );
1149
- }
1150
- csnPath.pop();
1151
- }
1152
- } );
1153
- csnPath.pop();
1154
- }
1155
- }
1156
-
1157
1081
  const _dependencies = Symbol('_dependencies');
1158
1082
  const _dependents = Symbol('_dependents');
1159
1083
 
@@ -1333,38 +1257,6 @@ function mergeOptions(...optionsObjects) {
1333
1257
  }
1334
1258
  }
1335
1259
 
1336
- // Return the name of the top-level artifact surrounding the artifact 'name'
1337
- // in 'model'.
1338
- // We define "top-level artifact" to be an artifact that has either no parent or only
1339
- // ancestors of kind 'namespace'. Note that it is possible for a non-top-level artifact
1340
- // to have a namespace as parent and e.g. a context as grandparent (weird but true).
1341
- // Will return the artifact 'name' if it is a top-level artifact itself, and 'undefined'
1342
- // if there is no artifact surrounding 'name' in the model
1343
- // TODO: to be checked by author: still intended behaviour with 'cds' prefix?
1344
- // TODO: Can this be replaced by getRootArtifactName? Or maybe not rely on namespace-hacking...
1345
- // FIXME: This only works with namespace-hacking, i.e. adding them as artifacts...
1346
- function getTopLevelArtifactNameOf(name, model) {
1347
- let dotIdx = name.indexOf('.');
1348
- if (dotIdx == -1) {
1349
- // No '.' in the name, i.e. no parent - this is a top-level artifact (if it exists)
1350
- return model.definitions[name] ? name : undefined;
1351
- }
1352
- // If the first name part is not in the model, there is nothing to find
1353
- if (!model.definitions[name.substring(0, dotIdx)]) {
1354
- return undefined;
1355
- }
1356
- // Skip forward through '.'s until finding a non-namespace
1357
- while (dotIdx != -1 && (!model.definitions[name.substring(0, dotIdx)] || model.definitions[name.substring(0, dotIdx)].kind === 'namespace')) {
1358
- dotIdx = name.indexOf('.', dotIdx + 1);
1359
- }
1360
- if (dotIdx == -1) {
1361
- // This is a top-level artifact
1362
- return name;
1363
- }
1364
- // The skipped part of 'name' is the top-level artifact name
1365
- return name.substring(0, dotIdx);
1366
- }
1367
-
1368
1260
  /**
1369
1261
  * If the artifact with the name given is part of a context (or multiple), return the top-most context.
1370
1262
  * Else, return the artifact itself. Namespaces are not of concern here.
@@ -1532,7 +1424,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1532
1424
  if (!options.testMode)
1533
1425
  return;
1534
1426
  const sorted = Object.create(null);
1535
- Object.keys(csn.definitions).sort().forEach((name) => {
1427
+ Object.keys(csn.definitions || {}).sort().forEach((name) => {
1536
1428
  sorted[name] = csn.definitions[name];
1537
1429
  });
1538
1430
  csn.definitions = sorted;
@@ -1542,7 +1434,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1542
1434
  * Return an array of non-abstract service names contained in CSN
1543
1435
  *
1544
1436
  * @param {CSN.Model} csn
1545
- * @returns {CSN.Service[]}
1437
+ * @returns {string[]}
1546
1438
  */
1547
1439
  function getServiceNames(csn) {
1548
1440
  let result = [];
@@ -1556,8 +1448,8 @@ function getServiceNames(csn) {
1556
1448
 
1557
1449
  /**
1558
1450
  * Check wether the artifact is @cds.persistence.skip
1559
- *
1560
- * @param {CSN.Artifact} artifact
1451
+ *
1452
+ * @param {CSN.Artifact} artifact
1561
1453
  * @returns {Boolean}
1562
1454
  */
1563
1455
  function isSkipped(artifact) {
@@ -1566,21 +1458,49 @@ function isSkipped(artifact) {
1566
1458
 
1567
1459
  /**
1568
1460
  * Walk path in the CSN and return the result.
1569
- *
1570
- * @param {CSN.Model} csn
1571
- * @param {CSN.Path} path
1461
+ *
1462
+ * @param {CSN.Model} csn
1463
+ * @param {CSN.Path} path
1572
1464
  * @returns {object} Whatever is at the end of path
1573
1465
  */
1574
1466
  function walkCsnPath(csn, path) {
1575
1467
  /** @type {object} */
1576
1468
  let obj = csn;
1577
- for(let i = 0; i < path.length; i++){
1578
- obj = obj[path[i]];
1469
+ for(const segment of path){
1470
+ obj = obj[segment];
1579
1471
  }
1580
1472
 
1581
1473
  return obj;
1582
1474
  }
1583
1475
 
1476
+ /**
1477
+ * If provided, get the replacement string for the given magic variable ref.
1478
+ * No validation is done that the ref is actually magic!
1479
+ *
1480
+ * @param {array} ref
1481
+ * @param {CSN.Options} options
1482
+ * @returns {string|null}
1483
+ */
1484
+ function getVariableReplacement(ref, options) {
1485
+ if(options && options.variableReplacements) {
1486
+ let replacement = options.variableReplacements;
1487
+ for(const segment of ref) {
1488
+ replacement = replacement[segment];
1489
+ if(replacement === undefined)
1490
+ return null;
1491
+ }
1492
+
1493
+ if(replacement === undefined)
1494
+ return null; // no valid replacement found
1495
+ else if(typeof replacement === 'string')
1496
+ return replacement; // valid replacement
1497
+ else
1498
+ return null; // $user.foo, but we only have configured $user.foo.bar -> error
1499
+ } else {
1500
+ return null;
1501
+ }
1502
+ }
1503
+
1584
1504
  module.exports = {
1585
1505
  getUtils,
1586
1506
  cloneCsn,
@@ -1601,12 +1521,12 @@ module.exports = {
1601
1521
  getUnderscoredName,
1602
1522
  getElementDatabaseNameOf,
1603
1523
  applyTransformations,
1524
+ applyTransformationsOnNonDictionary,
1604
1525
  setDependencies,
1605
1526
  isPersistedOnDatabase,
1606
1527
  generatedByCompilerVersion,
1607
1528
  getNormalizedQuery,
1608
1529
  mergeOptions,
1609
- getTopLevelArtifactNameOf,
1610
1530
  getRootArtifactName,
1611
1531
  getLastPartOfRef,
1612
1532
  getParentNamesOf,
@@ -1621,4 +1541,5 @@ module.exports = {
1621
1541
  getServiceNames,
1622
1542
  isSkipped,
1623
1543
  walkCsnPath,
1544
+ getVariableReplacement
1624
1545
  };
@@ -7,23 +7,35 @@
7
7
 
8
8
  // * `File.cds:3:5` if the original CSN has a non-enumerable `$location` property
9
9
  // with value `{file: "File.cds", line: 3, col: 5}`.
10
- // * `File.cds:3:5-1` if the original CSN has _no_ `$location` property, for an
10
+ // * `File.cds:3:5^` if the original CSN has _no_ `$location` property, for an
11
11
  // inferred member of a main artifact or member with `$location: `File.cds:3:5`;
12
- // the number of digits in the `-1` suffix is the member depth.
12
+ // the number of `^`s in the suffix is the member depth.
13
13
 
14
14
  // Other enumerable properties in the JSON for non-enumerable properties in the
15
15
  // original CSN:
16
16
 
17
- // * `$env` for the non-enumerable `$env` property in the original CSN.
18
- // * `$elements` for a non-enumerable `elements` property for sub queries.
17
+ // * `$parens`: the number of parentheses provided by the user around an expression
18
+ // or query if the number is different to the usual (mostly 0, sometimes 1).
19
+ // * `$elements` (in client-style CSN only) for a non-enumerable `elements` property
20
+ // for sub queries.
19
21
 
20
22
  // The following properties in the JSON represent the result of the CSN API
21
23
  // functions:
22
24
 
23
- // * `_type`, `_includes` and `_targets` have as values the `$locations` of the
25
+ // * `_type`, `_includes` and `_targets` have as values the `$location`s of the
24
26
  // referred artifacts which are returned by function `artifactRef`.
25
- // * `_links`, `_art` and `_scope` as sibling properties of `ref` have as values
26
- // the `$locations` of the artifacts/members returned by function `inspectRef`.
27
+ // * `_links` and `_art` as sibling properties of `ref` have as values the
28
+ // `$locations` of the artifacts/members returned by function `inspectRef` (`_art`
29
+ // for ref in `from` only, where it is different to the last item of `_links`).
30
+ // * `_scope` and `_env` as sibling properties of `ref` have (string) values,
31
+ // returned by function `inspectRef`, giving add/ info about the “ref base”.
32
+ // * `_origin` (in Universal CSN only) has as value the `$location` of the
33
+ // prototype returned by function getOrigin().
34
+ // * `_test.inspect.csnpath` as sibling property of `ref` has an object value
35
+ // with properties `_links` and `_scope` of a further `inspectRef` call;
36
+ // it is only called, with `[‹art›, ...‹csnpath›]` as argument, if `‹art›`,
37
+ // which is the referred artifact returned by the first `inspectRef` call,
38
+ // has an annotation `@$test.inspect.csnpath` with array value `‹csnpath›`.
27
39
 
28
40
  'use strict';
29
41
 
@@ -32,13 +44,14 @@ const { locationString } = require('../base/location');
32
44
 
33
45
  function enrichCsn( csn, options = {} ) {
34
46
  const transformers = {
35
- // $env: reveal,
36
47
  elements: dictionary,
37
48
  definitions: dictionary,
38
49
  actions: dictionary,
39
50
  params: dictionary,
40
51
  enum: dictionary,
41
52
  mixin: dictionary,
53
+ returns: definition,
54
+ items: definition,
42
55
  ref: pathRef,
43
56
  type: simpleRef,
44
57
  targetAspect: simpleRef,
@@ -81,10 +94,20 @@ function enrichCsn( csn, options = {} ) {
81
94
  csnPath.pop();
82
95
  }
83
96
 
97
+ function definition( parent, prop, obj ) {
98
+ // call getOrigin() before standard() to set implicit protos inside standard():
99
+ const origin = handleError( err => err ? err.toString() : getOrigin( obj ) );
100
+ standard( parent, prop, obj );
101
+ if (obj.$origin === undefined && !obj.type && origin != null)
102
+ obj._origin = refLocation( origin );
103
+ }
104
+
84
105
  function dictionary( parent, prop, dict ) {
106
+ if (!dict) // value null for inheritance interruption
107
+ return;
85
108
  csnPath.push( prop );
86
109
  for (let name of Object.getOwnPropertyNames( dict )) {
87
- standard( dict, name, dict[name] );
110
+ definition( dict, name, dict[name] );
88
111
  }
89
112
  if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
90
113
  parent['$'+prop] = dict;
@@ -92,10 +115,10 @@ function enrichCsn( csn, options = {} ) {
92
115
  }
93
116
 
94
117
  function refLocation( art ) {
95
- if (art)
118
+ if (art && typeof art === 'object')
96
119
  return art.$location || '<no location>';
97
120
  if (!options.testMode)
98
- return '<illegal link>';
121
+ return art || '<illegal link>';
99
122
  throw new Error( 'Undefined reference' );
100
123
  }
101
124
 
@@ -122,33 +145,21 @@ function enrichCsn( csn, options = {} ) {
122
145
  }
123
146
 
124
147
  function $origin( parent, prop, ref ) {
125
- if (options.testMode) {
126
- if (Array.isArray( ref )) // $origin: […], not $origin: {…}
148
+ handleError( err => {
149
+ if (err)
150
+ parent._origin = err.toString();
151
+ else if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […] / "short-ref"
127
152
  parent._origin = refLocation( getOrigin( parent ) );
128
- }
129
- else {
130
- try {
131
- if (Array.isArray( ref )) // $origin: […], not $origin: {…}
132
- parent._origin = refLocation( getOrigin( parent ) );
133
- } catch (e) {
134
- parent._origin = e.toString();
135
- }
136
- }
153
+ else if ( ref ) // $origin: {…}
154
+ standard( parent, prop, ref );
155
+ } );
137
156
  }
138
157
 
139
- function pathRef( parent, prop, path ) {
140
- const { links, art, scope, $env } = (() => {
141
- if (options.testMode)
142
- return inspectRef( csnPath );
143
- else {
144
- try {
145
- return inspectRef( csnPath );
146
- }
147
- catch (e) {
148
- return { scope: e.toString() };
149
- }
150
- }
151
- } )();
158
+ function pathRef( parent, prop, path, inspectionPath = csnPath ) {
159
+ const inspection = handleError( (err) => {
160
+ return (err) ? { scope: err.toString() } : inspectRef( inspectionPath );
161
+ });
162
+ const { links, art, scope, $env } = inspection;
152
163
  if (links)
153
164
  parent._links = links.map( l => refLocation( l.art ) );
154
165
  if (links && links[links.length-1].art !== art)
@@ -157,6 +168,15 @@ function enrichCsn( csn, options = {} ) {
157
168
  if ($env)
158
169
  parent._env = $env;
159
170
 
171
+ if (!prop) // recursive call for @$test.inspect.csnpath
172
+ return;
173
+ const testPath = art && art['@$test.inspect.csnpath'];
174
+ if (testPath && parent.ref) {
175
+ const further = {};
176
+ pathRef( further, null, null, [ inspection, ...testPath ] );
177
+ parent['_test.inspect.csnpath'] = further;
178
+ }
179
+
160
180
  csnPath.push( prop );
161
181
  path.forEach( function step( s, i ) {
162
182
  if (s && typeof s === 'object') {
@@ -171,10 +191,20 @@ function enrichCsn( csn, options = {} ) {
171
191
  csnPath.pop();
172
192
  }
173
193
 
174
- function _cache_debug( obj ) {
194
+ function handleError( callback ) {
195
+ if (options.testMode)
196
+ return callback();
197
+ try {
198
+ return callback();
199
+ } catch (err) {
200
+ return callback( err );
201
+ }
202
+ }
203
+
204
+ function _cache_debug( obj, subCache ) {
175
205
  if (options.enrichCsn !== 'DEBUG')
176
206
  return;
177
- const cache = __getCache_forEnrichCsnDebugging( obj );
207
+ const cache = subCache || __getCache_forEnrichCsnDebugging( obj );
178
208
  if (!cache)
179
209
  return;
180
210
  if (cache.$$objectNumber > 0) {
@@ -197,8 +227,20 @@ function enrichCsn( csn, options = {} ) {
197
227
  }
198
228
  else if (name[0] !== '$' || !Object.getPrototypeOf( val )) {
199
229
  // ‹name›: dictionary of CSN nodes,
200
- // ‹$name›: dictionary of cache values if no prototype,
201
- obj.$$cacheObject[name] = Object.keys( val ); // TODO: or dict?
230
+ // ‹$name›: dictionary of cache values if no prototype
231
+ if (name !== '$aliases') {
232
+ obj.$$cacheObject[name] = Object.keys( val ); // TODO: or dict?
233
+ }
234
+ else {
235
+ const sub = Object.create(null);
236
+ for (const n in val) {
237
+ const alias = val[n];
238
+ const c = {};
239
+ _cache_debug( c, alias );
240
+ sub[n] = c.$$cacheObject;
241
+ }
242
+ obj.$$cacheObject[name] = sub;
243
+ }
202
244
  }
203
245
  else if (Array.isArray( val )) {
204
246
  obj.$$cacheObject[name] = val.map( item => {
@@ -231,12 +273,11 @@ function setLocations( node, prop, loc ) {
231
273
  return;
232
274
  const isMember = artifactProperties.includes( prop );
233
275
  if (!isMember && node.$location) {
234
- const value = locationString( node.$location, true );
235
- reveal( node, '$location', value );
236
- loc = value + '-';
276
+ loc = locationString( node.$location, true );
277
+ reveal( node, '$location', loc );
237
278
  }
238
279
  else if (prop === true) {
239
- loc += '1';
280
+ loc += '^';
240
281
  node.$location = loc;
241
282
  }
242
283
  if (Array.isArray( node )) {