@sap/cds-compiler 2.7.0 → 2.11.2

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 (87) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +10 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +17 -33
  7. package/lib/api/options.js +25 -13
  8. package/lib/api/validate.js +33 -9
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +26 -2
  13. package/lib/base/messages.js +25 -9
  14. package/lib/base/model.js +5 -3
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/onConditions.js +5 -0
  17. package/lib/checks/selectItems.js +4 -0
  18. package/lib/checks/types.js +26 -2
  19. package/lib/checks/unknownMagic.js +41 -0
  20. package/lib/checks/validator.js +7 -2
  21. package/lib/compiler/assert-consistency.js +18 -5
  22. package/lib/compiler/base.js +65 -0
  23. package/lib/compiler/builtins.js +30 -1
  24. package/lib/compiler/checks.js +5 -2
  25. package/lib/compiler/definer.js +145 -120
  26. package/lib/compiler/index.js +16 -4
  27. package/lib/compiler/propagator.js +5 -2
  28. package/lib/compiler/resolver.js +207 -47
  29. package/lib/compiler/shared.js +47 -200
  30. package/lib/compiler/utils.js +173 -0
  31. package/lib/edm/annotations/genericTranslation.js +183 -187
  32. package/lib/edm/csn2edm.js +94 -98
  33. package/lib/edm/edm.js +16 -20
  34. package/lib/edm/edmPreprocessor.js +302 -115
  35. package/lib/edm/edmUtils.js +31 -12
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +28 -1
  38. package/lib/gen/language.tokens +79 -69
  39. package/lib/gen/languageLexer.interp +28 -1
  40. package/lib/gen/languageLexer.js +879 -805
  41. package/lib/gen/languageLexer.tokens +71 -62
  42. package/lib/gen/languageParser.js +5308 -4308
  43. package/lib/json/from-csn.js +59 -30
  44. package/lib/json/to-csn.js +354 -105
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +1 -0
  47. package/lib/language/genericAntlrParser.js +81 -14
  48. package/lib/language/language.g4 +163 -31
  49. package/lib/main.d.ts +136 -17
  50. package/lib/main.js +7 -1
  51. package/lib/model/api.js +78 -0
  52. package/lib/model/csnRefs.js +115 -32
  53. package/lib/model/csnUtils.js +71 -33
  54. package/lib/model/enrichCsn.js +36 -9
  55. package/lib/model/revealInternalProperties.js +20 -4
  56. package/lib/modelCompare/compare.js +2 -1
  57. package/lib/optionProcessor.js +33 -16
  58. package/lib/render/.eslintrc.json +3 -1
  59. package/lib/render/DuplicateChecker.js +1 -1
  60. package/lib/render/toCdl.js +60 -17
  61. package/lib/render/toHdbcds.js +122 -74
  62. package/lib/render/toSql.js +57 -32
  63. package/lib/render/utils/common.js +6 -10
  64. package/lib/sql-identifier.js +6 -1
  65. package/lib/transform/db/constraints.js +273 -119
  66. package/lib/transform/db/draft.js +9 -6
  67. package/lib/transform/db/expansion.js +19 -7
  68. package/lib/transform/db/flattening.js +31 -7
  69. package/lib/transform/db/transformExists.js +344 -66
  70. package/lib/transform/db/views.js +438 -0
  71. package/lib/transform/forHanaNew.js +65 -436
  72. package/lib/transform/forOdataNew.js +21 -10
  73. package/lib/transform/localized.js +2 -0
  74. package/lib/transform/odata/attachPath.js +19 -4
  75. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  76. package/lib/transform/odata/referenceFlattener.js +44 -38
  77. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  78. package/lib/transform/odata/structuralPath.js +72 -0
  79. package/lib/transform/odata/structureFlattener.js +13 -10
  80. package/lib/transform/odata/typesExposure.js +22 -12
  81. package/lib/transform/transformUtilsNew.js +55 -9
  82. package/lib/transform/translateAssocsToJoins.js +11 -17
  83. package/lib/transform/universalCsnEnricher.js +67 -0
  84. package/lib/utils/file.js +5 -3
  85. package/lib/utils/term.js +65 -42
  86. package/lib/utils/timetrace.js +48 -26
  87. package/package.json +1 -1
@@ -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,20 @@ 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
+ augmentPath,
135
+ splitIntoPath,
136
+ } = require('./utils');
135
137
  const { compareLayer, layer } = require('./moduleLayers');
136
- const { initBuiltins } = require('./builtins');
138
+ const { initBuiltins, isInReservedNamespace } = require('./builtins');
137
139
  const setLink = setProp;
138
140
 
139
141
  /**
@@ -148,7 +150,7 @@ const setLink = setProp;
148
150
  *
149
151
  * @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
150
152
  */
151
- function getDefinerFunctions( model ) {
153
+ function define( model ) {
152
154
  const { options } = model;
153
155
  // Get simplified "resolve" functionality and the message function:
154
156
  const {
@@ -160,24 +162,29 @@ function getDefinerFunctions( model ) {
160
162
  resolveTypeArguments,
161
163
  defineAnnotations,
162
164
  attachAndEmitValidNames,
163
- } = fns( model );
164
- const extensionsDict = Object.create(null);
165
- let addTextsLanguageAssoc = false;
166
-
167
- return {
168
- define,
165
+ } = model.$functions;
166
+ Object.assign( model.$functions, {
169
167
  initArtifact,
170
168
  lateExtensions,
171
169
  projectionAncestor,
172
170
  hasTruthyProp,
171
+ } );
172
+ // During the definer, we can only resolve artifact references, i.e,
173
+ // after a `.`, we only search in the `_subArtifacts` dictionary:
174
+ model.$volatileFunctions.environment = function artifactsEnv( art ) {
175
+ return art._subArtifacts || Object.create(null);
173
176
  };
174
177
 
178
+ const extensionsDict = Object.create(null);
179
+ let addTextsLanguageAssoc = false;
180
+ return doDefine();
181
+
175
182
  /**
176
183
  * Main function of the definer.
177
184
  *
178
185
  * @returns {XSN.Model}
179
186
  */
180
- function define() {
187
+ function doDefine() {
181
188
  if (options.deprecated &&
182
189
  messages.every( m => m.messageId !== 'api-deprecated-option' )) {
183
190
  warning( 'api-deprecated-option', {},
@@ -206,8 +213,6 @@ function getDefinerFunctions( model ) {
206
213
 
207
214
  if (options.parseCdl) {
208
215
  initExtensionsWithoutApplying();
209
- // Check for redefinitions
210
- Object.keys( model.definitions ).forEach( preProcessArtifact );
211
216
  // If no extensions shall be applied then we can skip further
212
217
  // artifact processing and return the model with an `extensions` property.
213
218
  return model;
@@ -215,8 +220,8 @@ function getDefinerFunctions( model ) {
215
220
 
216
221
  applyExtensions();
217
222
 
218
- Object.keys( model.definitions ).forEach( preProcessArtifact );
219
- const commonLanguagesEntity = isBetaEnabled( options, 'addTextsLanguageAssoc' ) &&
223
+ const commonLanguagesEntity // TODO: remove beta after a grace period
224
+ = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
220
225
  model.definitions['sap.common.Languages'];
221
226
  addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
222
227
  commonLanguagesEntity.elements.code);
@@ -237,13 +242,13 @@ function getDefinerFunctions( model ) {
237
242
  * @param {XSN.AST} src
238
243
  */
239
244
  function addSource( src ) {
240
- // handle sub model from CSN parser
245
+ // handle sub model from parser
241
246
  if (!src.kind)
242
247
  src.kind = 'source';
243
248
 
244
249
  let namespace = src.namespace && src.namespace.path;
245
250
  let prefix = namespace ? `${ pathName( namespace ) }.` : '';
246
- if (prefix.startsWith( 'cds.') && !prefix.match(/^cds\.foundation(\.|$)/)) {
251
+ if (isInReservedNamespace(prefix)) {
247
252
  error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
248
253
  // TODO: use $(NAME)
249
254
  'The namespace "cds" is reserved for CDS builtins' );
@@ -279,8 +284,7 @@ function getDefinerFunctions( model ) {
279
284
  function addDefinition( art, block ) {
280
285
  const { absolute } = art.name;
281
286
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
282
- if (absolute === 'cds' ||
283
- absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
287
+ if (absolute === 'cds' || isInReservedNamespace(absolute)) {
284
288
  error( 'reserved-namespace-cds', [ art.name.location, art ], {},
285
289
  // TODO: use $(NAME)
286
290
  'The namespace "cds" is reserved for CDS builtins' );
@@ -304,7 +308,7 @@ function getDefinerFunctions( model ) {
304
308
  }
305
309
  else {
306
310
  setLink( art, '_block', block );
307
- // dictAdd might set $duplicates to true if def in other source
311
+ // dictAdd might set $duplicates to true
308
312
  dictAdd( model.definitions, absolute, art );
309
313
  return true;
310
314
  }
@@ -443,6 +447,23 @@ function getDefinerFunctions( model ) {
443
447
 
444
448
  // Phase 2 ("init") --------------------------------------------------------
445
449
 
450
+ function checkRedefinition( art ) {
451
+ if (!art.$duplicates)
452
+ return;
453
+ if (art._main) {
454
+ error( 'duplicate-definition', [ art.name.location, art ], {
455
+ name: art.name.id,
456
+ '#': kindProperties[art.kind].normalized || art.kind,
457
+ } );
458
+ }
459
+ else {
460
+ error( 'duplicate-definition', [ art.name.location, art ], {
461
+ name: art.name.absolute,
462
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
463
+ } );
464
+ }
465
+ }
466
+
446
467
  function initNamespaceAndUsing( src ) {
447
468
  if (src.namespace) {
448
469
  const decl = src.namespace;
@@ -487,12 +508,13 @@ function getDefinerFunctions( model ) {
487
508
  if (!reInit)
488
509
  initParentLink( art, model.definitions );
489
510
  const block = art._block;
511
+ checkRedefinition( art );
490
512
  defineAnnotations( art, art, block );
491
513
  initMembers( art, art, block );
492
514
  initDollarSelf( art ); // $self
493
515
  if (art.params)
494
516
  initParams( art ); // $parameters
495
- if (art.includes && !(art.name.absolute in extensionsDict))
517
+ if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
496
518
  extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
497
519
 
498
520
  if (!art.query)
@@ -512,6 +534,7 @@ function getDefinerFunctions( model ) {
512
534
 
513
535
  function initVocabulary( art ) {
514
536
  initParentLink( art, model.vocabularies );
537
+ checkRedefinition( art );
515
538
  const block = art._block;
516
539
  defineAnnotations( art, art, block );
517
540
  initMembers( art, art, block );
@@ -553,20 +576,6 @@ function getDefinerFunctions( model ) {
553
576
 
554
577
  // From here til EOF, reexamine code ---------------------------------------
555
578
 
556
- // currently called from preProcessArtifact(), do be called in "init"
557
- function checkRedefinitions( obj, name, prop ) {
558
- forEachMember( obj, checkRedefinitions, obj.targetAspect );
559
- if (!obj.$duplicates)
560
- return;
561
- if (obj.name.location.file === '<built-in>') {
562
- // builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
563
- // The error shall only be printed for the user-defined conflicting artifact.
564
- return;
565
- }
566
- error( 'duplicate-definition', [ obj.name.location, obj ],
567
- { name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
568
- }
569
-
570
579
  function initDollarSelf( art ) {
571
580
  const selfname = '$self';
572
581
  // TODO: use setMemberParent() ?
@@ -607,14 +616,7 @@ function getDefinerFunctions( model ) {
607
616
  if (query.on)
608
617
  initExprForQuery( query.on, query );
609
618
  // TODO: MIXIN with name = ...subquery (not yet supported anyway)
610
- for (const elem of query.columns || []) {
611
- if (elem && (elem.value || elem.expand)) {
612
- setProp( elem, '_block', query._block );
613
- defineAnnotations( elem, elem, query._block );
614
- initExprForQuery( elem.value, query );
615
- initExpandInline( elem );
616
- }
617
- }
619
+ initSelectItems( query, query.columns );
618
620
  if (query.where)
619
621
  initExprForQuery( query.where, query );
620
622
  if (query.having)
@@ -622,22 +624,41 @@ function getDefinerFunctions( model ) {
622
624
  initMembers( query, query, query._block );
623
625
  }
624
626
 
625
- function initExpandInline( elem ) {
626
- // TODO: forbid with :param, global:true, in ref-where, outside queries (CSN), ...
627
- for (const sub of elem.expand || elem.inline || []) {
628
- if (!sub) // parse error
627
+ function initSelectItems( parent, columns ) {
628
+ // TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
629
+ let wildcard = null;
630
+ for (const col of columns || parent.expand || parent.inline || []) {
631
+ if (!col) // parse error
629
632
  continue;
630
- if (elem.value)
631
- setProp( sub, '_pathHead', elem ); // also set for '*' in expand/inline
632
- else if (elem._pathHead)
633
- setProp( sub, '_pathHead', elem._pathHead );
634
- if (sub.value || sub.expand) {
635
- setProp( sub, '_block', elem._block );
636
- defineAnnotations( sub, sub, elem._block ); // TODO: complain with inline
637
- initExpandInline( sub );
633
+ if (!columns) {
634
+ if (parent.value)
635
+ setProp( col, '_pathHead', parent ); // also set for '*' in expand/inline
636
+ else if (parent._pathHead)
637
+ setProp( col, '_pathHead', parent._pathHead );
638
+ }
639
+ if (col.val === '*') {
640
+ if (!wildcard) {
641
+ wildcard = col;
642
+ }
643
+ else {
644
+ // a late syntax error (this code also runs with parse-cdl), i.e.
645
+ // no semantic loc (wouldn't be available for expand/inline anyway)
646
+ error( 'syntax-duplicate-clause', [ col.location, null ],
647
+ { prop: '*', line: wildcard.location.line, col: wildcard.location.col },
648
+ 'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
649
+ // TODO: extra text variants for expand/inline? - probably not
650
+ col.val = null; // do not consider it for expandWildcard()
651
+ }
652
+ }
653
+ else if (col.value || col.expand) {
654
+ setProp( col, '_block', parent._block );
655
+ defineAnnotations( col, col, parent._block ); // TODO: complain with inline
656
+ // TODO: allow sub queries? at least in top-level expand without parallel ref
657
+ if (columns)
658
+ initExprForQuery( col.value, parent );
659
+ initSelectItems( col );
638
660
  }
639
661
  }
640
- // TODO: allow sub queries in top-level expand without parallel ref
641
662
  }
642
663
 
643
664
  function initExprForQuery( expr, query ) {
@@ -656,9 +677,38 @@ function getDefinerFunctions( model ) {
656
677
  }
657
678
  else if (expr.path && expr.$expected === 'exists') {
658
679
  expr.$expected = 'approved-exists';
680
+ approveExistsInChildren(expr);
659
681
  }
660
682
  }
661
683
 
684
+ /**
685
+ * If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
686
+ * since we will have a top-level subquery after exists-processing in the forHanaNew.
687
+ *
688
+ * Recursively drill down into:
689
+ * - the .path
690
+ * - the .args
691
+ * - the .where.args
692
+ *
693
+ * Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
694
+ *
695
+ * working: exists toE[exists toE] -> select from E where exists toE
696
+ * not working: toE[exists toE] -> we don't support subqueries in filters
697
+ *
698
+ * @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
699
+ */
700
+ function approveExistsInChildren(exprOrPathElement) {
701
+ if (exprOrPathElement.$expected === 'exists')
702
+ exprOrPathElement.$expected = 'approved-exists';
703
+ // Drill down
704
+ if (exprOrPathElement.args)
705
+ exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
706
+ else if (exprOrPathElement.where && exprOrPathElement.where.args)
707
+ exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
708
+ else if (exprOrPathElement.path)
709
+ exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
710
+ }
711
+
662
712
  // table is table expression in FROM, becomes an alias
663
713
  function initTableExpression( table, query, joinParents ) {
664
714
  if (!table) // parse error
@@ -706,6 +756,8 @@ function getDefinerFunctions( model ) {
706
756
  // ? ta._joinParent.args[ta.$joinArgsIndex] // in JOIN
707
757
  // : ta._parent.from ) // directly in FROM
708
758
  // Note for --raw-output: _joinParent pointing to CROSS JOIN node has not name
759
+ if (!tab) // parse error; time for #6241
760
+ return; // (parser method to only add non-null to array)
709
761
  setProp( tab, '_joinParent', table );
710
762
  tab.$joinArgsIndex = index;
711
763
  initTableExpression( tab, query, joinParents );
@@ -730,12 +782,13 @@ function getDefinerFunctions( model ) {
730
782
  setMemberParent( table, table.name.id, query );
731
783
  setProp( table, '_block', query._block );
732
784
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
733
- error( 'duplicate-definition', [ loc, table ], { name, '#': '$tableAlias' } );
785
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
734
786
  } );
735
787
  // also add to JOIN nodes for name restrictions:
736
788
  for (const p of joinParents) {
737
- // console.log('ADD:', query.name.id, parents.length, p)
738
- dictAdd( p.$tableAliases, table.name.id, table );
789
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
790
+ // already used for duplicate aliases of queries:
791
+ dictAddArray( p.$tableAliases, table.name.id, table );
739
792
  }
740
793
  if (table.name.id[0] === '$') {
741
794
  warning( 'syntax-dollar-ident', [ table.name.location, table ], {
@@ -822,7 +875,7 @@ function getDefinerFunctions( model ) {
822
875
  // assignments on the mixin... (also for future mixin definitions
823
876
  // with generated values)
824
877
  dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
825
- error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '$tableAlias' } );
878
+ error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
826
879
  } );
827
880
  if (mixin.name.id[0] === '$') {
828
881
  warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
@@ -859,6 +912,7 @@ function getDefinerFunctions( model ) {
859
912
  * (which is basically the component name of the `parent` element plus a dot).
860
913
  */
861
914
  function initMembers( construct, parent, block, initExtensions = false ) {
915
+ // TODO: split extend from init
862
916
  const isQueryExtension = kindProperties[construct.kind].isExtension &&
863
917
  (parent._main || parent).query;
864
918
  let obj = construct;
@@ -914,7 +968,7 @@ function getDefinerFunctions( model ) {
914
968
  forEachInOrder( construct, 'params', init );
915
969
  const { returns } = construct;
916
970
  if (returns) {
917
- returns.kind = 'param';
971
+ returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
918
972
  init( returns, '' ); // '' is special name for returns parameter
919
973
  }
920
974
  return;
@@ -948,6 +1002,7 @@ function getDefinerFunctions( model ) {
948
1002
  setProp( elem, '_block', bl );
949
1003
  setMemberParent( elem, name, parent, construct !== parent && prop );
950
1004
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1005
+ checkRedefinition( elem );
951
1006
  defineAnnotations( elem, elem, bl );
952
1007
  initMembers( elem, elem, bl, initExtensions );
953
1008
 
@@ -983,6 +1038,7 @@ function getDefinerFunctions( model ) {
983
1038
  }
984
1039
 
985
1040
  function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
1041
+ // TODO: do differently, see also annotateMembers() in resolver
986
1042
  // To have been checked by parsers:
987
1043
  // - artifacts (CDL-only anyway) only inside [extend] context|service
988
1044
  if (!dict)
@@ -1421,6 +1477,12 @@ function getDefinerFunctions( model ) {
1421
1477
 
1422
1478
  model.extensions.push(annotationArtifact);
1423
1479
  extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
1480
+ // if one of the annotate statement mentions 'returns', assume it
1481
+ // TODO: with warning/info?
1482
+ for (const ext of exts) {
1483
+ if (ext.$syntax === 'returns')
1484
+ annotationArtifact.$syntax = 'returns';
1485
+ }
1424
1486
  }
1425
1487
  }
1426
1488
  }
@@ -1704,6 +1766,7 @@ function getDefinerFunctions( model ) {
1704
1766
  }
1705
1767
 
1706
1768
  function extendMembers( extensions, art, noExtend ) {
1769
+ // TODO: do the whole extension stuff lazily if the elements are requested
1707
1770
  const elemExtensions = [];
1708
1771
  extensions.sort( compareLayer );
1709
1772
  for (const ext of extensions) {
@@ -1749,7 +1812,12 @@ function getDefinerFunctions( model ) {
1749
1812
  [ 'elements', 'actions' ].forEach( (prop) => {
1750
1813
  const dict = art._extend && art._extend[prop];
1751
1814
  for (const name in dict) {
1752
- const validDict = art[prop] || prop === 'elements' && art.enum;
1815
+ let obj = art;
1816
+ if (obj.targetAspect)
1817
+ obj = obj.targetAspect;
1818
+ while (obj.items)
1819
+ obj = obj.items;
1820
+ const validDict = obj[prop] || prop === 'elements' && obj.enum;
1753
1821
  const member = validDict[name];
1754
1822
  if (!member)
1755
1823
  extendNothing( dict[name], prop, name, art, validDict );
@@ -1900,24 +1968,6 @@ function getDefinerFunctions( model ) {
1900
1968
  }
1901
1969
  }
1902
1970
 
1903
- // TODO: move to "init" phase
1904
- /**
1905
- * Check whether redefinitions of the given artifact name exist and
1906
- * adapt to `targetAspect`.
1907
- *
1908
- * @param {string} name
1909
- */
1910
- function preProcessArtifact( name ) {
1911
- const art = model.definitions[name];
1912
- if (Array.isArray(art.$duplicates)) {
1913
- // A definition name containing a `.` is not invalid (TODO: starting or
1914
- // ending with a dot is invalid and could be checked here)
1915
- for (const a of art.$duplicates)
1916
- checkRedefinitions( a, name, 'definitions' );
1917
- }
1918
- checkRedefinitions( art, name, 'definitions' );
1919
- }
1920
-
1921
1971
  /**
1922
1972
  * Process "composition of" artifacts.
1923
1973
  *
@@ -1988,8 +2038,8 @@ function getDefinerFunctions( model ) {
1988
2038
 
1989
2039
  if (isKey && isLocalized) { // key with localized is wrong - ignore localized
1990
2040
  const errpos = elem.localized || elem.type || elem.name;
1991
- warning( 'localized-key', [ errpos.location, elem ], {},
1992
- 'Keyword "localized" is ignored for primary keys' );
2041
+ warning( 'localized-key', [ errpos.location, elem ], { keyword: 'localized' },
2042
+ 'Keyword $(KEYWORD) is ignored for primary keys' );
1993
2043
  }
1994
2044
  }
1995
2045
  if (textElems.length <= keys)
@@ -2131,9 +2181,9 @@ function getDefinerFunctions( model ) {
2131
2181
  });
2132
2182
  }
2133
2183
  }
2134
- else { // use location of LOCALIZED keyword
2184
+ if (hasTruthyProp( orig, 'localized' )) { // use location of LOCALIZED keyword
2135
2185
  const localized = orig.localized || orig.type || orig.name;
2136
- elem.localized = { val: false, $inferred: 'localized', location: localized.location };
2186
+ elem.localized = { val: null, $inferred: 'localized', location: localized.location };
2137
2187
  }
2138
2188
  }
2139
2189
  if (fioriEnabled)
@@ -2268,26 +2318,6 @@ function pathName(path) {
2268
2318
  return path.map( id => id.id ).join('.');
2269
2319
  }
2270
2320
 
2271
- /**
2272
- * Generates an XSN path out of the given name. Path segments are delimited by a dot.
2273
- * Each segment will have the given location assigned.
2274
- *
2275
- * @param {CSN.Location} location
2276
- * @param {string} name
2277
- * @returns {XSN.Path}
2278
- */
2279
- function splitIntoPath( location, name ) {
2280
- return name.split('.').map( id => ({ id, location }) );
2281
- }
2282
-
2283
- /**
2284
- * @param {CSN.Location} location
2285
- * @param {...any} args
2286
- */
2287
- function augmentPath( location, ...args ) {
2288
- return { path: args.map( id => ({ id, location }) ), location };
2289
- }
2290
-
2291
2321
  function augmentEqual( location, assocname, relations, prefix = '' ) {
2292
2322
  const args = relations.map( eq );
2293
2323
  return (args.length === 1)
@@ -2316,9 +2346,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
2316
2346
  // these function could be used to a future lib/compiler/utils.js, but DO NOT
2317
2347
  // SHARE with utility functions for CSN processors
2318
2348
 
2319
- module.exports = {
2320
- define: model => getDefinerFunctions( model ).define(),
2321
- getDefinerFunctions,
2322
- augmentPath,
2323
- splitIntoPath,
2324
- };
2349
+ 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,6 +20,7 @@ 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');
@@ -110,7 +123,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
110
123
  // fileCache = Object.assign( Object.create(null), fileCache );
111
124
  dir = path.resolve(dir);
112
125
  const a = processFilenames( filenames, dir );
113
- a.fileContentDict = Object.create(null);
114
126
 
115
127
  const model = { sources: a.sources, options };
116
128
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
@@ -149,7 +161,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
149
161
  }
150
162
  else {
151
163
  try {
152
- a.fileContentDict[filename] = source;
153
164
  const ast = parseX( source, rel, options, model.$messageFunctions );
154
165
  a.sources[filename] = ast;
155
166
  ast.location = { file: rel };
@@ -220,7 +231,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
220
231
  // absolute file names - they start with `/` or `\` or similar
221
232
  dir = path.resolve(dir);
222
233
  const a = processFilenames( filenames, dir );
223
- a.fileContentDict = Object.create(null);
224
234
 
225
235
  const model = { sources: a.sources, options };
226
236
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
@@ -281,7 +291,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
281
291
  }
282
292
  else {
283
293
  try {
284
- a.fileContentDict[filename] = source;
285
294
  const ast = parseX( source, rel, options, model.$messageFunctions );
286
295
  a.sources[filename] = ast;
287
296
  ast.location = { file: rel };
@@ -416,6 +425,9 @@ function compileDoX( model ) {
416
425
  throwWithError();
417
426
  return model;
418
427
  }
428
+ model.$functions = {};
429
+ model.$volatileFunctions = {};
430
+ fns( model ); // attach (mostly) paths functions
419
431
  define( model );
420
432
  // do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
421
433
  // 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 ) {
@@ -179,6 +179,8 @@ function propagate( model ) {
179
179
  // accessed at their type being a main artifact
180
180
  function expensive( prop, target, source ) {
181
181
  // console.log(prop,source.name,'->',target.kind,target.name);
182
+ if (source.kind === 'builtin')
183
+ return;
182
184
  if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
183
185
  // foreignKeys must always be copied with target to avoid any confusion
184
186
  // whether we have to generated implicit keys
@@ -211,7 +213,8 @@ function propagate( model ) {
211
213
  }
212
214
 
213
215
  function onlyViaParent( prop, target, source ) {
214
- if (target.$inferred === 'proxy') // assocs and enums do not have 'include'
216
+ if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
217
+ // assocs and enums do not have 'include'
215
218
  always( prop, target, source );
216
219
  }
217
220