@sap/cds-compiler 3.5.2 → 3.6.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 (85) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/bin/cdsc.js +14 -6
  3. package/doc/CHANGELOG_ARCHIVE.md +10 -10
  4. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  5. package/lib/api/main.js +32 -55
  6. package/lib/api/options.js +1 -0
  7. package/lib/api/validate.js +5 -0
  8. package/lib/base/message-registry.js +104 -32
  9. package/lib/base/messages.js +277 -212
  10. package/lib/base/model.js +33 -22
  11. package/lib/base/optionProcessorHelper.js +9 -2
  12. package/lib/base/shuffle.js +50 -0
  13. package/lib/checks/actionsFunctions.js +37 -20
  14. package/lib/checks/foreignKeys.js +13 -6
  15. package/lib/checks/nonexpandableStructured.js +1 -2
  16. package/lib/checks/onConditions.js +21 -19
  17. package/lib/checks/parameters.js +1 -1
  18. package/lib/checks/queryNoDbArtifacts.js +2 -0
  19. package/lib/checks/types.js +16 -22
  20. package/lib/compiler/assert-consistency.js +31 -28
  21. package/lib/compiler/builtins.js +20 -4
  22. package/lib/compiler/checks.js +72 -63
  23. package/lib/compiler/define.js +396 -314
  24. package/lib/compiler/extend.js +55 -49
  25. package/lib/compiler/index.js +5 -0
  26. package/lib/compiler/populate.js +28 -11
  27. package/lib/compiler/propagator.js +2 -1
  28. package/lib/compiler/resolve.js +29 -20
  29. package/lib/compiler/shared.js +15 -10
  30. package/lib/compiler/utils.js +7 -7
  31. package/lib/edm/annotations/genericTranslation.js +51 -46
  32. package/lib/edm/annotations/preprocessAnnotations.js +39 -42
  33. package/lib/edm/csn2edm.js +69 -21
  34. package/lib/edm/edm.js +2 -2
  35. package/lib/edm/edmInboundChecks.js +6 -8
  36. package/lib/edm/edmPreprocessor.js +88 -80
  37. package/lib/edm/edmUtils.js +6 -15
  38. package/lib/gen/Dictionary.json +81 -13
  39. package/lib/gen/language.checksum +1 -1
  40. package/lib/gen/language.interp +2 -1
  41. package/lib/gen/languageParser.js +4680 -4484
  42. package/lib/inspect/inspectModelStatistics.js +2 -1
  43. package/lib/inspect/inspectPropagation.js +2 -1
  44. package/lib/json/from-csn.js +131 -78
  45. package/lib/json/to-csn.js +39 -23
  46. package/lib/language/antlrParser.js +0 -3
  47. package/lib/language/docCommentParser.js +7 -3
  48. package/lib/language/errorStrategy.js +3 -2
  49. package/lib/language/genericAntlrParser.js +96 -41
  50. package/lib/language/language.g4 +112 -128
  51. package/lib/language/multiLineStringParser.js +2 -1
  52. package/lib/main.d.ts +115 -2
  53. package/lib/main.js +16 -3
  54. package/lib/model/csnRefs.js +3 -3
  55. package/lib/model/csnUtils.js +109 -179
  56. package/lib/model/enrichCsn.js +13 -8
  57. package/lib/model/revealInternalProperties.js +4 -3
  58. package/lib/optionProcessor.js +23 -3
  59. package/lib/render/manageConstraints.js +11 -15
  60. package/lib/render/toCdl.js +144 -47
  61. package/lib/render/toHdbcds.js +22 -22
  62. package/lib/render/toRename.js +3 -4
  63. package/lib/render/toSql.js +29 -20
  64. package/lib/render/utils/delta.js +3 -1
  65. package/lib/render/utils/sql.js +3 -16
  66. package/lib/transform/db/associations.js +6 -6
  67. package/lib/transform/db/cdsPersistence.js +3 -3
  68. package/lib/transform/db/constraints.js +8 -8
  69. package/lib/transform/db/expansion.js +4 -4
  70. package/lib/transform/db/flattening.js +12 -15
  71. package/lib/transform/db/temporal.js +4 -3
  72. package/lib/transform/db/transformExists.js +2 -1
  73. package/lib/transform/draft/db.js +7 -7
  74. package/lib/transform/forOdataNew.js +15 -4
  75. package/lib/transform/forRelationalDB.js +53 -39
  76. package/lib/transform/odata/toFinalBaseType.js +106 -82
  77. package/lib/transform/odata/typesExposure.js +26 -17
  78. package/lib/transform/odata/utils.js +1 -1
  79. package/lib/transform/parseExpr.js +1 -1
  80. package/lib/transform/transformUtilsNew.js +33 -10
  81. package/lib/transform/translateAssocsToJoins.js +8 -7
  82. package/lib/transform/universalCsn/coreComputed.js +7 -5
  83. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
  84. package/lib/utils/timetrace.js +2 -2
  85. package/package.json +1 -2
@@ -8,7 +8,7 @@ const {
8
8
  cloneCsnDictionary: _cloneCsnDictionary,
9
9
  cloneAnnotationValue: _cloneAnnotationValue,
10
10
  } = require('../json/to-csn');
11
- const { ModelError } = require('../base/error');
11
+ const { ModelError, CompilerAssertion } = require('../base/error');
12
12
  const { typeParameters } = require('../compiler/builtins');
13
13
  const { forEach } = require('../utils/objectUtils');
14
14
  const { version } = require('../../package.json');
@@ -44,6 +44,7 @@ const { version } = require('../../package.json');
44
44
  * @param {CSN.Model} model (Compact) CSN model
45
45
  */
46
46
  function getUtils( model, universalReady ) {
47
+ const special$self = !model?.definitions?.$self && '$self';
47
48
  const _csnRefs = csnRefs(model, universalReady);
48
49
  const { artifactRef } = _csnRefs;
49
50
  /** Cache for getFinalBaseTypeWithProps(). Specific to the current model. */
@@ -53,13 +54,11 @@ function getUtils( model, universalReady ) {
53
54
  getCsnDef,
54
55
  isStructured,
55
56
  getFinalType,
56
- getFinalTypeDef,
57
57
  isManagedAssociation,
58
58
  isAssocOrComposition,
59
59
  isAssociation,
60
60
  isComposition,
61
61
  getArtifactDatabaseNameOf,
62
- getNamespaceOfArtifact,
63
62
  getContextOfArtifact,
64
63
  addStringAnnotationTo,
65
64
  getServiceName,
@@ -219,31 +218,6 @@ function getUtils( model, universalReady ) {
219
218
  return undefined;
220
219
  }
221
220
 
222
-
223
- /**
224
- * Create an object to track visited objects identified by a unique string.
225
- * @param {string} [initialId] Initial entry (optional)
226
- */
227
- function createVisited( initialId ) {
228
- const visited = Object.create(null);
229
- check(initialId);
230
- return { check };
231
-
232
- /**
233
- * Check if an identifier has already been visited and
234
- * add it to the list of visited identifiers.
235
- * @param {string} id unique identifier
236
- */
237
- function check( id ) {
238
- if (!id)
239
- return;
240
- if (visited[id])
241
- throw new ModelError('Circular dependency');
242
-
243
- visited[id] = true;
244
- }
245
- }
246
-
247
221
  /**
248
222
  * Get the CSN definition for an artifact name.
249
223
  * @param {string} defName Absolute name of the artifact
@@ -265,41 +239,15 @@ function getUtils( model, universalReady ) {
265
239
  return !!(obj.elements || (obj.type && getFinalBaseTypeWithProps(obj.type)?.elements));
266
240
  }
267
241
 
268
- /**
269
- * Resolves typedefs to its final typedef which is returned.
270
- * If the artifact for typename isn't a typedef, the name itself is returned.
271
- *
272
- * @param {string} typeName Absolute type name
273
- * @returns {object}
274
- */
275
- function getFinalTypeDef( typeName ) {
276
- const visited = createVisited(typeName);
277
- let type = model.definitions[typeName];
278
- if (!type)
279
- return typeName;
280
-
281
- for (let nextType = type; nextType;) {
282
- type = nextType;
283
- visited.check(type.type);
284
- nextType = model.definitions[nextType.type];
285
- }
286
- return type;
287
- }
288
-
289
242
  /**
290
243
  * Resolves typedefs to its final type (name) which is returned.
291
- * @param {string} typeName Absolute type name
292
- * @returns {string}
244
+ * @param {string|object} typeName Absolute type name or type ref ({ref: [...]}).
245
+ * @returns {string|object}
293
246
  */
294
247
  function getFinalType( typeName ) {
295
- const visited = createVisited(typeName);
296
- let type = model.definitions[typeName];
297
- while (type && type.type) {
298
- typeName = type.type;
299
- visited.check(typeName);
300
- type = model.definitions[typeName];
301
- }
302
- return typeName;
248
+ if (!typeName)
249
+ return typeName;
250
+ return getFinalBaseTypeWithProps(typeName)?.type || null;
303
251
  }
304
252
 
305
253
  // Return true if 'node' is a managed association element
@@ -309,77 +257,33 @@ function getUtils( model, universalReady ) {
309
257
  }
310
258
 
311
259
  /**
312
- * Returns if a type is an association or a composition or a typedef
313
- * to any of them.
260
+ * Returns if a type is an association or a composition (possibly via type chain).
261
+ *
314
262
  * @param {string} typeName Absolute type name
315
263
  */
316
264
  function isAssocOrComposition( typeName ) {
317
- if (typeName === 'cds.Association' || typeName === 'cds.Composition')
318
- return true;
319
- const visited = createVisited(typeName);
320
- let type = model.definitions[typeName];
321
- while (type) {
322
- if (type.type === 'cds.Association' || type.type === 'cds.Composition')
323
- return true;
324
- visited.check(type.type);
325
- type = model.definitions[type.type];
326
- }
327
- return false;
265
+ const finalType = getFinalType( typeName );
266
+ return (finalType === 'cds.Association' || finalType === 'cds.Composition');
328
267
  }
329
268
 
330
269
  /**
331
- * Returns if a type is an association or a typedef to it.
270
+ * Returns true if a type is an association (possibly via type chain).
271
+ *
332
272
  * @param {string} typeName Absolute type name
333
273
  */
334
274
  function isAssociation( typeName ) {
335
- if (typeName === 'cds.Association')
336
- return true;
337
- const visited = createVisited(typeName);
338
- let type = model.definitions[typeName];
339
- while (type) {
340
- if (type.type === 'cds.Association')
341
- return true;
342
- visited.check(type.type);
343
- type = model.definitions[type.type];
344
- }
345
- return false;
275
+ const finalType = getFinalType( typeName );
276
+ return (finalType === 'cds.Association');
346
277
  }
347
278
 
348
279
  /**
349
- * Returns if a type is an composition or a typedef to it.
280
+ * Returns if a type is a composition (possibly via type chain).
281
+ *
350
282
  * @param {string} typeName Absolute type name
351
283
  */
352
284
  function isComposition( typeName ) {
353
- if (typeName === 'cds.Composition')
354
- return true;
355
- const visited = createVisited(typeName);
356
- let type = model.definitions[typeName];
357
- while (type) {
358
- if (type.type === 'cds.Composition')
359
- return true;
360
- visited.check(type.type);
361
- type = model.definitions[type.type];
362
- }
363
- return false;
364
- }
365
-
366
- /**
367
- * Return the namespace part of the artifact name.
368
- * @param {string} name Absolute name of artifact
369
- */
370
- function getNamespaceOfArtifact( name ) {
371
- let lastDotIdx = name.lastIndexOf('.');
372
- if (lastDotIdx === -1)
373
- return undefined;
374
- while (model.definitions[name]) {
375
- if (model.definitions[name].kind === 'namespace')
376
- return name;
377
- lastDotIdx = name.lastIndexOf('.');
378
- if (lastDotIdx === -1)
379
- return undefined;
380
- name = name.substring(0, lastDotIdx);
381
- }
382
- return name;
285
+ const finalType = getFinalType( typeName );
286
+ return (finalType === 'cds.Composition');
383
287
  }
384
288
 
385
289
  /**
@@ -409,7 +313,7 @@ function getUtils( model, universalReady ) {
409
313
  function addStringAnnotationTo( absoluteName, theValue, node ) {
410
314
  // Sanity check
411
315
  if (!absoluteName.startsWith('@'))
412
- throw Error(`Annotation name should start with "@": ${ absoluteName }`);
316
+ throw new CompilerAssertion(`Annotation name should start with "@": ${ absoluteName }`);
413
317
 
414
318
  // Only overwrite if undefined or null
415
319
  if (node[absoluteName] === undefined || node[absoluteName] === null) {
@@ -488,12 +392,6 @@ function getUtils( model, universalReady ) {
488
392
  }
489
393
  }
490
394
 
491
- function _normalizeTypeRef( type ) {
492
- if (type && typeof type === 'object' && type.ref?.length === 1)
493
- type = type.ref[0]; // simplify type: no element -> simple string can be used
494
- return type;
495
- }
496
-
497
395
  /**
498
396
  * Resolve to the final type of a type, that means follow type chains, references, etc.
499
397
  * Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
@@ -501,30 +399,32 @@ function getUtils( model, universalReady ) {
501
399
  * Returns `null` if the type can't be resolved or if the referenced element has no type,
502
400
  * e.g. `typeof V:calculated`.
503
401
  * Otherwise, if scalar, returns an object that has a `type` property and all collected type
504
- * properties, or the type object with `elements` or `items` property if structured/arrayed.
402
+ * properties, or an object with `type` and `elements` or `items` property if structured/arrayed.
505
403
  *
506
404
  * Notes:
507
405
  * - Caches type lookups. If the CSN changes drastically, you will need to re-call
508
- * `csnUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
509
- * - Does _not_ return the underlying type definition! It is an object with all relevant
406
+ * `getUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
407
+ * - Does _not_ return the underlying type definition! It is an object with all relevant
510
408
  * type properties collected while traversing the type chain!
511
409
  *
410
+ * @todo Rename to e.g. getFinalTypeInfo()
411
+ *
512
412
  * @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
513
413
  * @returns {object|null}
514
414
  */
515
415
  function getFinalBaseTypeWithProps( type ) {
516
- type = _normalizeTypeRef(type);
416
+ type = normalizeTypeRef(type);
517
417
  if (!type)
518
418
  return null;
519
419
 
520
- // Nothing to copy from builtin type name.
521
- if (typeof type === 'string' && isBuiltinType( type ))
522
- return { type };
523
-
524
-
525
420
  // We differentiate between ref and type to avoid collisions due to dict key.
526
421
  // Delimiter chosen arbitrarily; just one that is rarely used.
527
- const resolvedKey = (typeof type === 'object') ? `ref[${ type.ref.length }]:${ type.ref.join('\\') }` : `type:${ type }`;
422
+ // Need to take care of clashes:
423
+ // - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\', '\\'] })
424
+ // - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\2:\\\\'] })
425
+ const resolvedKey = (typeof type === 'object')
426
+ ? `ref[${ type.ref.length }]:${ type.ref.map((val, i) => `${ i }:${ val }`).join('\\') }`
427
+ : `type:${ type }`;
528
428
 
529
429
  if (finalBaseTypeCache[resolvedKey]) {
530
430
  if (finalBaseTypeCache[resolvedKey] === true)
@@ -532,8 +432,12 @@ function getUtils( model, universalReady ) {
532
432
  return finalBaseTypeCache[resolvedKey];
533
433
  }
534
434
 
435
+ // Nothing to copy from builtin type name.
436
+ if (typeof type === 'string' && (isBuiltinType( type ) || type === special$self))
437
+ return _cacheResolved({ type });
438
+
535
439
  const typeRef = artifactRef(type); // throws if not found
536
- const isNonScalar = _cacheNonScalar(typeRef);
440
+ const isNonScalar = _cacheNonScalar({ ...typeRef, type });
537
441
  if (isNonScalar)
538
442
  return finalBaseTypeCache[resolvedKey];
539
443
 
@@ -541,7 +445,8 @@ function getUtils( model, universalReady ) {
541
445
  _copyTypeProps(props, typeRef);
542
446
 
543
447
  // If the resolved type is a builtin, stop and use its type arguments.
544
- type = _normalizeTypeRef(typeRef.type);
448
+ type = normalizeTypeRef(typeRef.type);
449
+ props.type = type;
545
450
  if (typeof type === 'string' && isBuiltinType(type))
546
451
  return _cacheResolved(props);
547
452
 
@@ -552,11 +457,13 @@ function getUtils( model, universalReady ) {
552
457
  const finalBase = getFinalBaseTypeWithProps(type);
553
458
  if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
554
459
  return _cacheResolved(null);
460
+
555
461
  const nonScalar = _cacheNonScalar(finalBase);
556
462
  if (nonScalar)
557
463
  return finalBaseTypeCache[resolvedKey];
558
464
 
559
465
  // If not a non-scalar, must be resolved type.
466
+ props.type = finalBase.type;
560
467
  _copyTypeProps(props, finalBase);
561
468
  _cacheResolved(props);
562
469
  return props;
@@ -578,10 +485,6 @@ function getUtils( model, universalReady ) {
578
485
  * @returns {boolean} True, if structured/arrayed/invalid, false if scalar.
579
486
  */
580
487
  function _cacheNonScalar( obj ) {
581
- if (!obj) { // Reference has no proper type, e.g. due to `type of View:calculated`.
582
- _cacheResolved(null);
583
- return true;
584
- }
585
488
  if (obj.elements || obj.items) {
586
489
  _cacheResolved(obj);
587
490
  return true;
@@ -690,20 +593,10 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
690
593
  if (ignoreIgnore && construct._ignore)
691
594
  return;
692
595
 
693
-
694
596
  // `items` itself is a structure that can contain "elements", and more.
695
597
  if (construct.items)
696
598
  forEachMember( construct.items, callback, [ ...path, 'items' ], ignoreIgnore, iterateOptions, constructCallback );
697
599
 
698
-
699
- // Unlike XSN, we don't make "returns" a "params" in the callback.
700
- // Backends rely on the fact that `forEachElement` also goes through all
701
- // `elements` of the return type (if structured).
702
- // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
703
- if (construct.returns && !iterateOptions.elementsOnly)
704
- forEachMember( construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback );
705
-
706
-
707
600
  path = [ ...path ]; // Copy
708
601
  const propsWithMembers = (iterateOptions.elementsOnly ? [ 'elements' ] : [ 'elements', 'enum', 'actions', 'params' ]);
709
602
  propsWithMembers.forEach((prop) => {
@@ -715,6 +608,19 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
715
608
  constructCallback(construct, prop, path);
716
609
  }
717
610
  });
611
+
612
+ if (construct.returns) {
613
+ if (construct.returns.items || construct.returns.elements) {
614
+ if (!iterateOptions.elementsOnly)
615
+ forEachMember(construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback);
616
+ }
617
+ else if (Array.isArray(callback)) {
618
+ callback.forEach(cb => cb(construct.returns, '', 'params', [ ...path, 'returns' ], construct));
619
+ }
620
+ else {
621
+ callback(construct.returns, '', 'params', [ ...path, 'returns' ], construct);
622
+ }
623
+ }
718
624
  }
719
625
 
720
626
  /**
@@ -965,10 +871,10 @@ function getArtifactDatabaseNameOf( artifactName, sqlMapping, csn, sqlDialect =
965
871
  else if (sqlMapping === 'plain')
966
872
  return artifactName.replace(/\./g, '_').toUpperCase();
967
873
 
968
- throw new Error(`Unknown naming mode: ${ sqlMapping }`);
874
+ throw new CompilerAssertion(`Unknown naming mode: ${ sqlMapping }`);
969
875
  }
970
876
  else {
971
- throw new Error('A valid CSN model is required to correctly calculate the database name of an artifact.');
877
+ throw new CompilerAssertion('A valid CSN model is required to correctly calculate the database name of an artifact.');
972
878
  }
973
879
  }
974
880
 
@@ -1054,7 +960,7 @@ function getUnderscoredName( startIndex, parts, csn ) {
1054
960
 
1055
961
  function isValidMappingDialectCombi( sqlDialect, sqlMapping ) {
1056
962
  if (sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
1057
- throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${ sqlDialect }`);
963
+ throw new CompilerAssertion(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${ sqlDialect }`);
1058
964
  return true;
1059
965
  }
1060
966
 
@@ -1087,7 +993,7 @@ function getElementDatabaseNameOf( elemName, sqlMapping, sqlDialect = 'plain' )
1087
993
  else if (sqlMapping === 'quoted')
1088
994
  return elemName.replace(/\./g, '_');
1089
995
 
1090
- throw new Error(`Unknown naming mode: ${ sqlMapping }`);
996
+ throw new CompilerAssertion(`Unknown naming mode: ${ sqlMapping }`);
1091
997
  }
1092
998
 
1093
999
  const _dependencies = Symbol('_dependencies');
@@ -1220,6 +1126,19 @@ function getNormalizedQuery( art ) {
1220
1126
  return art;
1221
1127
  }
1222
1128
 
1129
+ /**
1130
+ * If `art.type` is an object with `ref` of length 1, normalize it to
1131
+ * just the type reference string, e.g. `{ ref: [ 'T'] }` becomes `'T'`.
1132
+ *
1133
+ * @param {string|object} type
1134
+ * @return {string|object}
1135
+ */
1136
+ function normalizeTypeRef( type ) {
1137
+ if (type && typeof type === 'object' && type.ref?.length === 1)
1138
+ type = type.ref[0]; // simplify type: no element -> simple string can be used
1139
+ return type;
1140
+ }
1141
+
1223
1142
  /**
1224
1143
  * If the artifact with the name given is part of a context (or multiple), return the top-most context.
1225
1144
  * Else, return the artifact itself. Namespaces are not of concern here.
@@ -1390,36 +1309,46 @@ function hasValidSkipOrExists( artifact ) {
1390
1309
  }
1391
1310
 
1392
1311
  /**
1393
- * Get the namespace part of the artifact name - not the whole prefix, just the part caused by namespaces.
1394
- *
1395
- * @param {CSN.Model} csn CSN model
1396
- * @param {string} artifactName artifact name to get the namespace for
1397
- * @returns {string | null} The namespace name
1398
- */
1399
- function getNamespace( csn, artifactName ) {
1400
- const parts = artifactName.split('.');
1401
- let seen = parts[0];
1402
- const art = csn.definitions[seen];
1403
-
1404
- // First step is not a namespace (we faked those in the CSN)
1405
- // No subsequent step can be a namespace then
1406
- if (art && art.kind !== 'namespace')
1407
- return null;
1408
-
1409
-
1410
- for (let i = 1; i < parts.length; i++) {
1411
- // This was definitely a namespace so far
1412
- const previousArtifactName = seen;
1413
- seen = `${ seen }.${ parts[i] }`;
1414
- // This might not be - if it isn't, return the result.
1415
- const currentArtifact = csn.definitions[seen];
1416
- if (currentArtifact && currentArtifact.kind !== 'namespace')
1417
- return previousArtifactName;
1312
+ * Return the namespace part of the artifact name.
1313
+ *
1314
+ * Return the longest undefined prefix path.
1315
+ * If the first path segment is defined (that is name has
1316
+ * no dots and is defined), return undefined.
1317
+ *
1318
+ * Example:
1319
+ * model.definitions {
1320
+ * a.b: 1,
1321
+ * a.b.c.d: 1
1322
+ * }
1323
+ * getNamespace('foo.bar') = 'foo.bar'
1324
+ * getNamespace('a.b.c.d') = 'a'
1325
+ *
1326
+ * model.definitions {
1327
+ * a: 1,
1328
+ * a.b.c.d: 1
1329
+ * }
1330
+ * getNamespace('a.b.c.d') = undefined
1331
+ *
1332
+ * @param {string} name Absolute name of artifact
1333
+ * @param {CSN.Model} csn CSN model
1334
+ * @param {any} ns default namespace return value
1335
+ * @returns {any} The namespace or the value of ns
1336
+ */
1337
+ function getNamespace( csn, name, ns = undefined ) {
1338
+ let dotpos = -1;
1339
+ while (dotpos < name.length) {
1340
+ for (dotpos++; dotpos < name.length && name[dotpos] !== '.'; dotpos++)
1341
+ ;
1342
+ const tns = name.substring(0, dotpos);
1343
+ if (csn.definitions[tns] === undefined)
1344
+ ns = tns;
1345
+ else
1346
+ return ns;
1418
1347
  }
1419
- // We came till here - so the full artifactName is a namespace
1420
- return artifactName;
1348
+ return ns;
1421
1349
  }
1422
1350
 
1351
+
1423
1352
  /**
1424
1353
  * Sorts the definition dictionary in tests mode.
1425
1354
  *
@@ -1577,6 +1506,7 @@ module.exports = {
1577
1506
  getRootArtifactName,
1578
1507
  getLastPartOfRef,
1579
1508
  getLastPartOf,
1509
+ normalizeTypeRef,
1580
1510
  copyAnnotations,
1581
1511
  copyAnnotationsAndDoc,
1582
1512
  isAspect,
@@ -41,6 +41,8 @@
41
41
 
42
42
  const { csnRefs, artifactProperties } = require('./csnRefs');
43
43
  const { locationString } = require('../base/location');
44
+ const { CompilerAssertion } = require('../base/error');
45
+ const shuffleGen = require('../base/shuffle');
44
46
 
45
47
  function enrichCsn( csn, options = {} ) {
46
48
  const transformers = {
@@ -64,6 +66,7 @@ function enrichCsn( csn, options = {} ) {
64
66
  // options.enrichCsn = 'DEBUG';
65
67
  let $$cacheObjectNumber = 0; // for debugging
66
68
  const debugLocationInfo = options.enrichCsn === 'DEBUG' && Object.create(null);
69
+ const special$self = !csn.definitions.$self && '$self';
67
70
 
68
71
  setLocations( csn, false, null );
69
72
  const {
@@ -74,6 +77,7 @@ function enrichCsn( csn, options = {} ) {
74
77
  // eslint-disable-next-line camelcase
75
78
  __getCache_forEnrichCsnDebugging,
76
79
  } = csnRefs( csn, true );
80
+ const { shuffleArray } = shuffleGen( options.testMode );
77
81
 
78
82
  const csnPath = [];
79
83
  if (csn.definitions)
@@ -93,7 +97,8 @@ function enrichCsn( csn, options = {} ) {
93
97
  obj.forEach( (n, i) => standard( obj, i, n ) );
94
98
  }
95
99
  else {
96
- for (const name of Object.getOwnPropertyNames( obj )) {
100
+ // no shuffle here; we would need to sort the new properties otherwise
101
+ for (const name of Object.getOwnPropertyNames( obj ) ) {
97
102
  const trans = transformers[name] || transformers[name.charAt(0)] || standard;
98
103
  trans( obj, name, obj[name] );
99
104
  }
@@ -116,13 +121,13 @@ function enrichCsn( csn, options = {} ) {
116
121
  if (!dict) // value null for inheritance interruption
117
122
  return;
118
123
  csnPath.push( prop );
119
- for (const name of Object.getOwnPropertyNames( dict )) {
124
+ for (const name of shuffleArray( Object.getOwnPropertyNames( dict ) )) {
120
125
  if (prop === 'definitions')
121
126
  initDefinition( dict[name] );
122
127
  definition( dict, name, dict[name] );
123
128
  }
124
129
  if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
125
- parent[`$${ prop }`] = dict;
130
+ parent[`$${ prop }`] = dict; // for $elements of sub queries in client-CSN
126
131
  csnPath.pop();
127
132
  }
128
133
 
@@ -133,7 +138,7 @@ function enrichCsn( csn, options = {} ) {
133
138
  ? `<illegal ref = ${ art }>`
134
139
  : `<illegal ref: ${ typeof art }>`;
135
140
  }
136
- throw new Error( 'Illegal reference' );
141
+ throw new CompilerAssertion( 'Illegal reference' );
137
142
  }
138
143
  else if (art.$location) {
139
144
  return art.$location;
@@ -141,7 +146,7 @@ function enrichCsn( csn, options = {} ) {
141
146
 
142
147
  if (!options.testMode)
143
148
  return `<${ Object.keys( art ).join('+') }+!$location>`;
144
- throw new Error( 'Reference to object without $location' );
149
+ throw new CompilerAssertion( 'Reference to object without $location' );
145
150
  }
146
151
 
147
152
  function simpleRef( parent, prop, ref ) {
@@ -151,7 +156,7 @@ function enrichCsn( csn, options = {} ) {
151
156
  parent[`_${ prop }`] = ref.map( r => refLocation( artifactRef( r, notFound ) ) );
152
157
  }
153
158
  else if (typeof ref === 'string') {
154
- if (!ref.startsWith( 'cds.'))
159
+ if (!ref.startsWith( 'cds.') && ref !== special$self)
155
160
  parent[`_${ prop }`] = refLocation( artifactRef( ref, notFound ) );
156
161
  }
157
162
  else if (!ref.elements) {
@@ -201,7 +206,7 @@ function enrichCsn( csn, options = {} ) {
201
206
  }
202
207
 
203
208
  csnPath.push( prop );
204
- path.forEach( function step( s, i ) {
209
+ path.forEach( function step( s, i ) { // no shuffle, need index
205
210
  if (s && typeof s === 'object') {
206
211
  csnPath.push( i );
207
212
  if (s.args)
@@ -289,7 +294,7 @@ function enrichCsn( csn, options = {} ) {
289
294
 
290
295
  function debugLocation( loc, userProvided ) {
291
296
  if (debugLocationInfo && !userProvided) {
292
- loc = loc.replace( /\([0-9]+\)\^/, '^' );
297
+ loc = loc.replace( /\(\d+\)\^/, '^' );
293
298
  debugLocationInfo[loc] = (debugLocationInfo[loc] || 0) + 1;
294
299
  loc = `${ loc }(${ debugLocationInfo[loc] })`;
295
300
  }
@@ -12,6 +12,7 @@
12
12
  'use strict';
13
13
 
14
14
  const msg = require('../base/messages');
15
+ const { CompilerAssertion } = require('../base/error');
15
16
 
16
17
  const $inferred = Symbol.for('cds.$inferred');
17
18
  const $location = Symbol.for('cds.$location');
@@ -132,7 +133,7 @@ function revealInternalProperties( model, nameOrPath ) {
132
133
  if (path.length === 1) {
133
134
  const def = xsn.definitions?.[path[0]] || xsn.vocabularies?.[path[0]];
134
135
  if (!def)
135
- throw new Error(`reveal xsn: Unknown definition: “${ path[0] }”`);
136
+ throw new CompilerAssertion(`reveal xsn: Unknown definition: “${ path[0] }”`);
136
137
  return reveal( def );
137
138
  }
138
139
 
@@ -143,7 +144,7 @@ function revealInternalProperties( model, nameOrPath ) {
143
144
  if (xsn[segment])
144
145
  xsn = xsn[segment];
145
146
  else if (segment) // huh, this should be a call error
146
- throw new Error(`Raw Output: Path segment "${ segment }" could not be found. Path: ${ JSON.stringify(path) }!"`);
147
+ throw new CompilerAssertion(`Raw Output: Path segment "${ segment }" could not be found. Path: ${ JSON.stringify(path) }!"`);
147
148
  }
148
149
  const propName = path[path.length > 1 ? path.length - 2 : 0];
149
150
  const obj = {};
@@ -369,7 +370,7 @@ function primOrString( node ) {
369
370
 
370
371
  function quoted( name, undef = '‹undefined›' ) {
371
372
  if (typeof name === 'number')
372
- return name;
373
+ return String(name);
373
374
  return (name ? `“${ name }”` : undef);
374
375
  }
375
376