@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
@@ -88,7 +88,7 @@
88
88
  // Sub Phase 2 (initXYZ)
89
89
  // - set _parent, _main (later: _service?) links, and _block links of members
90
90
  // - add _subArtifacts dictionary and "namespace artifacts" for name resolution
91
- // - duplicate checks (TODO - currently via preProcessArtifact in definer)
91
+ // - duplicate checks
92
92
  // - structure checks ?
93
93
  // - annotation assignments
94
94
  // - POST: resolvePath() can be called for artifact references (if complete model)
@@ -101,9 +101,11 @@
101
101
  // to avoid consequential or repeated errors.
102
102
  // - But: The same artifact is added to multiple dictionaries.
103
103
  // - Solution part 1: $duplicates as property of the artifact or member
104
- // for 'definitions', '_artifacts' and member dictionaries.
105
- // - Solution part 2: array value in dictionary for duplicates in CDL 'artifacts'
106
- // dictionary, also used for `_combined` in query search dictionary.
104
+ // for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
105
+ // dictionary of the whole model, `$tableAliases` dictionary of queries.
106
+ // - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
107
+ // dictionary, `_combined` dictionary for query search, `$tableAliases`
108
+ // of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
107
109
 
108
110
  'use strict';
109
111
 
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
111
113
  const {
112
114
  isDeprecatedEnabled, isBetaEnabled,
113
115
  setProp, forEachGeneric, forEachInOrder,
114
- forEachMember, forEachDefinition,
116
+ forEachDefinition,
115
117
  forEachMemberRecursivelyWithQuery,
116
118
  } = require('../base/model');
117
119
  const {
@@ -120,20 +122,21 @@ const {
120
122
  const {
121
123
  dictLocation,
122
124
  } = require('../base/location');
125
+ const { kindProperties, dictKinds } = require('./base');
123
126
  const {
124
- annotationVal, annotationIsFalse, annotateWith,
125
- } = require('./utils');
126
- const {
127
- dictKinds,
128
- kindProperties,
129
- fns,
127
+ annotationVal,
128
+ annotationIsFalse,
129
+ annotateWith,
130
130
  linkToOrigin,
131
131
  setMemberParent,
132
132
  storeExtension,
133
133
  dependsOnSilent,
134
- } = require('./shared');
134
+ pathName,
135
+ augmentPath,
136
+ splitIntoPath,
137
+ } = require('./utils');
135
138
  const { compareLayer, layer } = require('./moduleLayers');
136
- const { initBuiltins } = require('./builtins');
139
+ const { initBuiltins, isInReservedNamespace } = require('./builtins');
137
140
  const setLink = setProp;
138
141
 
139
142
  /**
@@ -148,7 +151,7 @@ const setLink = setProp;
148
151
  *
149
152
  * @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
150
153
  */
151
- function getDefinerFunctions( model ) {
154
+ function define( model ) {
152
155
  const { options } = model;
153
156
  // Get simplified "resolve" functionality and the message function:
154
157
  const {
@@ -160,24 +163,29 @@ function getDefinerFunctions( model ) {
160
163
  resolveTypeArguments,
161
164
  defineAnnotations,
162
165
  attachAndEmitValidNames,
163
- } = fns( model );
164
- const extensionsDict = Object.create(null);
165
- let addTextsLanguageAssoc = false;
166
-
167
- return {
168
- define,
166
+ } = model.$functions;
167
+ Object.assign( model.$functions, {
169
168
  initArtifact,
170
169
  lateExtensions,
171
170
  projectionAncestor,
172
171
  hasTruthyProp,
172
+ } );
173
+ // During the definer, we can only resolve artifact references, i.e,
174
+ // after a `.`, we only search in the `_subArtifacts` dictionary:
175
+ model.$volatileFunctions.environment = function artifactsEnv( art ) {
176
+ return art._subArtifacts || Object.create(null);
173
177
  };
174
178
 
179
+ const extensionsDict = Object.create(null);
180
+ let addTextsLanguageAssoc = false;
181
+ return doDefine();
182
+
175
183
  /**
176
184
  * Main function of the definer.
177
185
  *
178
186
  * @returns {XSN.Model}
179
187
  */
180
- function define() {
188
+ function doDefine() {
181
189
  if (options.deprecated &&
182
190
  messages.every( m => m.messageId !== 'api-deprecated-option' )) {
183
191
  warning( 'api-deprecated-option', {},
@@ -206,8 +214,6 @@ function getDefinerFunctions( model ) {
206
214
 
207
215
  if (options.parseCdl) {
208
216
  initExtensionsWithoutApplying();
209
- // Check for redefinitions
210
- Object.keys( model.definitions ).forEach( preProcessArtifact );
211
217
  // If no extensions shall be applied then we can skip further
212
218
  // artifact processing and return the model with an `extensions` property.
213
219
  return model;
@@ -215,7 +221,6 @@ function getDefinerFunctions( model ) {
215
221
 
216
222
  applyExtensions();
217
223
 
218
- Object.keys( model.definitions ).forEach( preProcessArtifact );
219
224
  const commonLanguagesEntity // TODO: remove beta after a grace period
220
225
  = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
221
226
  model.definitions['sap.common.Languages'];
@@ -238,13 +243,13 @@ function getDefinerFunctions( model ) {
238
243
  * @param {XSN.AST} src
239
244
  */
240
245
  function addSource( src ) {
241
- // handle sub model from CSN parser
246
+ // handle sub model from parser
242
247
  if (!src.kind)
243
248
  src.kind = 'source';
244
249
 
245
250
  let namespace = src.namespace && src.namespace.path;
246
251
  let prefix = namespace ? `${ pathName( namespace ) }.` : '';
247
- if (prefix.startsWith( 'cds.') && !prefix.match(/^cds\.foundation(\.|$)/)) {
252
+ if (isInReservedNamespace(prefix)) {
248
253
  error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
249
254
  // TODO: use $(NAME)
250
255
  'The namespace "cds" is reserved for CDS builtins' );
@@ -280,8 +285,7 @@ function getDefinerFunctions( model ) {
280
285
  function addDefinition( art, block ) {
281
286
  const { absolute } = art.name;
282
287
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
283
- if (absolute === 'cds' ||
284
- absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
288
+ if (absolute === 'cds' || isInReservedNamespace(absolute)) {
285
289
  error( 'reserved-namespace-cds', [ art.name.location, art ], {},
286
290
  // TODO: use $(NAME)
287
291
  'The namespace "cds" is reserved for CDS builtins' );
@@ -305,7 +309,7 @@ function getDefinerFunctions( model ) {
305
309
  }
306
310
  else {
307
311
  setLink( art, '_block', block );
308
- // dictAdd might set $duplicates to true if def in other source
312
+ // dictAdd might set $duplicates to true
309
313
  dictAdd( model.definitions, absolute, art );
310
314
  return true;
311
315
  }
@@ -366,7 +370,7 @@ function getDefinerFunctions( model ) {
366
370
  // TODO: check name: no "."
367
371
  if (path[0].id === 'localized' || path[0].id.startsWith( 'localized.' )) {
368
372
  decl.$inferred = 'LOCALIZED-IGNORED';
369
- warning( 'using-localized-view', [ path.location, decl ], {},
373
+ warning( 'using-localized-view', [ decl.location, decl ], {},
370
374
  'Localization views can\'t be referred to - ignored USING' );
371
375
  // actually not ignored anymore
372
376
  }
@@ -444,6 +448,23 @@ function getDefinerFunctions( model ) {
444
448
 
445
449
  // Phase 2 ("init") --------------------------------------------------------
446
450
 
451
+ function checkRedefinition( art ) {
452
+ if (!art.$duplicates)
453
+ return;
454
+ if (art._main) {
455
+ error( 'duplicate-definition', [ art.name.location, art ], {
456
+ name: art.name.id,
457
+ '#': kindProperties[art.kind].normalized || art.kind,
458
+ } );
459
+ }
460
+ else {
461
+ error( 'duplicate-definition', [ art.name.location, art ], {
462
+ name: art.name.absolute,
463
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
464
+ } );
465
+ }
466
+ }
467
+
447
468
  function initNamespaceAndUsing( src ) {
448
469
  if (src.namespace) {
449
470
  const decl = src.namespace;
@@ -488,6 +509,7 @@ function getDefinerFunctions( model ) {
488
509
  if (!reInit)
489
510
  initParentLink( art, model.definitions );
490
511
  const block = art._block;
512
+ checkRedefinition( art );
491
513
  defineAnnotations( art, art, block );
492
514
  initMembers( art, art, block );
493
515
  initDollarSelf( art ); // $self
@@ -513,6 +535,7 @@ function getDefinerFunctions( model ) {
513
535
 
514
536
  function initVocabulary( art ) {
515
537
  initParentLink( art, model.vocabularies );
538
+ checkRedefinition( art );
516
539
  const block = art._block;
517
540
  defineAnnotations( art, art, block );
518
541
  initMembers( art, art, block );
@@ -554,20 +577,6 @@ function getDefinerFunctions( model ) {
554
577
 
555
578
  // From here til EOF, reexamine code ---------------------------------------
556
579
 
557
- // currently called from preProcessArtifact(), do be called in "init"
558
- function checkRedefinitions( obj, name, prop ) {
559
- forEachMember( obj, checkRedefinitions, obj.targetAspect );
560
- if (!obj.$duplicates)
561
- return;
562
- if (obj.name.location.file === '<built-in>') {
563
- // builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
564
- // The error shall only be printed for the user-defined conflicting artifact.
565
- return;
566
- }
567
- error( 'duplicate-definition', [ obj.name.location, obj ],
568
- { name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
569
- }
570
-
571
580
  function initDollarSelf( art ) {
572
581
  const selfname = '$self';
573
582
  // TODO: use setMemberParent() ?
@@ -774,12 +783,13 @@ function getDefinerFunctions( model ) {
774
783
  setMemberParent( table, table.name.id, query );
775
784
  setProp( table, '_block', query._block );
776
785
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
777
- error( 'duplicate-definition', [ loc, table ], { name, '#': '$tableAlias' } );
786
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
778
787
  } );
779
788
  // also add to JOIN nodes for name restrictions:
780
789
  for (const p of joinParents) {
781
- // console.log('ADD:', query.name.id, parents.length, p)
782
- dictAdd( p.$tableAliases, table.name.id, table );
790
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
791
+ // already used for duplicate aliases of queries:
792
+ dictAddArray( p.$tableAliases, table.name.id, table );
783
793
  }
784
794
  if (table.name.id[0] === '$') {
785
795
  warning( 'syntax-dollar-ident', [ table.name.location, table ], {
@@ -793,8 +803,8 @@ function getDefinerFunctions( model ) {
793
803
 
794
804
  // art is:
795
805
  // - entity for top-level queries (including UNION args)
796
- // - $tableAlias for sub query in FROM
797
- // - $query for real sub query (in columns, WHERE, ...)
806
+ // - $tableAlias for sub query in FROM - TODO: what about UNION there?
807
+ // - $query for real sub query (in columns, WHERE, ...), again: what about UNION there?
798
808
  function initQueryExpression( query, art ) {
799
809
  if (!query) // parse error
800
810
  return query;
@@ -846,7 +856,7 @@ function getDefinerFunctions( model ) {
846
856
  query.kind = 'select';
847
857
  query.name = { location: query.location };
848
858
  setMemberParent( query, main.$queries.length + 1, main );
849
- // console.log(JSON.stringify(query.name))
859
+ // console.log(art.kind,art.name,query.name,query._$next.name)
850
860
  // if (query.name.query === 1 && query.name.absolute === 'S') throw Error();
851
861
  main.$queries.push( query );
852
862
  setProp( query, '_parent', art ); // _parent should point to alias/main/query
@@ -866,7 +876,7 @@ function getDefinerFunctions( model ) {
866
876
  // assignments on the mixin... (also for future mixin definitions
867
877
  // with generated values)
868
878
  dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
869
- error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '$tableAlias' } );
879
+ error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
870
880
  } );
871
881
  if (mixin.name.id[0] === '$') {
872
882
  warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
@@ -993,6 +1003,7 @@ function getDefinerFunctions( model ) {
993
1003
  setProp( elem, '_block', bl );
994
1004
  setMemberParent( elem, name, parent, construct !== parent && prop );
995
1005
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1006
+ checkRedefinition( elem );
996
1007
  defineAnnotations( elem, elem, bl );
997
1008
  initMembers( elem, elem, bl, initExtensions );
998
1009
 
@@ -1141,7 +1152,8 @@ function getDefinerFunctions( model ) {
1141
1152
  break; // only direct projection for auto-exposed
1142
1153
  }
1143
1154
  let ancestors = art && (!autoexposed && art._ancestors || []);
1144
- for (const a of chain.reverse()) {
1155
+ chain.reverse();
1156
+ for (const a of chain) {
1145
1157
  ancestors = (ancestors ? [ ...ancestors, art ] : []);
1146
1158
  setProp( a, '_ancestors', ancestors );
1147
1159
  art = a;
@@ -1263,6 +1275,9 @@ function getDefinerFunctions( model ) {
1263
1275
  }
1264
1276
  }
1265
1277
 
1278
+ /**
1279
+ * @returns {boolean|0} `true`, if allowed, `false` if forbidden, `0` if circular containment.
1280
+ */
1266
1281
  function allowAspectComposition( target, elem, keys, entityName ) {
1267
1282
  if (!target.elements || Object.values( target.elements ).some( e => e.$duplicates ))
1268
1283
  return false; // no elements or with redefinitions
@@ -1330,7 +1345,7 @@ function getDefinerFunctions( model ) {
1330
1345
  name: { path: splitIntoPath( location, entityName ), absolute: entityName, location },
1331
1346
  location,
1332
1347
  elements,
1333
- $inferred: 'aspect-composition',
1348
+ $inferred: 'composition-entity',
1334
1349
  };
1335
1350
  if (target.name) { // named target aspect
1336
1351
  setLink( art, '_origin', target );
@@ -1509,8 +1524,15 @@ function getDefinerFunctions( model ) {
1509
1524
  forEachMemberRecursivelyWithQuery(art, checkArtifact);
1510
1525
  }
1511
1526
 
1512
- // Function for parse.cdl
1513
- /** @param {XSN.Artifact} artifact */
1527
+ /**
1528
+ * Function for parse.cdl.
1529
+ * This parse.cdl function resolves types inside the artifact ("resolveTypeUnchecked").
1530
+ *
1531
+ * @todo This function needs to be properly reworked because at the moment we simply
1532
+ * added checks as issues arose (e.g. for "artifact.value").
1533
+ *
1534
+ * @param {XSN.Artifact} artifact
1535
+ * */
1514
1536
  function checkArtifact( artifact ) {
1515
1537
  // columns are initialized (and made to elements) in the resolver - do init here
1516
1538
  for (const col of artifact.columns || []) {
@@ -1521,11 +1543,21 @@ function getDefinerFunctions( model ) {
1521
1543
  recursivelyResolveExpressionCastTypes(col.value, artifact);
1522
1544
  }
1523
1545
 
1546
+ // Possible inside expand/inline
1547
+ if (artifact.value)
1548
+ recursivelyResolveExpressionCastTypes(artifact.value, artifact);
1549
+
1524
1550
  resolveTypeUnchecked(artifact, artifact);
1525
1551
 
1526
1552
  if (artifact.items)
1527
1553
  resolveTypeUnchecked(artifact.items, artifact);
1528
1554
 
1555
+ if (Array.isArray(artifact.expand))
1556
+ artifact.expand.forEach(art => checkArtifact(art));
1557
+
1558
+ if (Array.isArray(artifact.inline))
1559
+ artifact.inline.forEach(art => checkArtifact(art));
1560
+
1529
1561
  for (const include of (artifact.includes || []))
1530
1562
  resolveUncheckedPath(include, 'include', artifact);
1531
1563
 
@@ -1958,24 +1990,6 @@ function getDefinerFunctions( model ) {
1958
1990
  }
1959
1991
  }
1960
1992
 
1961
- // TODO: move to "init" phase
1962
- /**
1963
- * Check whether redefinitions of the given artifact name exist and
1964
- * adapt to `targetAspect`.
1965
- *
1966
- * @param {string} name
1967
- */
1968
- function preProcessArtifact( name ) {
1969
- const art = model.definitions[name];
1970
- if (Array.isArray(art.$duplicates)) {
1971
- // A definition name containing a `.` is not invalid (TODO: starting or
1972
- // ending with a dot is invalid and could be checked here)
1973
- for (const a of art.$duplicates)
1974
- checkRedefinitions( a, name, 'definitions' );
1975
- }
1976
- checkRedefinitions( art, name, 'definitions' );
1977
- }
1978
-
1979
1993
  /**
1980
1994
  * Process "composition of" artifacts.
1981
1995
  *
@@ -2111,7 +2125,7 @@ function getDefinerFunctions( model ) {
2111
2125
  name: { path: splitIntoPath( location, absolute ), absolute, location },
2112
2126
  location: base.location,
2113
2127
  elements,
2114
- $inferred: 'localized',
2128
+ $inferred: 'localized-entity',
2115
2129
  };
2116
2130
  const locale = {
2117
2131
  name: { location, id: 'locale' },
@@ -2212,7 +2226,7 @@ function getDefinerFunctions( model ) {
2212
2226
  name: { location, id: 'texts' },
2213
2227
  kind: 'element',
2214
2228
  location,
2215
- $inferred: 'localized-texts',
2229
+ $inferred: 'localized',
2216
2230
  type: augmentPath( location, 'cds.Composition' ),
2217
2231
  cardinality: { targetMax: { literal: 'string', val: '*', location }, location },
2218
2232
  target: augmentPath( location, textsName ),
@@ -2227,7 +2241,7 @@ function getDefinerFunctions( model ) {
2227
2241
  name: { location, id: 'localized' },
2228
2242
  kind: 'element',
2229
2243
  location,
2230
- $inferred: 'localized-texts',
2244
+ $inferred: 'localized',
2231
2245
  type: augmentPath( location, 'cds.Association' ),
2232
2246
  target: augmentPath( location, textsName ),
2233
2247
  on: augmentEqual( location, 'localized', keys ),
@@ -2316,36 +2330,6 @@ function mergeI18nBlocks( model ) {
2316
2330
  }
2317
2331
  }
2318
2332
 
2319
- /**
2320
- * Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with
2321
- * locations).
2322
- *
2323
- * @param {XSN.Path} path
2324
- */
2325
- function pathName(path) {
2326
- return path.map( id => id.id ).join('.');
2327
- }
2328
-
2329
- /**
2330
- * Generates an XSN path out of the given name. Path segments are delimited by a dot.
2331
- * Each segment will have the given location assigned.
2332
- *
2333
- * @param {CSN.Location} location
2334
- * @param {string} name
2335
- * @returns {XSN.Path}
2336
- */
2337
- function splitIntoPath( location, name ) {
2338
- return name.split('.').map( id => ({ id, location }) );
2339
- }
2340
-
2341
- /**
2342
- * @param {CSN.Location} location
2343
- * @param {...any} args
2344
- */
2345
- function augmentPath( location, ...args ) {
2346
- return { path: args.map( id => ({ id, location }) ), location };
2347
- }
2348
-
2349
2333
  function augmentEqual( location, assocname, relations, prefix = '' ) {
2350
2334
  const args = relations.map( eq );
2351
2335
  return (args.length === 1)
@@ -2374,9 +2358,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
2374
2358
  // these function could be used to a future lib/compiler/utils.js, but DO NOT
2375
2359
  // SHARE with utility functions for CSN processors
2376
2360
 
2377
- module.exports = {
2378
- define: model => getDefinerFunctions( model ).define(),
2379
- getDefinerFunctions,
2380
- augmentPath,
2381
- splitIntoPath,
2382
- };
2361
+ module.exports = { define };
@@ -1,5 +1,17 @@
1
1
  // Main XSN-based compiler functions
2
2
 
3
+ // ...
4
+
5
+ // How functions are shared across the Core Compiler sub modules:
6
+
7
+ // - Shared XSN-related functions which do not use a context are in utils.js,
8
+ // they are `require`d as usual at the beginning of sub modules.
9
+ // - The XSN is the only context which context-dependent functions can depend on.
10
+ // - Sharing such a function is by adding it to `‹xsn›.$functions`,
11
+ // e.g. `resolvePath` and similar will be attached to the XSN.
12
+ // - Functions which might be overwritten in a next sub module
13
+ // are added to `‹xsn›.$volatileFunctions`, currently just `environment`.
14
+
3
15
  'use strict';
4
16
 
5
17
  const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
@@ -8,12 +20,12 @@ const parseCsn = require('../json/from-csn');
8
20
 
9
21
  const assertConsistency = require('./assert-consistency');
10
22
  const moduleLayers = require('./moduleLayers');
23
+ const { fns } = require('./shared');
11
24
  const { define } = require('./definer');
12
25
  const resolve = require('./resolver');
13
26
  const propagator = require('./propagator');
14
27
  const check = require('./checks');
15
28
 
16
-
17
29
  const { emptyWeakLocation } = require('../base/location');
18
30
  const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
19
31
  const { promiseAllDoNotRejectImmediately } = require('../base/node-helpers');
@@ -52,7 +64,7 @@ class ArgumentError extends Error {
52
64
  * @param {object} options Compile options
53
65
  * @param {object} messageFunctions If not provided, parse errors will not lead to an exception
54
66
  */
55
- function parseX( source, filename, options = {}, messageFunctions ) {
67
+ function parseX( source, filename, options = {}, messageFunctions = null ) {
56
68
  if (!messageFunctions)
57
69
  messageFunctions = createMessageFunctions( options, 'parse' );
58
70
  const ext = path.extname( filename ).toLowerCase();
@@ -110,7 +122,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
110
122
  // fileCache = Object.assign( Object.create(null), fileCache );
111
123
  dir = path.resolve(dir);
112
124
  const a = processFilenames( filenames, dir );
113
- a.fileContentDict = Object.create(null);
114
125
 
115
126
  const model = { sources: a.sources, options };
116
127
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
@@ -132,37 +143,25 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
132
143
  });
133
144
 
134
145
  // Read file `filename` and parse its content, return messages
135
- function readAndParse( filename ) {
146
+ async function readAndParse( filename ) {
147
+ const { sources } = a;
136
148
  if ( filename === false ) // module which has not been found
137
149
  return [];
138
- const rel = a.sources[filename] || path.relative( dir, filename );
150
+ const rel = sources[filename] || path.relative( dir, filename );
139
151
  if (typeof rel === 'object') // already parsed
140
152
  return []; // no further dependency processing
141
153
  // no parallel readAndParse with same resolved filename should read the file,
142
- // also ensure deterministic sequence in a.sources:
143
- a.sources[filename] = { location: { file: rel } };
154
+ // also ensure deterministic sequence in sources:
155
+ sources[filename] = { location: { file: rel } };
144
156
 
145
- return new Promise( (fulfill, reject) => {
146
- cdsFs( fileCache, options.traceFs ).readFile( filename, 'utf8', (err, source) => {
147
- if (err) {
148
- reject(err);
149
- }
150
- else {
151
- try {
152
- a.fileContentDict[filename] = source;
153
- const ast = parseX( source, rel, options, model.$messageFunctions );
154
- a.sources[filename] = ast;
155
- ast.location = { file: rel };
156
- ast.dirname = path.dirname( filename );
157
- assertConsistency( ast, options );
158
- fulfill( ast );
159
- }
160
- catch (e) {
161
- reject( e );
162
- }
163
- }
164
- });
165
- });
157
+ const source = await cdsFs( fileCache, options.traceFs ).readFileAsync( filename, 'utf8' );
158
+ const ast = parseX( source, rel, options, model.$messageFunctions );
159
+ sources[filename] = ast;
160
+ ast.location = { file: rel };
161
+ ast.dirname = path.dirname( filename );
162
+ assertConsistency( ast, options );
163
+
164
+ return ast;
166
165
  }
167
166
 
168
167
  // Combine the parse results (if there are not file IO errors)
@@ -220,7 +219,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
220
219
  // absolute file names - they start with `/` or `\` or similar
221
220
  dir = path.resolve(dir);
222
221
  const a = processFilenames( filenames, dir );
223
- a.fileContentDict = Object.create(null);
224
222
 
225
223
  const model = { sources: a.sources, options };
226
224
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
@@ -281,7 +279,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
281
279
  }
282
280
  else {
283
281
  try {
284
- a.fileContentDict[filename] = source;
285
282
  const ast = parseX( source, rel, options, model.$messageFunctions );
286
283
  a.sources[filename] = ast;
287
284
  ast.location = { file: rel };
@@ -416,6 +413,9 @@ function compileDoX( model ) {
416
413
  throwWithError();
417
414
  return model;
418
415
  }
416
+ model.$functions = {};
417
+ model.$volatileFunctions = {};
418
+ fns( model ); // attach (mostly) paths functions
419
419
  define( model );
420
420
  // do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
421
421
  // TODO: do not use this function for parseCdl anyway…
@@ -8,7 +8,7 @@ const {
8
8
  setProp,
9
9
  isDeprecatedEnabled,
10
10
  } = require( '../base/model');
11
- const { linkToOrigin, withAssociation } = require('./shared');
11
+ const { linkToOrigin, withAssociation } = require('./utils');
12
12
  // const { refString } = require( '../base/messages')
13
13
 
14
14
  function propagate( model ) {
@@ -19,6 +19,8 @@ function propagate( model ) {
19
19
  '@cds.persistence.calcview': never,
20
20
  '@cds.persistence.udf': never,
21
21
  '@cds.persistence.skip': notWithPersistenceTable,
22
+ '@sql.prepend': never,
23
+ '@sql.append': never,
22
24
  '@Analytics.hidden': never,
23
25
  '@Analytics.visible': never,
24
26
  '@cds.autoexpose': onlyViaArtifact,
@@ -64,8 +66,7 @@ function propagate( model ) {
64
66
  if (!art)
65
67
  return;
66
68
  if (!checkAndSetStatus( art )) {
67
- if ( art.status !== 'propagated')
68
- runMembers( art );
69
+ runMembers( art );
69
70
  return;
70
71
  }
71
72
  // console.log('RUN:', art.name, art.elements ? Object.keys(art.elements) : 0)
@@ -179,6 +180,8 @@ function propagate( model ) {
179
180
  // accessed at their type being a main artifact
180
181
  function expensive( prop, target, source ) {
181
182
  // console.log(prop,source.name,'->',target.kind,target.name);
183
+ if (source.kind === 'builtin')
184
+ return;
182
185
  if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
183
186
  // foreignKeys must always be copied with target to avoid any confusion
184
187
  // whether we have to generated implicit keys
@@ -211,7 +214,8 @@ function propagate( model ) {
211
214
  }
212
215
 
213
216
  function onlyViaParent( prop, target, source ) {
214
- if (target.$inferred === 'proxy') // assocs and enums do not have 'include'
217
+ if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
218
+ // assocs and enums do not have 'include'
215
219
  always( prop, target, source );
216
220
  }
217
221