Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 72.5% 153 / 0 / 211
Functions: -% 0 / 1 / 1
Branches: 61.2% 120 / 0 / 196

OMCompiler/Compiler/NFFrontEnd/NFInstUtil.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package NFInstUtil
37 import Absyn;
38 import Algorithm = NFAlgorithm;
39 import Binding = NFBinding;
40 import Call = NFCall;
41 import Class = NFClass;
42 import NFClassTree.ClassTree;
43 import Component = NFComponent;
44 import ComponentRef = NFComponentRef;
45 import Equation = NFEquation;
46 import Expression = NFExpression;
47 import FlatModel = NFFlatModel;
48 import NFFlatten.FunctionTree;
49 import NFFunction.Function;
50 import NFInstNode.InstNode;
51 import NFPrefixes.{Variability, Purity};
52 import SCode;
53 import Statement = NFStatement;
54 import Subscript = NFSubscript;
55 import Type = NFType;
56 import Variable = NFVariable;
57
58 protected
59 import AbsynUtil;
60 import DAE;
61 import BaseModelica;
62 import SCodeUtil;
63 import Dump;
64 import ElementSource;
65 import Flags;
66 import UnorderedMap;
67 import MetaModelica.Dangerous.listReverseInPlace;
68 import SCodeDump;
69 import ExecStat.execStat;
70
71 public
72 function dumpFlatModelDebug
73 input String stage;
74 input FlatModel flatModel;
75 input FunctionTree functions = FunctionTree.new();
76 protected
77 FlatModel flat_model = flatModel;
78 algorithm
79
4/8
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 8909 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8909 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
8915 if Flags.getConfigString(Flags.OBFUSCATE) == "protected" or
80 Flags.getConfigString(Flags.OBFUSCATE) == "encrypted" then
81 6 flat_model := FlatModel.obfuscate(flat_model);
82 end if;
83
84 // --dumpFlatModel=stage dumps specific stages, --dumpFlatModel dumps all stages.
85
3/4
✓ Branch 1 taken 8915 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8909 times.
✓ Branch 5 taken 6 times.
8915 if Flags.isConfigFlagSet(Flags.DUMP_FLAT_MODEL, stage) or
86 listEmpty(Flags.getConfigStringList(Flags.DUMP_FLAT_MODEL)) then
87 6 flat_model := combineSubscripts(flatModel);
88
89 6 print("########################################\n");
90 6 print(stage);
91 6 print("\n########################################\n\n");
92
93
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if Flags.getConfigBool(Flags.BASE_MODELICA) then
94 ✗ FlatModel.printFlatString(flat_model, functions);
95 else
96 6 FlatModel.printString(flat_model, functions);
97 end if;
98
99 6 print("\n");
100 end if;
101 end dumpFlatModelDebug;
102
103 function combineSubscripts
104 input output FlatModel flatModel;
105 algorithm
106
2/2
✓ Branch 1 taken 1465 times.
✓ Branch 2 taken 3 times.
1468 if Flags.isSet(Flags.COMBINE_SUBSCRIPTS) then
107 3 flatModel := FlatModel.mapExp(flatModel, combineSubscriptsExp);
108 end if;
109 end combineSubscripts;
110
111 function combineSubscriptsExp
112 input output Expression exp;
113 protected
114 function traverser
115 input output Expression exp;
116 algorithm
117 () := match exp
118 case Expression.CREF()
119 algorithm
120 58 exp.cref := ComponentRef.combineSubscripts(exp.cref);
121 then
122 ();
123
124 else ();
125 end match;
126 end traverser;
127 algorithm
128 55 exp := Expression.map(exp, traverser);
129 end combineSubscriptsExp;
130
131 function printStructuralParameters
132 input FlatModel flatModel;
133 protected
134 list<Variable> params;
135 list<String> names;
136 algorithm
137
1/2
✓ Branch 1 taken 1435 times.
✗ Branch 2 not taken.
1435 if Flags.isSet(Flags.PRINT_STRUCTURAL) then
138 ✗ params := list(v for v guard Variable.isStructural(v) in flatModel.variables);
139
140 ✗ if not listEmpty(params) then
141 ✗ names := list(ComponentRef.toString(v.name) for v in params);
142 ✗ Error.addMessage(Error.NOTIFY_FRONTEND_STRUCTURAL_PARAMETERS,
143 {stringDelimitList(names, ", ")});
144 end if;
145 end if;
146 end printStructuralParameters;
147
148 function dumpFlatModel
149 input FlatModel flatModel;
150 input FunctionTree functions;
151 output String str;
152 protected
153 FlatModel flat_model;
154 algorithm
155 32 flat_model := combineSubscripts(flatModel);
156 32 str := FlatModel.toFlatString(flat_model, functions);
157 end dumpFlatModel;
158
159 function replaceEmptyArrays
160 input output FlatModel flatModel;
161 algorithm
162 1435 flatModel := FlatModel.mapExp(flatModel, replaceEmptyArraysExp);
163 end replaceEmptyArrays;
164
165 function replaceEmptyArraysExp
166 "Variables with 0-dimensions are not present in the flat model, so replace
167 any cref that refers to such a variable with an empty array expression."
168 input output Expression exp;
169 protected
170 function traverser
171 input Expression exp;
172 output Expression outExp;
173 protected
174 ComponentRef cref;
175 list<Subscript> subs;
176 Type ty;
177 algorithm
178 outExp := match exp
179 case Expression.CREF(cref = cref)
180 guard ComponentRef.isEmptyArray(cref)
181 algorithm
182
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 if ComponentRef.hasSubscripts(cref) then
183 20 cref := ComponentRef.fillSubscripts(cref);
184 20 cref := ComponentRef.replaceWholeSubscripts(cref);
185 20 subs := ComponentRef.subscriptsAllFlat(cref);
186 20 cref := ComponentRef.stripSubscriptsAll(cref);
187 20 ty := ComponentRef.getSubscriptedType(cref);
188 else
189 subs := {};
190 ✗ ty := exp.ty;
191 end if;
192
193 20 outExp := Expression.makeEmptyArray(ty);
194
195
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if not listEmpty(subs) then
196 20 outExp := Expression.SUBSCRIPTED_EXP(outExp, subs, exp.ty, false);
197 end if;
198 then
199 outExp;
200
201 else exp;
202 end match;
203 end traverser;
204 algorithm
205 1106818 exp := Expression.map(exp, traverser);
206 end replaceEmptyArraysExp;
207
208 function expandSlicedCrefs
209 input output FlatModel flatModel;
210 input output FunctionTree functions;
211 algorithm
212
4/4
✓ Branch 1 taken 1428 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 217 times.
✓ Branch 5 taken 1211 times.
1430 if Flags.isSet(Flags.COMBINE_SUBSCRIPTS) or not Flags.isSet(Flags.NF_SCALARIZE) then
213 219 return;
214 end if;
215
216
4/4
✓ Branch 0 taken 371655 times.
✓ Branch 1 taken 1211 times.
✓ Branch 2 taken 371655 times.
✓ Branch 3 taken 1211 times.
374077 flatModel.variables := list(Variable.mapExpShallow(v, expandSlicedCrefsExp) for v in flatModel.variables);
217 1211 flatModel := FlatModel.mapEquations(flatModel, expandSlicedCrefsEq);
218 1211 flatModel := FlatModel.mapAlgorithms(flatModel, expandSlicedCrefsAlg);
219 1211 functions := FunctionTree.map(functions, expandSlicedCrefsFunction);
220 end expandSlicedCrefs;
221
222 function addTrailingWholeIndices
223 "Add implicit trailing subscripts to array expressions.
224 E.g. for Real[10,2] a this will replace a[i] with a[i,:]."
225 input output Expression exp;
226 algorithm
227 exp := match exp
228 case Expression.CREF()
229 guard ComponentRef.hasImplicitTrailingIndex(exp.cref)
230 algorithm
231 4 exp.cref := ComponentRef.fillSubscripts(exp.cref);
232 then exp;
233
234 else exp;
235 end match;
236 end addTrailingWholeIndices;
237
238 function expandSlicedCrefsExp
239 input output Expression exp;
240 algorithm
241 exp := match exp
242 case Expression.CREF()
243 guard ComponentRef.isSliced(exp.cref)
244 1 then expandSlicedCrefsExp2(exp.cref, exp.ty);
245
246 else exp;
247 end match;
248 end expandSlicedCrefsExp;
249
250 function expandSlicedCrefsExp2
251 input ComponentRef cref;
252 input Type ty;
253 output Expression outExp;
254 protected
255 ComponentRef cr;
256 list<tuple<InstNode, Expression>> iterators;
257 algorithm
258 1 (cr, iterators) := ComponentRef.iterate(cref);
259 1 outExp := Expression.CALL(
260 Call.TYPED_ARRAY_CONSTRUCTOR(
261 ty,
262 ComponentRef.variability(cref),
263 ComponentRef.purity(cref),
264 Expression.fromCref(cr),
265 iterators
266 )
267 );
268 end expandSlicedCrefsExp2;
269
270 function expandSlicedCrefsEq
271 input output Equation eq;
272 protected
273 Expression e1, e2;
274 Equation eq2;
275 algorithm
276 eq := match eq
277 case Equation.EQUALITY(rhs = e1)
278 algorithm
279 146039 e1:= Expression.map(e1, addTrailingWholeIndices);
280 146039 e2 := Expression.map(e1, expandSlicedCrefsExp);
281
282
2/2
✓ Branch 0 taken 109939 times.
✓ Branch 1 taken 36100 times.
146039 if not referenceEq(e1, e2) then
283 109939 eq.rhs := e2;
284 end if;
285 then
286 eq;
287
288 else
289 algorithm
290 2242 eq2 := Equation.mapExpShallow(eq, function Expression.map(func = addTrailingWholeIndices));
291 2242 then Equation.mapExpShallow(eq2, function Expression.map(func = expandSlicedCrefsExp));
292 end match;
293 end expandSlicedCrefsEq;
294
295 function expandSlicedCrefsAlg
296 input output Algorithm alg;
297 algorithm
298
4/4
✓ Branch 0 taken 57699 times.
✓ Branch 1 taken 9303 times.
✓ Branch 2 taken 57699 times.
✓ Branch 3 taken 9303 times.
67002 alg.statements := list(Statement.map(s, expandSlicedCrefsStmt) for s in alg.statements);
299 end expandSlicedCrefsAlg;
300
301 function expandSlicedCrefsStmt
302 input output Statement stmt;
303 protected
304 Expression e1, e2;
305 algorithm
306 stmt := match stmt
307 local
308 Statement stmt2;
309 case Statement.ASSIGNMENT(rhs = e1)
310 algorithm
311 84785 stmt.lhs := Expression.map(stmt.lhs, addTrailingWholeIndices);
312 84785 e1 := Expression.map(e1, addTrailingWholeIndices);
313 84785 e2 := Expression.map(e1, expandSlicedCrefsExp);
314
315
2/2
✓ Branch 0 taken 79333 times.
✓ Branch 1 taken 5452 times.
84785 if not referenceEq(e1, e2) then
316 79333 stmt.rhs := e2;
317 end if;
318 then
319 stmt;
320
321 else
322 algorithm
323 7293 stmt2 := Statement.mapExpShallow(stmt, function Expression.map(func = addTrailingWholeIndices));
324 7293 then Statement.mapExpShallow(stmt2, function Expression.map(func = expandSlicedCrefsExp));
325 end match;
326 end expandSlicedCrefsStmt;
327
328 function expandSlicedCrefsFunction
329 input Absyn.Path fnPath;
330 input output Function fn;
331 algorithm
332 10931 fn := Function.mapExp(fn,
333 function Expression.map(func = expandSlicedCrefsExp), mapBody = false);
334 10931 fn := Function.mapBody(fn, expandSlicedCrefsAlg);
335 end expandSlicedCrefsFunction;
336
337 type MergeNameMap = UnorderedMap<String, Absyn.ComponentRef>;
338
339 function makeMergeNameMap
340 output MergeNameMap nameMap = UnorderedMap.new<Absyn.ComponentRef>(stringHashDjb2, stringEq);
341 end makeMergeNameMap;
342
343 function mergeScalars
344 "Tries to merge components inside the given class into arrays. Components
345 can be merged if they have e.g. the same type, same prefixes, same
346 modifiers, etc."
347 input output InstNode node;
348 input Absyn.Path classPath;
349 input Boolean isRootClass;
350 input MergeNameMap nameMap = makeMergeNameMap();
351 protected
352 SCode.Element elem;
353 algorithm
354
2/2
✓ Branch 1 taken 19607 times.
✓ Branch 2 taken 4 times.
19611 if not Flags.isSet(Flags.MERGE_COMPONENTS) then
355 19607 return;
356 end if;
357
358 4 elem := InstNode.definition(node);
359 4 elem := mergeScalars2(elem, classPath, isRootClass, nameMap);
360 4 node := InstNode.setDefinition(elem, node);
361 4 execStat(getInstanceName());
362 end mergeScalars;
363
364 function mergeScalars2
365 "Helper function to mergeScalars, does the actual merging."
366 input output SCode.Element cls;
367 input Absyn.Path classPath;
368 input Boolean isRootClass;
369 input MergeNameMap nameMap;
370 protected
371 SCode.ClassDef cdef;
372 list<SCode.Element> elems;
373 algorithm
374 () := match cls
375 case SCode.Element.CLASS(classDef = cdef as SCode.ClassDef.PARTS())
376 algorithm
377 // Merge components.
378 4 elems := mergeScalars3(cdef.elementLst, nameMap);
379
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
10 elems := list(mergeScalarsElement(e, nameMap) for e in elems);
380 4 cdef.elementLst := elems;
381 // Replace references to merged components with their new names.
382 4 cdef.normalEquationLst := mergeScalarsEql(cdef.normalEquationLst, nameMap);
383 4 cdef.initialEquationLst := mergeScalarsEql(cdef.initialEquationLst, nameMap);
384 4 cdef.normalAlgorithmLst := mergeScalarsAlgs(cdef.normalAlgorithmLst, nameMap);
385 4 cdef.initialAlgorithmLst := mergeScalarsAlgs(cdef.initialAlgorithmLst, nameMap);
386
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 cls.classDef := cdef;
387
388
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if isRootClass then
389 4 System.writeFile(AbsynUtil.pathString(classPath) + "_merged_table.json",
390 UnorderedMap.toJSON(nameMap, Util.id, Dump.printComponentRefStr));
391 end if;
392 then
393 ();
394
395 else ();
396 end match;
397 end mergeScalars2;
398
399 function mergeScalars3
400 "Helper function to mergeScalars2. Takes a list of elements and returns a
401 new list with components merged, as well as a map of the merged components'
402 old names to their new names."
403 input list<SCode.Element> elements;
404 input MergeNameMap nameMap;
405 output list<SCode.Element> outElements;
406 protected
407 list<list<SCode.Element>> mergeable;
408 SCode.Element merged_e;
409 Integer i = UnorderedMap.size(nameMap) + 1;
410 String prefix;
411 algorithm
412 // Find the groups of mergeable component.
413 4 (mergeable, outElements) := makeMergeMap(elements);
414
415 // Merge each group of mergeable components.
416
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 for el in mergeable loop
417 // The name of the merged component will be $LastIdentInTypeOfComponent +
418 // an index to ensure the name is unique.
419 6 prefix := "$" + AbsynUtil.pathLastIdent(SCodeUtil.getElementTypePath(listHead(el)));
420 6 merged_e := mergeComponents(el, prefix + String(i), nameMap);
421 6 i := i + 1;
422 6 outElements := merged_e :: outElements;
423 end for;
424
425 4 outElements := listReverseInPlace(outElements);
426 end mergeScalars3;
427
428 function makeMergeMap
429 "Takes a list of elements and returns a list of mergeable component groups
430 and a list of all other unmergeable elements."
431 input list<SCode.Element> elements;
432 output list<list<SCode.Element>> mergeable = {};
433 output list<SCode.Element> unmergeable = {};
434 protected
435 type ElementList = list<SCode.Element>;
436 UnorderedMap<String, ElementList> merge_map;
437 list<list<SCode.Element>> grouped_elems;
438
439 function append_merge
440 input Option<list<SCode.Element>> oldValue;
441 input SCode.Element elem;
442 output list<SCode.Element> newValue;
443 algorithm
444
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 6 times.
14 if isSome(oldValue) then
445 8 SOME(newValue) := oldValue;
446 else
447 newValue := {};
448 end if;
449
450 newValue := elem :: newValue;
451 end append_merge;
452 algorithm
453 4 merge_map := UnorderedMap.new<ElementList>(stringHashDjb2, stringEq);
454
455 // Group the components by their signature if they fulfill the requirements
456 // for being considered mergeable.
457
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 4 times.
18 for e in elements loop
458 () := match e
459 case SCode.Element.COMPONENT()
460 guard isMergeableComponent(e)
461 algorithm
462 14 UnorderedMap.addUpdate(getComponentSignature(e),
463 function append_merge(elem = e), merge_map);
464 then
465 ();
466
467 else
468 algorithm
469 unmergeable := e :: unmergeable;
470 then
471 ();
472 end match;
473 end for;
474
475 4 grouped_elems := UnorderedMap.valueList(merge_map);
476
477 // Move single mergeable components to the list of unmergeables.
478
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 for el in grouped_elems loop
479
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if listLength(el) == 1 then
480 ✗ unmergeable := listHead(el) :: unmergeable;
481 else
482 6 mergeable := listReverseInPlace(el) :: mergeable;
483 end if;
484 end for;
485 end makeMergeMap;
486
487 function isMergeableComponent
488 "Returns true if an element is a component that is considered to be
489 mergeable, otherwise false. A component is considered to be not mergeable
490 if it is e.g. a redeclare or inner/outer."
491 input SCode.Element element;
492 output Boolean isMergeable;
493 algorithm
494 isMergeable := match element
495 case SCode.Element.COMPONENT(attributes = SCode.Attributes.ATTR(arrayDims = {}),
496 prefixes = SCode.Prefixes.PREFIXES(
497 redeclarePrefix = SCode.Redeclare.NOT_REDECLARE(),
498 innerOuter = Absyn.InnerOuter.NOT_INNER_OUTER(),
499 replaceablePrefix = SCode.Replaceable.NOT_REPLACEABLE()),
500 condition = NONE())
501
2/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
14 then isMergeableType(element.typeSpec) and isMergeableMod(element.modifications);
502
503 else false;
504 end match;
505 end isMergeableComponent;
506
507 function isMergeableMod
508 input SCode.Mod mod;
509 output Boolean mergeable;
510 algorithm
511 mergeable := match mod
512 case SCode.MOD(eachPrefix = SCode.Each.NOT_EACH())
513 algorithm
514
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 42 times.
70 for m in mod.subModLst loop
515
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
28 if not isMergeableMod(m.mod) then
516 mergeable := false;
517 ✗ return;
518 end if;
519 end for;
520 then
521 true;
522
523 case SCode.NOMOD() then true;
524 else false;
525 end match;
526 end isMergeableMod;
527
528 function isMergeableType
529 input Absyn.TypeSpec ty;
530 output Boolean mergeable;
531 algorithm
532 mergeable := match ty
533 case Absyn.TPATH(arrayDim = NONE()) then true;
534 else false;
535 end match;
536 end isMergeableType;
537
538 function getComponentSignature
539 "Creates a signature string for a component which consists of the components
540 attributes, type, and modifiers, that can be used to group similar
541 components together."
542 input SCode.Element element;
543 output String signature;
544 protected
545 SCode.Prefixes prefs;
546 SCode.Attributes attrs;
547 Absyn.TypeSpec ty;
548 SCode.Mod mod;
549 algorithm
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 SCode.Element.COMPONENT(prefixes = prefs, attributes = attrs, typeSpec = ty,
551 modifications = mod) := element;
552
553 112 signature := stringAppendList({
554 SCodeDump.visibilityStr(prefs.visibility),
555 SCodeDump.finalStr(prefs.finalPrefix),
556 SCodeDump.connectorTypeStr(attrs.connectorType),
557 SCodeDump.variabilityString(attrs.variability),
558 Dump.unparseDirectionSymbolStr(attrs.direction),
559 Dump.unparseTypeSpec(ty),
560 getModSignature(mod)
561 });
562 end getComponentSignature;
563
564 function getModSignature
565 "Creates a signature string for a modifier that can be hashed in order to
566 group similar modifiers. The signature is similar to the string
567 representation of the modifier with the binding expressions removed, e.g.:
568
569 (x=3, y(start=1)=4, m(a=1)) => '(m(a=,),x=,y(start=,)=,)'
570
571 Submodifiers are also sorted by their names, so (x=3, y=4) and (y=4, x=3)
572 both have the signature '(x=,y=,)'."
573 input SCode.Mod mod;
574 input String name = "";
575 output String signature;
576 protected
577 function sub_mod_lt
578 input SCode.SubMod m1;
579 input SCode.SubMod m2;
580 output Boolean res = m1.ident < m2.ident;
581 end sub_mod_lt;
582
583 list<String> strl = {};
584 Boolean has_binding, has_submods;
585 algorithm
586 signature := match mod
587 case SCode.Mod.MOD()
588 algorithm
589
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 12 times.
42 has_binding := isSome(mod.binding);
590 42 has_submods := not listEmpty(mod.subModLst);
591
592
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 12 times.
42 if has_binding then
593 strl := "=" :: strl;
594 end if;
595
596
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 30 times.
42 if has_submods then
597 strl := ")" :: strl;
598
599
2/2
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 12 times.
40 for m in List.sort(mod.subModLst, sub_mod_lt) loop
600 strl := "," :: strl;
601 28 strl := getModSignature(m.mod, m.ident) :: strl;
602 end for;
603
604 strl := "(" :: strl;
605 end if;
606
607
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if has_binding or has_submods then
608 strl := name :: strl;
609 end if;
610
611
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
42 if SCodeUtil.finalBool(mod.finalPrefix) then
612 strl := "final " :: strl;
613 end if;
614
615
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
42 if SCodeUtil.eachBool(mod.eachPrefix) then
616 strl := "each " :: strl;
617 end if;
618 42 then
619 stringAppendList(strl);
620
621 else "";
622 end match;
623 end getModSignature;
624
625 function mergeComponents
626 "Merges a list of components into a single component."
627 input list<SCode.Element> components;
628 input String prefix;
629 input MergeNameMap nameMap;
630 output SCode.Element mergedComponent;
631 protected
632 Absyn.TypeSpec ty;
633 SCode.Prefixes prefs;
634 SCode.Attributes attrs;
635 SCode.Mod mod;
636 Integer i = 1;
637 String name;
638 Absyn.ComponentRef cref;
639 list<SCode.Mod> mods;
640 algorithm
641 // All components should have the same type and attributes, so take them
642 // from the first component in the list.
643
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 SCode.Element.COMPONENT(typeSpec = ty,
644 prefixes = prefs,
645 attributes = attrs) := listHead(components);
646
647 // Add a dimension equal to the number of components.
648 12 attrs.arrayDims := {AbsynUtil.makeIntegerSubscript(listLength(components))};
649
650 // Merge the modifiers into a single modifier.
651
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 6 times.
20 mods := list(SCodeUtil.componentMod(c) for c in components);
652 6 mod := mergeMods(mods);
653
654 6 mergedComponent := SCode.Element.COMPONENT(
655 prefix,
656 prefs,
657 attrs,
658 ty,
659 mod,
660 SCode.noComment,
661 NONE(),
662 Absyn.dummyInfo
663 );
664
665 // Add a mapping from old name to new name for each of the merged components.
666
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6 times.
20 for c in components loop
667
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 SCode.Element.COMPONENT(name = name) := c;
668 28 cref := Absyn.ComponentRef.CREF_IDENT(prefix, {AbsynUtil.makeIntegerSubscript(i)});
669 14 i := i + 1;
670 14 UnorderedMap.addUnique(name, cref, nameMap);
671 end for;
672 end mergeComponents;
673
674 function mergeMods
675 "Merges a list of modifiers into one modifier. All modifiers are assumed to
676 be equal with exception for binding expressions."
677 input list<SCode.Mod> mods;
678 output SCode.Mod mod;
679 protected
680 list<Absyn.Path> names;
681 list<list<Absyn.Exp>> bindings;
682 UnorderedMap<Absyn.Path, Absyn.Exp> binding_map;
683 algorithm
684
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if listEmpty(mods) then
685 mod := SCode.Mod.NOMOD();
686 ✗ return;
687 end if;
688
689 // Get the paths of all the modified elements.
690 6 mod := listHead(mods);
691 6 names := getModNames(mod);
692 6 bindings := List.fill({}, listLength(names));
693
694 // Collect the bindings from all modifiers.
695
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 6 times.
20 for m in listReverse(mods) loop
696 14 bindings := getModBindings(m, names, bindings);
697 end for;
698
699 // Make a name => binding map.
700
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 6 times.
18 binding_map := UnorderedMap.fromLists<Absyn.Exp>(names,
701 listReverse(Absyn.Exp.ARRAY(b) for b in bindings),
702 AbsynUtil.pathHash, AbsynUtil.pathEqual);
703
704 // Use one the modifiers as a template and replace the bindings in it with
705 // the merged bindings, in order to preserve 'each' and 'final'.
706 6 mod := mergeMods2(mod, binding_map);
707 end mergeMods;
708
709 function getModNames
710 "Returns a list of the modified names in a modifier, e.g.:
711 (x = 4, y(start = 1) = 3, m(a = 2)) => {x, y, y.start, m.a}"
712 input SCode.Mod mod;
713 input list<String> name = {};
714 input output list<Absyn.Path> names = {};
715 algorithm
716 names := match mod
717 case SCode.Mod.MOD()
718 algorithm
719
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 5 times.
17 if isSome(mod.binding) then
720 12 names := makeModPath(name) :: names;
721 end if;
722
723
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 17 times.
28 for m in mod.subModLst loop
724 11 names := getModNames(m.mod, m.ident :: name, names);
725 end for;
726 then
727 names;
728
729 else names;
730 end match;
731 end getModNames;
732
733 function makeModPath
734 input list<String> name;
735 output Absyn.Path path;
736 algorithm
737
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 2 times.
24 if listEmpty(name) then
738 // Use $ to indicate an empty path since paths can't be empty.
739 path := Absyn.Path.IDENT("$");
740 else
741 22 path := AbsynUtil.stringListPathReversed(name);
742 end if;
743 end makeModPath;
744
745 function mergeMods2
746 "Helper function to mergeMods, replaces bindings in the given modifier with
747 the merged bindings."
748 input output SCode.Mod mod;
749 input UnorderedMap<Absyn.Path, Absyn.Exp> bindingMap;
750 input list<String> name = {};
751 protected
752 Absyn.Exp new_binding;
753 list<SCode.SubMod> submods = {};
754 algorithm
755 () := match mod
756 case SCode.Mod.MOD()
757 algorithm
758 // If the modifier has a binding expression, look up the new binding
759 // in the map and replace it.
760
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 5 times.
17 if isSome(mod.binding) then
761 12 new_binding := UnorderedMap.getOrFail(makeModPath(name), bindingMap);
762 12 mod.binding := SOME(new_binding);
763 end if;
764
765 // Recursively do the same to the submodifiers.
766
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 12 times.
17 if not listEmpty(mod.subModLst) then
767
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
16 for m in mod.subModLst loop
768 22 m.mod := mergeMods2(m.mod, bindingMap, m.ident :: name);
769 submods := m :: submods;
770 end for;
771
772 5 mod.subModLst := listReverseInPlace(submods);
773 end if;
774 then
775 ();
776
777 else ();
778 end match;
779 end mergeMods2;
780
781 function getModBindings
782 "Looks up the named bindings in a modifier and appends them to the binding
783 expression lists."
784 input SCode.Mod mod;
785 input list<Absyn.Path> names;
786 input output list<list<Absyn.Exp>> bindings;
787 protected
788 list<Absyn.Exp> mod_bindings = {};
789 algorithm
790
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 14 times.
44 for name in names loop
791 30 mod_bindings := lookupModBinding(name, mod) :: mod_bindings;
792 end for;
793
794
7/8
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 30 times.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
44 bindings := list(cons(e1, e2) threaded for e1 in mod_bindings, e2 in bindings);
795 end getModBindings;
796
797 function lookupModBinding
798 "Looks up the binding expression for the modifier given by the path."
799 input Absyn.Path name;
800 input SCode.Mod mod;
801 output Absyn.Exp binding;
802 algorithm
803
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 30 times.
30 SCode.Mod.MOD(binding = SOME(binding)) := lookupMod(name, mod);
804 end lookupModBinding;
805
806 function lookupMod
807 "Looks up the modifier given by the path."
808 input Absyn.Path name;
809 input SCode.Mod mod;
810 output SCode.Mod outMod;
811 algorithm
812 outMod := match name
813 case Absyn.Path.IDENT()
814 // $ means an empty path, return the given modifier in that case.
815 // Otherwise look the name up in the modifier.
816
3/4
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 2 times.
30 then if name.name == "$" then mod else SCodeUtil.lookupModInMod(name.name, mod);
817
818 case Absyn.Path.QUALIFIED()
819 algorithm
820 ✗ outMod := SCodeUtil.lookupModInMod(name.name, mod);
821 ✗ then
822 lookupMod(name.path, outMod);
823 end match;
824 end lookupMod;
825
826 function mergeScalarsElement
827 input output SCode.Element element;
828 input MergeNameMap nameMap;
829 algorithm
830 () := match element
831 case SCode.Element.EXTENDS()
832 algorithm
833 ✗ element.modifications := mergeScalarsMod(element.modifications, nameMap);
834 then
835 ();
836
837 case SCode.Element.COMPONENT()
838 algorithm
839 6 element.modifications := mergeScalarsMod(element.modifications, nameMap);
840 then
841 ();
842
843 else ();
844 end match;
845 end mergeScalarsElement;
846
847 function mergeScalarsEql
848 "Updates the names of merged components in a list of equations."
849 input output list<SCode.Equation> eql;
850 input MergeNameMap nameMap;
851 algorithm
852 8 eql := SCodeUtil.mapEquationsList(eql, function mergeScalarsEq(nameMap = nameMap));
853 end mergeScalarsEql;
854
855 function mergeScalarsEq
856 "Updates the names of merged components in an equation."
857 input output SCode.Equation eq;
858 input MergeNameMap nameMap;
859 algorithm
860 1 eq := SCodeUtil.mapEquationExps(eq, function mergeScalarsExps(nameMap = nameMap));
861
862 () := match eq
863 case SCode.Equation.EQ_CONNECT()
864 algorithm
865 ✗ eq.crefLeft := mergeScalarsCref(eq.crefLeft, nameMap);
866 ✗ eq.crefRight := mergeScalarsCref(eq.crefRight, nameMap);
867 then
868 ();
869
870 else ();
871 end match;
872 end mergeScalarsEq;
873
874 function mergeScalarsMod
875 input output SCode.Mod mod;
876 input MergeNameMap nameMap;
877 algorithm
878 () := match mod
879 case SCode.Mod.MOD()
880 algorithm
881 34 mod.binding := Util.applyOption(mod.binding, function mergeScalarsExps(nameMap = nameMap));
882
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 17 times.
45 mod.subModLst := list(mergeScalarsSubMod(m, nameMap) for m in mod.subModLst);
883 then
884 ();
885
886 else ();
887 end match;
888 end mergeScalarsMod;
889
890 function mergeScalarsSubMod
891 input output SCode.SubMod mod;
892 input MergeNameMap nameMap;
893 algorithm
894 11 mod.mod := mergeScalarsMod(mod.mod, nameMap);
895 end mergeScalarsSubMod;
896
897 function mergeScalarsExps
898 "Updates the names of merged components in an expression."
899 input output Absyn.Exp exp;
900 input MergeNameMap nameMap;
901 algorithm
902 16 exp := AbsynUtil.traverseExp(exp, mergeScalarsExp, nameMap);
903 end mergeScalarsExps;
904
905 function mergeScalarsExp
906 input output Absyn.Exp exp;
907 input output MergeNameMap nameMap;
908 algorithm
909 () := match exp
910 case Absyn.Exp.CREF()
911 guard not AbsynUtil.crefIsWild(exp.componentRef)
912 algorithm
913 8 exp.componentRef := mergeScalarsCref(exp.componentRef, nameMap);
914 then
915 ();
916
917 else ();
918 end match;
919 end mergeScalarsExp;
920
921 function mergeScalarsCref
922 "Updates the names of a component reference if it refers to a merged component."
923 input output Absyn.ComponentRef cref;
924 input MergeNameMap nameMap;
925 protected
926 Option<Absyn.ComponentRef> repl_ocr;
927 Absyn.ComponentRef repl_cr;
928 list<Absyn.Subscript> subs;
929 algorithm
930 // Look up the first part of the cref in the name map.
931 8 repl_ocr := UnorderedMap.get(AbsynUtil.crefFirstIdent(cref), nameMap);
932
933 // If a replacement was found...
934
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 if isSome(repl_ocr) then
935 8 SOME(repl_cr) := repl_ocr;
936 // The new name has a subscript that needs to be merged with any existing subscripts.
937 8 subs := AbsynUtil.crefFirstSubs(cref);
938
939
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if not listEmpty(subs) then
940 ✗ subs := listAppend(AbsynUtil.crefFirstSubs(repl_cr), subs);
941 ✗ repl_cr := AbsynUtil.crefSetLastSubs(repl_cr, subs);
942 end if;
943
944 // Replace the first part of the cref with the new part.
945 8 cref := AbsynUtil.crefReplaceFirst(cref, repl_cr);
946 end if;
947 end mergeScalarsCref;
948
949 function mergeScalarsAlgs
950 "Updates the names of merged components in a list of algorithm sections."
951 input output list<SCode.AlgorithmSection> algs;
952 input MergeNameMap nameMap;
953 algorithm
954
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 8 times.
9 algs := list(SCodeUtil.mapAlgorithmStatements(a,
955 function mergeScalarsStmt(nameMap = nameMap)) for a in algs);
956 end mergeScalarsAlgs;
957
958 function mergeScalarsStmt
959 "Updates the names of merged components in a statement."
960 input output SCode.Statement stmt;
961 input MergeNameMap nameMap;
962 algorithm
963 1 stmt := SCodeUtil.mapStatementExps(stmt, function mergeScalarsExps(nameMap = nameMap));
964 end mergeScalarsStmt;
965
966 function mergeScalarsComponentBindings
967 input InstNode node;
968 input MergeNameMap nameMap;
969 protected
970 Class cls;
971 ClassTree cls_tree;
972 algorithm
973 ✗ cls := InstNode.getClass(node);
974 ✗ cls_tree := Class.classTree(cls);
975 ✗ ClassTree.applyComponents(cls_tree, function mergeScalarsComponentBinding(nameMap = nameMap));
976 ✗ cls := Class.setClassTree(cls_tree, cls);
977 ✗ InstNode.updateClass(cls, node);
978 end mergeScalarsComponentBindings;
979
980 function mergeScalarsComponentBinding
981 input InstNode node;
982 input MergeNameMap nameMap;
983 protected
984 Component comp;
985 algorithm
986 ✗ if not InstNode.isComponent(node) then
987 ✗ return;
988 end if;
989
990 ✗ comp := InstNode.component(node);
991
992 () := match comp
993 case Component.COMPONENT_DEF()
994 algorithm
995 ✗ comp.definition := mergeScalarsElement(comp.definition, nameMap);
996 ✗ InstNode.updateComponent(comp, node);
997 then
998 ();
999
1000 else ();
1001 end match;
1002 end mergeScalarsComponentBinding;
1003
1004 function createExtractorModel
1005 input FlatModel flatModel;
1006 input FunctionTree funcs;
1007 output FlatModel extractorModel = flatModel;
1008 output FunctionTree outFuncs = funcs;
1009 protected
1010 list<Variable> top_level_connectors, flows, inputs;
1011 Function fn_template;
1012 Integer index = 0;
1013 list<Equation> eqs = {};
1014 Equation eq;
1015 list<Expression> args;
1016 algorithm
1017 ✗ (top_level_connectors, flows, inputs) := collectExtractorModelVariables(flatModel.variables);
1018 ✗ fn_template := createExtractorModelDummyFn(top_level_connectors);
1019 ✗ args := list(Expression.fromCref(Variable.name(c)) for c in top_level_connectors);
1020
1021 ✗ for f in flows loop
1022 ✗ (eq, outFuncs, index) := createExtractorModelDummyEq(f, "flow", fn_template, args, outFuncs, index);
1023 eqs := eq :: eqs;
1024 end for;
1025
1026 ✗ for i in inputs loop
1027 ✗ (eq, outFuncs, index) := createExtractorModelDummyEq(i, "input", fn_template, args, outFuncs, index);
1028 eqs := eq :: eqs;
1029 end for;
1030
1031 ✗ eqs := listReverseInPlace(eqs);
1032 ✗ extractorModel.equations := listAppend(extractorModel.equations, eqs);
1033 end createExtractorModel;
1034
1035 function collectExtractorModelVariables
1036 input list<Variable> vars;
1037 output list<Variable> topLevelConnectorVars = {};
1038 output list<Variable> flowVars = {};
1039 output list<Variable> inputVars = {};
1040 protected
1041 InstNode top_node;
1042 algorithm
1043 ✗ for var in listReverse(vars) loop
1044 ✗ if not ComponentRef.isSimple(var.name) then
1045 ✗ top_node := ComponentRef.node(ComponentRef.last(var.name));
1046
1047 ✗ if InstNode.isConnector(top_node) and InstNode.isPublic(top_node) then
1048 topLevelConnectorVars := var :: topLevelConnectorVars;
1049
1050 ✗ if Variable.isFlow(var) then
1051 flowVars := var :: flowVars;
1052 elseif Variable.isInput(var) then
1053 inputVars := var :: flowVars;
1054 end if;
1055 end if;
1056 end if;
1057 end for;
1058 end collectExtractorModelVariables;
1059
1060 constant Absyn.TypeSpec REAL_TYPE_SPEC = Absyn.TypeSpec.TPATH(Absyn.Path.IDENT("Real"), NONE());
1061
1062 function createExtractorModelDummyFn
1063 input list<Variable> connectors;
1064 output Function fn;
1065 protected
1066 SCode.ClassDef cdef;
1067 SCode.Element output_param, elem;
1068 InstNode fn_node;
1069 list<SCode.Element> params;
1070 SCode.Mod output_binding;
1071 SCode.Comment cmt;
1072 algorithm
1073 ✗ output_binding := SCodeUtil.makeMod(binding = SOME(Absyn.Exp.INTEGER(0)));
1074 ✗ output_param := SCode.Element.COMPONENT("dummy", SCode.defaultPrefixes, SCode.defaultOutputAttr,
1075 REAL_TYPE_SPEC, output_binding, SCode.noComment, NONE(), Absyn.dummyInfo);
1076 ✗ params := listAppend(list(createExtractorModelDummyFnInput(c) for c in connectors), {output_param});
1077
1078 ✗ cdef := SCode.ClassDef.PARTS(
1079 params,
1080 {},
1081 {},
1082 {},
1083 {},
1084 {},
1085 {},
1086 NONE()
1087 );
1088
1089 ✗ cmt := SCode.COMMENT(SOME(SCode.Annotation.ANNOTATION(SCodeUtil.makeMod(subMods =
1090 {SCode.SubMod.NAMEMOD("Inline", SCodeUtil.makeMod(binding = SOME(Absyn.Exp.BOOL(false))))}))), NONE());
1091
1092 ✗ elem := SCode.Element.CLASS(
1093 "dummy",
1094 SCode.defaultPrefixes,
1095 SCode.Encapsulated.NOT_ENCAPSULATED(),
1096 SCode.Partial.NOT_PARTIAL(),
1097 SCode.Restriction.R_FUNCTION(SCode.FunctionRestriction.FR_NORMAL_FUNCTION(Absyn.FunctionPurity.PURE())),
1098 cdef,
1099 cmt,
1100 Absyn.dummyInfo
1101 );
1102
1103 ✗ fn_node := InstNode.new(elem, InstNode.EMPTY_NODE());
1104 ✗ fn_node := Function.instFunctionNode(fn_node, NFInstContext.FUNCTION, Absyn.dummyInfo);
1105 ✗ fn :: _ := Function.typeNodeCache(fn_node);
1106 end createExtractorModelDummyFn;
1107
1108 function createExtractorModelDummyFnInput
1109 input Variable var;
1110 output SCode.Element inputElem;
1111 algorithm
1112 ✗ inputElem := SCode.Element.COMPONENT(
1113 ComponentRef.toFlatString(var.name, BaseModelica.defaultFormat),
1114 SCode.defaultPrefixes,
1115 SCode.defaultInputAttr,
1116 REAL_TYPE_SPEC,
1117 SCode.Mod.NOMOD(),
1118 SCode.noComment,
1119 NONE(),
1120 Absyn.dummyInfo
1121 );
1122 end createExtractorModelDummyFnInput;
1123
1124 function createExtractorModelDummyEq
1125 input Variable var;
1126 input String varType;
1127 input Function fn;
1128 input list<Expression> args;
1129 output Equation eq;
1130 input output FunctionTree funcs;
1131 input output Integer index;
1132 protected
1133 Function indexed_fn;
1134 Absyn.Path fn_name;
1135 DAE.ElementSource src = DAE.emptyElementSource;
1136 String var_name;
1137 algorithm
1138 while true loop
1139 ✗ index := index + 1;
1140 ✗ fn_name := Absyn.Path.IDENT("f" + String(index));
1141
1142 ✗ if not FunctionTree.hasKey(funcs, fn_name) then
1143 break;
1144 end if;
1145 end while;
1146
1147 ✗ indexed_fn := Function.setName(fn_name, fn);
1148 ✗ var_name := ComponentRef.toString(Variable.name(var));
1149 ✗ src := ElementSource.addCommentToSource(src,
1150 SOME(SCode.Comment.COMMENT(NONE(), SOME("Dummy equation for " + var_name + " " + varType + " variable"))));
1151
1152 ✗ eq := Equation.makeEquality(
1153 Expression.REAL(0),
1154 Expression.CALL(Call.makeTypedCall(indexed_fn, args, Variability.CONTINUOUS, Purity.PURE)),
1155 Type.REAL(),
1156 src,
1157 InstNode.fromHandle(fn.node)
1158 );
1159
1160 ✗ funcs := FunctionTree.add(funcs, fn_name, indexed_fn);
1161 end createExtractorModelDummyEq;
1162
1163 annotation(__OpenModelica_Interface="nf_frontend");
1164 end NFInstUtil;
1165