Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 68.9% 241 / 0 / 350
Functions: -% 0 / 1 / 1
Branches: 52.9% 129 / 0 / 244

OMCompiler/Compiler/Script/NFApi.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 NFApi
37
38 import Absyn;
39 import AbsynUtil;
40 import SCode;
41 import DAE;
42 import NFModifier.Modifier;
43 import SimCode;
44 import Values;
45
46 protected
47
48 import Inst = NFInst;
49 import InstUtil = NFInstUtil;
50 import NFBinding.Binding;
51 import NFComponent.Component;
52 import ComponentRef = NFComponentRef;
53 import Dimension = NFDimension;
54 import Expression = NFExpression;
55 import Import = NFImport;
56 import NFClass.Class;
57 import NFInstNode.InstNode;
58 import NFInstNode.InstNodeType;
59 import NFModifier.ModifierScope;
60 import Equation = NFEquation;
61 import NFType.Type;
62 import Subscript = NFSubscript;
63 import Connection = NFConnection;
64 import InstContext = NFInstContext;
65
66 import Absyn.Path;
67 import AbsynToSCode;
68 import CevalScriptBackend;
69 import NFInstanceAPI;
70 import Config;
71 import ConvertDAE = NFConvertDAE;
72 import DAEUtil;
73 import Dump;
74 import EvalConstants = NFEvalConstants;
75 import ErrorExt;
76 import ExecStat.{execStat,execStatReset};
77 import Flags;
78 import FlagsUtil;
79 import FlatModel = NFFlatModel;
80 import Flatten = NFFlatten;
81 import Global;
82 import JSON;
83 import List;
84 import Lookup = NFLookup;
85 import MetaModelica.Dangerous;
86 import NFCall.Call;
87 import Ceval = NFCeval;
88 import NFClassTree.ClassTree;
89 import NFFlatten.FunctionTree;
90 import NFPrefixes.{Variability, Purity};
91 import NFSections.Sections;
92 import Package = NFPackage;
93 import Prefixes = NFPrefixes;
94 import Restriction = NFRestriction;
95 import Scalarize = NFScalarize;
96 import SimCodeMain;
97 import SimplifyExp = NFSimplifyExp;
98 import SimplifyModel = NFSimplifyModel;
99 import SymbolTable;
100 import Typing = NFTyping;
101 import UnitCheck = NFUnitCheck;
102 import Util;
103 import ValuesMake;
104 import Variable = NFVariable;
105 import VerifyModel = NFVerifyModel;
106 import SCodeUtil;
107 import InstSettings = NFInst.InstSettings;
108 import MetaModelica.Dangerous.listReverseInPlace;
109
110 constant InstContext.Type ANNOTATION_CONTEXT = intBitOr(NFInstContext.RELAXED, NFInstContext.ANNOTATION);
111 constant InstContext.Type INST_API_ANNOTATION_CONTEXT = intBitOr(ANNOTATION_CONTEXT, NFInstContext.INSTANCE_API);
112 constant InstContext.Type FAST_CONTEXT = intBitOr(NFInstContext.RELAXED, NFInstContext.FAST_LOOKUP);
113
114 public
115 function evaluateAnnotation
116 "Instantiates the annotation class, gets the DAE and populates the annotation result"
117 input Absyn.Program absynProgram;
118 input Absyn.Path classPath;
119 input Absyn.Annotation inAnnotation;
120 output String outString = "";
121 protected
122 Boolean b, s;
123 algorithm
124 20 b := FlagsUtil.set(Flags.SCODE_INST, true);
125 20 s := FlagsUtil.set(Flags.NF_SCALARIZE, true); // #5689
126 try
127 20 outString := evaluateAnnotation_dispatch(absynProgram, classPath, inAnnotation);
128 20 FlagsUtil.set(Flags.SCODE_INST, b);
129 20 FlagsUtil.set(Flags.NF_SCALARIZE, s);
130 else
131 ✗ FlagsUtil.set(Flags.SCODE_INST, b);
132 ✗ FlagsUtil.set(Flags.NF_SCALARIZE, s);
133 ✗ fail();
134 end try;
135 end evaluateAnnotation;
136
137 protected
138 function evaluateAnnotation_dispatch
139 "Instantiates the annotation class, gets the DAE and populates the annotation result"
140 input Absyn.Program absynProgram;
141 input Absyn.Path classPath;
142 input Absyn.Annotation inAnnotation;
143 input Boolean addAnnotationName = false;
144 output String outString = "";
145 protected
146 InstNode top, inst_cls, anncls, inst_anncls;
147 String name, annName, str;
148 SCode.Program program;
149 list<Absyn.ElementArg> el = {};
150 list<String> stringLst = {};
151 Absyn.Exp absynExp;
152 Expression exp, save;
153 SourceInfo info;
154 list<Absyn.ElementArg> mod, stripped_mod, graphics_mod;
155 Absyn.EqMod eqmod;
156 SCode.Mod smod;
157 DAE.DAElist dae;
158 Type ty;
159 Variability var;
160 algorithm
161 175 stringLst := {};
162 175 Absyn.ANNOTATION(el) := inAnnotation;
163
164
2/2
✓ Branch 1 taken 175 times.
✓ Branch 2 taken 175 times.
350 for e in listReverse(el) loop
165
166 175 e := AbsynUtil.createChoiceArray(e);
167
168 str := matchcontinue e
169 case Absyn.MODIFICATION(
170 path = Absyn.IDENT(annName),
171 modification = SOME(Absyn.CLASSMOD({}, eqmod as Absyn.EQMOD(absynExp))),
172 info = info)
173 algorithm
174 // no need for the class if there are no crefs
175
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 if AbsynUtil.onlyLiteralsInEqMod(eqmod) then
176 33 (program, top) := NFInstanceAPI.mkTop(absynProgram, scodeFor(absynProgram), annName);
177 33 inst_cls := top;
178 else
179 // run the front-end front
180 ✗ (program, name, inst_cls) := frontEndFront(absynProgram, classPath);
181 end if;
182
183 33 exp := NFInst.instExp(absynExp, inst_cls, ANNOTATION_CONTEXT, info);
184 33 (exp, ty, var) := Typing.typeExp(exp, ANNOTATION_CONTEXT, info);
185 // exp := NFCeval.evalExp(exp);
186 33 exp := SimplifyExp.simplify(exp);
187 33 str := Expression.toString(exp);
188 33 then
189 stringAppendList({annName, "=", str});
190
191 case Absyn.MODIFICATION(
192 path = Absyn.IDENT(annName),
193 modification = SOME(Absyn.CLASSMOD(mod, Absyn.NOMOD())),
194 info = info)
195 algorithm
196 // no need for the class if there are no crefs
197
2/2
✓ Branch 1 taken 92 times.
✓ Branch 2 taken 50 times.
142 if AbsynUtil.onlyLiteralsInAnnotationMod(mod) then
198 92 (program, top) := NFInstanceAPI.mkTop(absynProgram, scodeFor(absynProgram), annName);
199 92 inst_cls := top;
200 else
201 // run the front-end front
202 50 (program, name, inst_cls) := frontEndFront(absynProgram, classPath);
203 end if;
204
205 141 (stripped_mod, graphics_mod) := AbsynUtil.stripGraphicsAndInteractionModification(mod);
206
207 282 smod := AbsynToSCode.translateMod(SOME(Absyn.CLASSMOD(stripped_mod, Absyn.NOMOD())), SCode.NOT_FINAL(), SCode.NOT_EACH(), NONE(), info);
208 141 anncls := Lookup.lookupClassName(Absyn.IDENT(annName), inst_cls, ANNOTATION_CONTEXT, Absyn.dummyInfo, checkAccessViolations = false);
209 141 inst_anncls := NFInst.expand(anncls, ANNOTATION_CONTEXT);
210 141 inst_anncls := NFInst.instClass(inst_anncls, Modifier.create(smod, annName, ModifierScope.CLASS(annName), inst_cls, 0), NFAttributes.DEFAULT_ATTR, true, 0, 0, inst_cls, ANNOTATION_CONTEXT);
211 // Instantiate expressions (i.e. anything that can contains crefs, like
212 // bindings, dimensions, etc). This is done as a separate step after
213 // instantiation to make sure that lookup is able to find the correct nodes.
214 139 NFInst.instExpressions(inst_anncls, context = ANNOTATION_CONTEXT, settings = NFInst.DEFAULT_SETTINGS);
215
216 // Mark structural parameters.
217 139 NFInst.updateImplicitVariability(inst_anncls, Flags.isSet(Flags.EVAL_PARAM), ANNOTATION_CONTEXT);
218
219 139 dae := frontEndBack(inst_anncls, annName, false);
220 139 str := DAEUtil.getVariableBindingsStr(DAEUtil.daeElements(dae));
221
222
4/4
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 118 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 5 times.
139 if (listMember(annName, {"Icon", "Diagram", "choices"})) and not listEmpty(graphics_mod) then
223 try
224
6/12
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 16 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 16 times.
16 {Absyn.MODIFICATION(modification = SOME(Absyn.CLASSMOD(eqMod = Absyn.EQMOD(exp = absynExp))))} := graphics_mod;
225 16 exp := NFInst.instExp(absynExp, inst_cls, ANNOTATION_CONTEXT, info);
226 16 (exp, ty, var) := Typing.typeExp(exp, ANNOTATION_CONTEXT, info);
227 save := exp;
228 try
229 16 exp := NFCeval.evalExp(save);
230 else
231 2 exp := EvalConstants.evaluateExp(save, info);
232 end try;
233 16 exp := SimplifyExp.simplify(exp);
234 16 str := str + "," + Expression.toString(exp);
235 else
236 // just don't fail!
237 end try;
238 end if;
239
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 17 times.
383 then
240 if addAnnotationName
241 then stringAppendList({annName, "(", str, ")"})
242 else str;
243
244 case Absyn.MODIFICATION(path = Absyn.IDENT(annName), modification = NONE(), info = info)
245 algorithm
246 ✗ (program, top) := NFInstanceAPI.mkTop(absynProgram, scodeFor(absynProgram), annName);
247 ✗ inst_cls := top;
248
249 ✗ anncls := Lookup.lookupClassName(Absyn.IDENT(annName), inst_cls, ANNOTATION_CONTEXT, Absyn.dummyInfo, checkAccessViolations = false);
250
251 ✗ inst_anncls := NFInst.instantiate(anncls, context = ANNOTATION_CONTEXT);
252 // Instantiate expressions (i.e. anything that can contains crefs, like
253 // bindings, dimensions, etc). This is done as a separate step after
254 // instantiation to make sure that lookup is able to find the correct nodes.
255 ✗ NFInst.instExpressions(inst_anncls, context = ANNOTATION_CONTEXT, settings = NFInst.DEFAULT_SETTINGS);
256
257 // Mark structural parameters.
258 ✗ NFInst.updateImplicitVariability(inst_anncls, Flags.isSet(Flags.EVAL_PARAM), ANNOTATION_CONTEXT);
259
260 ✗ dae := frontEndBack(inst_anncls, annName, false);
261 ✗ str := DAEUtil.getVariableBindingsStr(DAEUtil.daeElements(dae));
262 ✗ then
263 if addAnnotationName
264 then stringAppendList({annName, "(", str, ")"})
265 else str;
266
267 case Absyn.MODIFICATION(path = Absyn.IDENT(annName), info = info)
268 algorithm
269 3 str := "error evaluating: annotation(" + Dump.unparseElementArgStr(e) + ")";
270 3 str := Util.escapeQuotes(str);
271 3 then
272 stringAppendList({annName, "(\"", str, "\")"});
273
274 end matchcontinue;
275
276 stringLst := str :: stringLst;
277 end for;
278
279 175 outString := stringDelimitList(stringLst, ", ");
280
281
1/2
✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
175 if Flags.isSet(Flags.EXEC_STAT) then
282 ✗ execStat("NFApi.evaluateAnnotation_dispatch("+ AbsynUtil.pathString(classPath) + " annotation(" + stringDelimitList(List.map(el, Dump.unparseElementArgStr), ", ") + ")");
283 end if;
284
285 end evaluateAnnotation_dispatch;
286
287 public
288 function evaluateAnnotations
289 "Instantiates the annotation class, gets the DAE and populates the annotation result"
290 input Absyn.Program absynProgram;
291 input Absyn.Path classPath;
292 input list<Absyn.Element> inElements;
293 output list<String> outStringLst = {};
294 protected
295 Boolean b, s;
296 algorithm
297 10 b := FlagsUtil.set(Flags.SCODE_INST, true);
298 10 s := FlagsUtil.set(Flags.NF_SCALARIZE, true); // #5689
299 try
300 10 outStringLst := evaluateAnnotations_dispatch(absynProgram, classPath, inElements);
301 10 FlagsUtil.set(Flags.SCODE_INST, b);
302 10 FlagsUtil.set(Flags.NF_SCALARIZE, s);
303 else
304 ✗ FlagsUtil.set(Flags.SCODE_INST, b);
305 ✗ FlagsUtil.set(Flags.NF_SCALARIZE, s);
306 ✗ fail();
307 end try;
308 end evaluateAnnotations;
309
310 protected
311 function evaluateAnnotations_dispatch
312 "Instantiates the annotation class, gets the DAE and populates the annotation result"
313 input Absyn.Program absynProgram;
314 input Absyn.Path classPath;
315 input list<Absyn.Element> inElements;
316 output list<String> outStringLst = {};
317 protected
318 String str;
319 list<list<Absyn.ElementArg>> elArgs = {}, el = {};
320 list<String> stringLst = {};
321 list<Absyn.ComponentItem> items;
322 Option<Absyn.ConstrainClass> cc;
323 list<Absyn.ElementArg> anns;
324 Option<Absyn.Comment> cmt;
325 algorithm
326 // handle the annotations
327
2/2
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 10 times.
183 for i in inElements loop
328 elArgs := match i
329 case Absyn.ELEMENT(specification = Absyn.COMPONENTS(components = items), constrainClass = cc)
330 algorithm
331 163 el := AbsynUtil.getAnnotationsFromItems(items, AbsynUtil.getAnnotationsFromConstraintClass(cc));
332 163 then
333 listAppend(el, elArgs);
334
335 case Absyn.ELEMENT(specification = Absyn.COMPONENTS())
336 then {}::elArgs;
337
338 case Absyn.ELEMENT(specification = Absyn.CLASSDEF(
339 class_ = Absyn.CLASS(body = Absyn.DERIVED(comment = cmt))),
340 constrainClass = cc)
341 algorithm
342 anns := match cmt
343 case SOME(Absyn.COMMENT(annotation_ = SOME(Absyn.ANNOTATION(anns))))
344 then anns;
345 else {};
346 end match;
347 7 then
348 listAppend(anns, AbsynUtil.getAnnotationsFromConstraintClass(cc))::elArgs;
349
350 case Absyn.ELEMENT(specification = Absyn.COMPONENTS())
351 then {} :: elArgs;
352
353 case Absyn.ELEMENT(specification = Absyn.CLASSDEF(class_ = Absyn.CLASS(body = Absyn.DERIVED())))
354 then {} :: elArgs;
355
356
357 else elArgs;
358 end match;
359 end for;
360
361
2/2
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 10 times.
181 for l in elArgs loop
362 stringLst := {};
363
364
2/2
✓ Branch 2 taken 155 times.
✓ Branch 3 taken 171 times.
326 for e in listReverse(l) loop
365 155 str := evaluateAnnotation_dispatch(absynProgram, classPath, Absyn.ANNOTATION({e}), true);
366 stringLst := str :: stringLst;
367 end for;
368
369 171 str := stringDelimitList(stringLst, ", ");
370 171 outStringLst := stringAppendList({"{", str, "}"}) :: outStringLst;
371 end for;
372
373
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 if Flags.isSet(Flags.EXEC_STAT) then
374 ✗ execStat("NFApi.evaluateAnnotations_dispatch("+ AbsynUtil.pathString(classPath) + " annotation(" + stringDelimitList(List.map(List.flatten(elArgs), Dump.unparseElementArgStr), ", ") + ")");
375 end if;
376 end evaluateAnnotations_dispatch;
377
378 public
379 function mkFullyQual
380 input Absyn.Program absynProgram;
381 input Absyn.Path classPath;
382 input Absyn.Path pathToQualify;
383 input Boolean failOnError = false;
384 output Absyn.Path qualPath = pathToQualify;
385 protected
386 InstNode expanded_cls, cls;
387 SCode.Program program;
388 String name, id1, id2;
389 Boolean b, s;
390 InstContext.Type context;
391 algorithm
392 // do some quick checks
393 // classPath is already fully qualified
394 // check if the paths start with the same id and the second path is qualified
395 () := match (classPath, pathToQualify)
396 case (Absyn.QUALIFIED(id1, _), Absyn.QUALIFIED(id2, _)) guard id1 == id2
397 algorithm
398 4943 return;
399 then ();
400 else ();
401 end match;
402
403 // else, do the hard stuff!
404 2156 b := FlagsUtil.set(Flags.SCODE_INST, true);
405 2156 s := FlagsUtil.set(Flags.NF_SCALARIZE, true); // #5689
406 try
407
1/2
✓ Branch 1 taken 2156 times.
✗ Branch 2 not taken.
2156 if not Flags.isSet(Flags.NF_API_NOISE) then
408 2156 ErrorExt.setCheckpoint("NFApi.mkFullyQual");
409 end if;
410 // run the front-end front
411 2156 (program, name, expanded_cls) := frontEndLookup(absynProgram, classPath);
412
413 2156 context := InstContext.set(NFInstContext.RELAXED, NFInstContext.FAST_LOOKUP);
414
415 // if is derived qualify in the parent
416
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2156 times.
2156 if InstNode.isDerivedClass(expanded_cls) then
417 ✗ cls := Lookup.lookupClassName(pathToQualify, InstNode.classParent(expanded_cls), context, Absyn.dummyInfo, checkAccessViolations = false);
418 else // qualify in the class
419 2156 cls := Lookup.lookupClassName(pathToQualify, expanded_cls, context, Absyn.dummyInfo, checkAccessViolations = false);
420 end if;
421
422 2132 qualPath := InstNode.fullPath(cls);
423
424
1/2
✓ Branch 1 taken 2132 times.
✗ Branch 2 not taken.
2132 if not Flags.isSet(Flags.NF_API_NOISE) then
425 2132 ErrorExt.rollBack("NFApi.mkFullyQual");
426 end if;
427
428 2132 FlagsUtil.set(Flags.SCODE_INST, b);
429 2132 FlagsUtil.set(Flags.NF_SCALARIZE, s);
430 else
431
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 if not Flags.isSet(Flags.NF_API_NOISE) then
432 24 ErrorExt.rollBack("NFApi.mkFullyQual");
433 end if;
434
435 24 FlagsUtil.set(Flags.SCODE_INST, b);
436 24 FlagsUtil.set(Flags.NF_SCALARIZE, s);
437
438
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if failOnError then
439 ✗ fail();
440 else
441 // do not fail, just return the Absyn path
442 qualPath := pathToQualify;
443 end if;
444 end try;
445
446
1/2
✓ Branch 1 taken 2156 times.
✗ Branch 2 not taken.
2156 if Flags.isSet(Flags.EXEC_STAT) then
447 ✗ execStat("NFApi.mkFullyQual(" + AbsynUtil.pathString(classPath) + ", " + AbsynUtil.pathString(pathToQualify) + ") -> " + AbsynUtil.pathString(qualPath));
448 end if;
449 end mkFullyQual;
450
451 public function clearCache
452 "Release every tree the instance API is holding. A session that has dropped
453 its program otherwise keeps its last models instantiated until something
454 instantiates again."
455 algorithm
456 6 setGlobalRoot(Global.instNFInstCacheIndex, {});
457 6 setGlobalRoot(Global.instNFLookupCacheIndex, {});
458 6 NFInstanceAPI.clearTopScopeCache();
459 end clearCache;
460
461 protected
462 function frontEndFront
463 input Absyn.Program absynProgram;
464 input Absyn.Path classPath;
465 output SCode.Program program;
466 output String name;
467 output InstNode inst_cls;
468 protected
469 list<tuple<tuple<Absyn.Program, Absyn.Path>, tuple<SCode.Program, String, InstNode>>> cache;
470 algorithm
471 50 cache := getGlobalRoot(Global.instNFInstCacheIndex);
472
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 7 times.
50 if not listEmpty(cache) then
473
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 for i in cache loop
474
1/2
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
43 if referenceEq(absynProgram, Util.tuple21(Util.tuple21(i))) then
475
2/2
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 7 times.
43 if AbsynUtil.pathEqual(classPath, Util.tuple22(Util.tuple21(i))) then
476 36 (program, name, inst_cls) := Util.tuple22(i);
477 36 return;
478 end if;
479
480 // program changed, wipe the cache!
481 cache := {};
482 7 setGlobalRoot(Global.instNFInstCacheIndex, cache);
483
484 7 break;
485 else
486 ✗ if AbsynUtil.pathEqual(classPath, Util.tuple22(Util.tuple21(i))) then
487 // class reloaded, wipe the cache!
488 cache := {};
489 ✗ setGlobalRoot(Global.instNFInstCacheIndex, cache);
490
491 ✗ break;
492 end if;
493 end if;
494 end for;
495 end if;
496
497 14 (program, name, inst_cls) := frontEndFront_dispatch(absynProgram, classPath);
498
499
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if listLength(cache) > 100 then
500 // trim it down, keep 10
501 ✗ cache := List.firstN(cache, 10);
502 end if;
503
504 13 cache := ((absynProgram,classPath), (program, name, inst_cls))::cache;
505 13 setGlobalRoot(Global.instNFInstCacheIndex, cache);
506 end frontEndFront;
507
508 protected
509 function frontEndFront_dispatch
510 input Absyn.Program absynProgram;
511 input Absyn.Path classPath;
512 output SCode.Program program;
513 output String name;
514 output InstNode inst_cls;
515 protected
516 InstNode top, cls;
517 algorithm
518 14 name := AbsynUtil.pathString(classPath);
519
520 14 (program, top) := NFInstanceAPI.mkTop(absynProgram, scodeFor(absynProgram), name);
521
522 // Look up the class to instantiate and mark it as the root class.
523 14 cls := Lookup.lookupClassName(classPath, top, NFInstContext.RELAXED, Absyn.dummyInfo, checkAccessViolations = false);
524 14 cls := InstNode.makeRootClass(cls);
525
526 // Instantiate the class.
527 14 inst_cls := NFInst.instantiate(cls, context = NFInstContext.RELAXED);
528
529 13 NFInst.insertGeneratedInners(inst_cls, top, NFInstContext.RELAXED);
530
531 // Instantiate expressions (i.e. anything that can contains crefs, like
532 // bindings, dimensions, etc). This is done as a separate step after
533 // instantiation to make sure that lookup is able to find the correct nodes.
534 13 NFInst.instExpressions(inst_cls, context = NFInstContext.RELAXED, settings = NFInst.DEFAULT_SETTINGS);
535
536 // Mark structural parameters.
537 13 NFInst.updateImplicitVariability(inst_cls, Flags.isSet(Flags.EVAL_PARAM), NFInstContext.RELAXED);
538
539
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if Flags.isSet(Flags.EXEC_STAT) then
540 ✗ execStat("NFApi.frontEndFront_dispatch(" + name + ")");
541 end if;
542
543 13 Inst.clearCaches();
544 end frontEndFront_dispatch;
545
546 protected
547 function frontEndBack
548 input InstNode inst_cls;
549 input String name;
550 input Boolean scalarize = true;
551 output DAE.DAElist dae;
552 protected
553 FlatModel flat_model;
554 FunctionTree funcs;
555 AvlTreePathFunction.Tree daeFuncs;
556 algorithm
557 // Type the class.
558 139 Typing.typeClass(inst_cls, NFInstContext.RELAXED);
559
560 // Flatten and simplify the model.
561 139 flat_model := Flatten.flatten(inst_cls, Absyn.Path.IDENT(name));
562 139 flat_model := EvalConstants.evaluate(flat_model, NFInstContext.RELAXED);
563 139 flat_model := UnitCheck.checkUnits(flat_model);
564 139 flat_model := SimplifyModel.simplify(flat_model);
565 139 flat_model := Package.collectConstants(flat_model);
566 139 funcs := Flatten.collectFunctions(flat_model);
567
568 // Scalarize array components in the flat model.
569
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 if Flags.isSet(Flags.NF_SCALARIZE) /* and scalarize */ then
570 139 flat_model := Scalarize.scalarize(flat_model);
571 else
572 // Remove empty arrays from variables
573 ✗ flat_model.variables := List.filterOnFalse(flat_model.variables, Variable.isEmptyArray);
574 end if;
575
576 139 VerifyModel.verify(flat_model, InstNode.isPartial(inst_cls));
577
578 // Convert the flat model to a DAE.
579 139 (dae, daeFuncs) := ConvertDAE.convert(flat_model, funcs);
580
581
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 if Flags.isSet(Flags.EXEC_STAT) then
582 ✗ execStat("NFApi.frontEndBack(" + AbsynUtil.pathString(InstNode.enclosingScopePath(inst_cls)) + ", name: " + name + ", scalarize: " + boolString(scalarize) + ")");
583 end if;
584
585 end frontEndBack;
586
587 protected
588 function frontEndLookup
589 input Absyn.Program absynProgram;
590 input Absyn.Path classPath;
591 output SCode.Program program;
592 output String name;
593 output InstNode expanded_cls;
594 protected
595 list<tuple<tuple<Absyn.Program, Absyn.Path>, tuple<SCode.Program, String, InstNode>>> cache;
596 algorithm
597 4676 cache := getGlobalRoot(Global.instNFLookupCacheIndex);
598
2/2
✓ Branch 0 taken 2736 times.
✓ Branch 1 taken 1940 times.
4676 if not listEmpty(cache) then
599
2/2
✓ Branch 0 taken 2743 times.
✓ Branch 1 taken 34 times.
2777 for i in cache loop
600
2/2
✓ Branch 2 taken 2690 times.
✓ Branch 3 taken 53 times.
2743 if referenceEq(absynProgram, Util.tuple21(Util.tuple21(i))) then
601
2/2
✓ Branch 3 taken 318 times.
✓ Branch 4 taken 2372 times.
2690 if AbsynUtil.pathEqual(classPath, Util.tuple22(Util.tuple21(i))) then
602 318 (program, name, expanded_cls) := Util.tuple22(i);
603 318 return;
604 end if;
605
606 // program changed, wipe the cache!
607 cache := {};
608 2372 setGlobalRoot(Global.instNFLookupCacheIndex, cache);
609
610 2372 break;
611 else
612
2/2
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 41 times.
53 if AbsynUtil.pathEqual(classPath, Util.tuple22(Util.tuple21(i))) then
613 // class reloaded, wipe the cache!
614 cache := {};
615 12 setGlobalRoot(Global.instNFLookupCacheIndex, cache);
616
617 12 break;
618 end if;
619 end if;
620 end for;
621 end if;
622
623 4358 (program, name, expanded_cls) := frontEndLookup_dispatch(absynProgram, classPath);
624
625
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4345 times.
4345 if listLength(cache) > 100 then
626 // trim it down, keep 10
627 ✗ cache := List.firstN(cache, 10);
628 end if;
629
630 4345 cache := ((absynProgram,classPath), (program, name, expanded_cls))::cache;
631 4345 setGlobalRoot(Global.instNFLookupCacheIndex, cache);
632 end frontEndLookup;
633
634 protected
635 function frontEndLookup_dispatch
636 input Absyn.Program absynProgram;
637 input Absyn.Path classPath;
638 output SCode.Program program;
639 output String name;
640 output InstNode expanded_cls;
641 protected
642 InstNode top, cls;
643 algorithm
644 4358 name := AbsynUtil.pathString(classPath);
645
646 4358 (program, top) := NFInstanceAPI.mkTop(absynProgram, scodeFor(absynProgram), name);
647
648
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4355 times.
4358 if AbsynUtil.pathEqual(classPath, Absyn.IDENT("AllLoadedClasses")) then
649 3 expanded_cls := top;
650 else
651 4355 cls := Inst.lookupRootClass(classPath, top, FAST_CONTEXT);
652
653 // Expand the class.
654 4354 expanded_cls := NFInst.expand(cls, FAST_CONTEXT);
655 end if;
656
657
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4345 times.
4345 if Flags.isSet(Flags.EXEC_STAT) then
658 ✗ execStat("NFApi.frontEndLookup_dispatch("+ name +")");
659 end if;
660
661 4345 Inst.clearCaches();
662 end frontEndLookup_dispatch;
663
664 public
665 function getInheritedClasses
666 input Absyn.Path classPath;
667 input Absyn.Program program;
668 output list<Absyn.Path> extendsPaths;
669 protected
670 InstNode cls_node;
671 Class cls;
672 array<InstNode> exts;
673 Integer start_idx;
674 algorithm
675
2/2
✓ Branch 1 taken 548 times.
✓ Branch 2 taken 2517 times.
3065 if not Flags.isSet(Flags.SCODE_INST) then
676 extendsPaths := {};
677 548 return;
678 end if;
679
680 2517 (_, _, cls_node) := frontEndLookup(program, classPath);
681
682
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2504 times.
2504 if not InstNode.isClass(cls_node) then
683 extendsPaths := {};
684 ✗ return;
685 end if;
686
687 2504 cls := InstNode.getClass(cls_node);
688
689 extendsPaths := match cls
690 16 case Class.EXPANDED_DERIVED() then {InstNode.fullPath(cls.baseClass, true)};
691 else
692 algorithm
693 2488 exts := ClassTree.getExtends(Class.classTree(cls));
694 // Skip the first extends of a class extends since it would just return
695 // the name of the class itself. That's technically correct, but not
696 // very useful and a potential user trap when using the API recursively.
697
2/2
✓ Branch 2 taken 2487 times.
✓ Branch 3 taken 1 time.
2488 start_idx := if SCodeUtil.isClassExtends(InstNode.definition(cls_node)) then 2 else 1;
698
2/2
✓ Branch 0 taken 402 times.
✓ Branch 1 taken 2488 times.
2890 then
699 list(InstNode.fullPath(exts[i], true) for i in start_idx:arrayLength(exts));
700 end match;
701 end getInheritedClasses;
702
703 function getNthInheritedClass
704 input Absyn.Path classPath;
705 input Integer index;
706 input Absyn.Program program;
707 output Values.Value result;
708 protected
709 InstNode cls_node;
710 Class cls;
711 array<InstNode> exts;
712 algorithm
713
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if not Flags.isSet(Flags.SCODE_INST) then
714 ✗ result := ValuesMake.makeBoolean(false);
715 ✗ return;
716 end if;
717
718 3 (_, _, cls_node) := frontEndLookup(program, classPath);
719
720
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if not InstNode.isClass(cls_node) then
721 ✗ result := ValuesMake.makeBoolean(false);
722 ✗ return;
723 end if;
724
725 3 cls := InstNode.getClass(cls_node);
726
727 exts := match cls
728 3 case Class.EXPANDED_DERIVED() then listArray({cls.baseClass});
729 ✗ else ClassTree.getExtends(Class.classTree(cls));
730 end match;
731
732
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
6 if index < 1 or index > arrayLength(exts) then
733 ✗ result := ValuesMake.makeBoolean(false);
734 ✗ return;
735 end if;
736
737 3 result := ValuesMake.makeCodeTypeName(InstNode.fullPath(exts[index], true));
738 end getNthInheritedClass;
739
740 function getModelInstance
741 input Absyn.Path classPath;
742 input Absyn.Path contextPath;
743 input String modifier;
744 input Boolean prettyPrint;
745 output Values.Value res;
746 protected
747 JSON json;
748 algorithm
749 try
750 96 json := NFInstanceAPI.buildModelInstanceJSON(SymbolTable.getAbsyn(), SOME(SymbolTable.getSCode()), classPath, contextPath, modifier);
751 48 res := Values.STRING(JSON.toString(json, prettyPrint));
752 48 execStat("JSON.toString");
753 48 Inst.clearCaches();
754 else
755 ✗ Inst.clearCaches();
756 ✗ fail();
757 end try;
758 end getModelInstance;
759
760 function getModelInstanceReference
761 "Like getModelInstance, but instead of serializing the model instance to a
762 JSON string it stores the (boxed) JSON structure in memory and returns an
763 integer handle to it (see issue #15219). OMEdit, which links the compiler
764 in-process, can then read the structure directly via the C function
765 ModelInstanceReference_get, avoiding both JSON string generation here and
766 JSON string parsing in OMEdit. The handle must be freed with
767 releaseModelInstanceReference. A handle of 0 indicates failure."
768 input Absyn.Path classPath;
769 input Absyn.Path contextPath;
770 input String modifier;
771 output Values.Value res;
772 protected
773 JSON json;
774 Integer handle;
775 algorithm
776 try
777 ✗ json := NFInstanceAPI.buildModelInstanceJSON(SymbolTable.getAbsyn(), SOME(SymbolTable.getSCode()), classPath, contextPath, modifier);
778 ✗ json := JSON.toListForm(json);
779 ✗ execStat("NFApi.toListForm");
780 ✗ handle := NFInstanceAPI.storeModelInstanceReference(json);
781 ✗ res := Values.INTEGER(handle);
782 ✗ Inst.clearCaches();
783 else
784 ✗ Inst.clearCaches();
785 ✗ fail();
786 end try;
787 end getModelInstanceReference;
788
789 function getModelInstanceIconReference
790 "Like getModelInstanceReference, but stores only what an icon renderer reads:
791 the class' Icon with its extends chain, and the locally declared connector
792 components' Placement and type Icon. A lookup and an expand per class,
793 against an instantiation and a dump of every component."
794 input Absyn.Path classPath;
795 output Values.Value res;
796 protected
797 JSON json;
798 Integer handle;
799 algorithm
800 try
801 30 json := NFInstanceAPI.buildModelInstanceIconJSON(SymbolTable.getAbsyn(), SOME(SymbolTable.getSCode()), classPath);
802 15 json := JSON.toListForm(json);
803 15 handle := NFInstanceAPI.storeModelInstanceReference(json);
804 15 res := Values.INTEGER(handle);
805 15 Inst.clearCaches();
806 else
807 ✗ Inst.clearCaches();
808 ✗ fail();
809 end try;
810 end getModelInstanceIconReference;
811
812 function getModelInstanceAnnotation
813 input Absyn.Path classPath;
814 input list<String> filter;
815 input Boolean prettyPrint;
816 output Values.Value res;
817 protected
818 JSON json;
819 algorithm
820 try
821 6 json := NFInstanceAPI.buildModelInstanceAnnotationJSON(SymbolTable.getAbsyn(), SOME(SymbolTable.getSCode()), classPath, filter);
822 3 res := Values.STRING(JSON.toString(json, prettyPrint));
823 3 Inst.clearCaches();
824 else
825 ✗ Inst.clearCaches();
826 ✗ fail();
827 end try;
828 end getModelInstanceAnnotation;
829
830 function getModelInstanceAnnotationReference
831 "Like getModelInstanceAnnotation, but returns an integer handle to an
832 in-memory JSON structure instead of a JSON string (see
833 getModelInstanceReference and issue #15219)."
834 input Absyn.Path classPath;
835 input list<String> filter;
836 output Values.Value res;
837 protected
838 JSON json;
839 Integer handle;
840 algorithm
841 try
842 ✗ json := NFInstanceAPI.buildModelInstanceAnnotationJSON(SymbolTable.getAbsyn(), SOME(SymbolTable.getSCode()), classPath, filter);
843 ✗ json := JSON.toListForm(json);
844 ✗ handle := NFInstanceAPI.storeModelInstanceReference(json);
845 ✗ res := Values.INTEGER(handle);
846 ✗ Inst.clearCaches();
847 else
848 ✗ Inst.clearCaches();
849 ✗ fail();
850 end try;
851 end getModelInstanceAnnotationReference;
852
853 function releaseModelInstanceReference
854 "Releases a handle previously returned by getModelInstanceReference or
855 getModelInstanceAnnotationReference. Returns true on success."
856 input Integer handle;
857 output Values.Value res;
858 algorithm
859
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 res := Values.BOOL(NFInstanceAPI.releaseModelInstanceReferenceImpl(handle));
860 end releaseModelInstanceReference;
861
862 function modifierToJSON
863 input String modifier;
864 input Boolean prettyPrint;
865 output Values.Value jsonString;
866 algorithm
867 ✗ jsonString := Values.STRING(JSON.toString(NFInstanceAPI.modifierJSON(modifier), prettyPrint));
868 end modifierToJSON;
869
870 function scodeFor
871 "SymbolTable's already translated SCode, when `absynProgram` is its program.
872 NFInstanceAPI retranslates the whole library without it."
873 input Absyn.Program absynProgram;
874 output Option<SCode.Program> scodeProgram =
875 if referenceEq(absynProgram, SymbolTable.getAbsyn()) then SOME(SymbolTable.getSCode()) else NONE();
876 end scodeFor;
877
878 uniontype MoveEnv
879 record MOVE_ENV
880 InstNode scope;
881 Absyn.Path destinationPath;
882 InstNode destination;
883 end MOVE_ENV;
884 end MoveEnv;
885
886 function updateMovedClassPaths
887 "Updates all the paths inside of a class that is being moved such that the
888 paths are still valid in the new location."
889 input output Absyn.Class cls "The class definition";
890 input Absyn.Path clsPath "The fully qualified path of the class";
891 input Absyn.Within destination "The destination package (or top scope)";
892 protected
893 InstNode top, src_node, dst_node = InstNode.EMPTY_NODE();
894 MoveEnv env;
895 Absyn.Path dst_path, p;
896 Boolean found = false;
897 algorithm
898 // Make a top node and look up the class in it.
899 12 (_, top) := NFInstanceAPI.mkTop(SymbolTable.getAbsyn(), SOME(SymbolTable.getSCode()), AbsynUtil.pathString(clsPath));
900 6 src_node := Inst.lookupRootClass(clsPath, top, FAST_CONTEXT);
901 6 Inst.expand(src_node, FAST_CONTEXT);
902
903 // Get the destination path including the class name.
904 dst_path := match destination
905 4 case Absyn.Within.WITHIN() then AbsynUtil.suffixPath(destination.path, InstNode.name(src_node));
906 2 else Absyn.Path.IDENT(InstNode.name(src_node));
907 end match;
908
909 // Also look up the destination package, which is needed to resolve shadowing issues in some cases.
910 // The destination package might not exist yet, but in that case there's nothing to look up in it anyway,
911 // so the first existing enclosing scope also works.
912 6 dst_node := top;
913 p := dst_path;
914
915
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
10 while not found and not AbsynUtil.pathIsIdent(p) loop
916 try
917 4 p := AbsynUtil.pathPrefix(p);
918 4 dst_node := Lookup.lookupName(p, top, FAST_CONTEXT, false);
919 4 Inst.expand(dst_node, FAST_CONTEXT);
920 found := true;
921 else
922 end try;
923 end while;
924
925 6 env := MOVE_ENV(src_node, dst_path, dst_node);
926 6 cls.body := updateMovedClassDef(cls.body, env);
927 end updateMovedClassPaths;
928
929 function updateMovedClass
930 input output Absyn.Class cls;
931 input MoveEnv env;
932 protected
933 InstNode cls_node;
934 MoveEnv cls_env;
935 algorithm
936
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if classHasScope(cls) then
937 // Change the scope in the environment to this class, if the class has its
938 // own scope (i.e. is a long class definition).
939 ✗ cls_node := Lookup.lookupLocalSimpleName(cls.name, env.scope);
940 ✗ Inst.expand(cls_node, FAST_CONTEXT);
941 ✗ cls_env := MoveEnv.MOVE_ENV(cls_node, AbsynUtil.suffixPath(env.destinationPath, cls.name), env.destination);
942 else
943 cls_env := env;
944 end if;
945
946 2 cls.body := updateMovedClassDef(cls.body, cls_env);
947 end updateMovedClass;
948
949 function classHasScope
950 input Absyn.Class cls;
951 output Boolean hasScope;
952 algorithm
953 hasScope := match cls.body
954 case Absyn.ClassDef.PARTS() then true;
955 case Absyn.ClassDef.CLASS_EXTENDS() then true;
956 else false;
957 end match;
958 end classHasScope;
959
960 function updateMovedClassDef
961 input output Absyn.ClassDef cdef;
962 input MoveEnv env;
963 algorithm
964 () := match cdef
965 case Absyn.ClassDef.PARTS()
966 algorithm
967
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 6 times.
20 cdef.classParts := list(updateMovedClassPart(p, env) for p in cdef.classParts);
968
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
12 cdef.ann := list(updateMovedAnnotation(a, env) for a in cdef.ann);
969 then
970 ();
971
972 case Absyn.ClassDef.DERIVED()
973 algorithm
974 2 cdef.typeSpec := updateMovedTypeSpec(cdef.typeSpec, env);
975 2 cdef.attributes := updateMovedElementAttributes(cdef.attributes, env);
976
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 cdef.arguments := list(updateMovedElementArg(a, env) for a in cdef.arguments);
977 2 cdef.comment := updateMovedCommentOpt(cdef.comment, env);
978 then
979 ();
980
981 case Absyn.ClassDef.CLASS_EXTENDS()
982 algorithm
983 ✗ cdef.modifications := list(updateMovedElementArg(a, env) for a in cdef.modifications);
984 ✗ cdef.parts := list(updateMovedClassPart(p, env) for p in cdef.parts);
985 ✗ cdef.ann := list(updateMovedAnnotation(a, env) for a in cdef.ann);
986 then
987 ();
988
989 case Absyn.ClassDef.PDER()
990 algorithm
991 ✗ cdef.functionName := updateMovedPath(cdef.functionName, env);
992 ✗ cdef.comment := updateMovedCommentOpt(cdef.comment, env);
993 then
994 ();
995
996 else ();
997 end match;
998 end updateMovedClassDef;
999
1000 function updateMovedClassPart
1001 input output Absyn.ClassPart part;
1002 input MoveEnv env;
1003 algorithm
1004 () := match part
1005 case Absyn.ClassPart.PUBLIC()
1006 algorithm
1007
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 6 times.
23 part.contents := list(updateMovedElementItem(i, env) for i in part.contents);
1008 then
1009 ();
1010
1011 case Absyn.ClassPart.PROTECTED()
1012 algorithm
1013 ✗ part.contents := list(updateMovedElementItem(i, env) for i in part.contents);
1014 then
1015 ();
1016
1017 case Absyn.ClassPart.EQUATIONS()
1018 algorithm
1019 1 part.contents := updateMovedEquationItems(part.contents, env);
1020 then
1021 ();
1022
1023 case Absyn.ClassPart.INITIALEQUATIONS()
1024 algorithm
1025 ✗ part.contents := updateMovedEquationItems(part.contents, env);
1026 then
1027 ();
1028
1029 case Absyn.ClassPart.ALGORITHMS()
1030 algorithm
1031 1 part.contents := updateMovedAlgorithmItems(part.contents, env);
1032 then
1033 ();
1034
1035 case Absyn.ClassPart.INITIALALGORITHMS()
1036 algorithm
1037 ✗ part.contents := updateMovedAlgorithmItems(part.contents, env);
1038 then
1039 ();
1040
1041 case Absyn.ClassPart.EXTERNAL()
1042 algorithm
1043 ✗ part.annotation_ := updateMovedAnnotationOpt(part.annotation_, env);
1044 then
1045 ();
1046
1047 else ();
1048 end match;
1049 end updateMovedClassPart;
1050
1051 function updateMovedElementItem
1052 input output Absyn.ElementItem item;
1053 input MoveEnv env;
1054 algorithm
1055 () := match item
1056 case Absyn.ElementItem.ELEMENTITEM()
1057 algorithm
1058 11 item.element := updateMovedElement(item.element, env);
1059 then
1060 ();
1061
1062 else ();
1063 end match;
1064 end updateMovedElementItem;
1065
1066 function updateMovedElement
1067 input output Absyn.Element element;
1068 input MoveEnv env;
1069 algorithm
1070 () := match element
1071 case Absyn.Element.ELEMENT()
1072 algorithm
1073 11 element.specification := updateMovedElementSpec(element.specification, env);
1074
1075
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 if isSome(element.constrainClass) then
1076 ✗ element.constrainClass :=
1077 SOME(updateMovedConstrainClass(Util.getOption(element.constrainClass), env));
1078 end if;
1079 then
1080 ();
1081
1082 else ();
1083 end match;
1084 end updateMovedElement;
1085
1086 function updateMovedConstrainClass
1087 input output Absyn.ConstrainClass cc;
1088 input MoveEnv env;
1089 algorithm
1090 ✗ cc.elementSpec := updateMovedElementSpec(cc.elementSpec, env);
1091 cc.comment := updateMovedCommentOpt(cc.comment, env);
1092 end updateMovedConstrainClass;
1093
1094 function updateMovedElementSpec
1095 input output Absyn.ElementSpec spec;
1096 input MoveEnv env;
1097 algorithm
1098 () := match spec
1099 case Absyn.ElementSpec.CLASSDEF()
1100 algorithm
1101 2 spec.class_ := updateMovedClass(spec.class_, env);
1102 then
1103 ();
1104
1105 case Absyn.ElementSpec.EXTENDS()
1106 algorithm
1107 ✗ spec.path := updateMovedPath(spec.path, env);
1108 ✗ spec.elementArg := list(updateMovedElementArg(a, env) for a in spec.elementArg);
1109 ✗ spec.annotationOpt := updateMovedAnnotationOpt(spec.annotationOpt, env);
1110 then
1111 ();
1112
1113 case Absyn.ElementSpec.COMPONENTS()
1114 algorithm
1115 9 spec.attributes := updateMovedElementAttributes(spec.attributes, env);
1116 9 spec.typeSpec := updateMovedTypeSpec(spec.typeSpec, env);
1117
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
27 spec.components := list(updateMovedComponentItem(c, env) for c in spec.components);
1118 then
1119 ();
1120
1121 else ();
1122 end match;
1123 end updateMovedElementSpec;
1124
1125 function updateMovedElementAttributes
1126 input output Absyn.ElementAttributes attr;
1127 input MoveEnv env;
1128 algorithm
1129
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if not listEmpty(attr.arrayDim) then
1130 ✗ attr.arrayDim := list(updateMovedSubscript(s, env) for s in attr.arrayDim);
1131 end if;
1132 end updateMovedElementAttributes;
1133
1134 function updateMovedElementArg
1135 input output Absyn.ElementArg arg;
1136 input MoveEnv env;
1137 algorithm
1138 () := match arg
1139 case Absyn.ElementArg.MODIFICATION()
1140 algorithm
1141 ✗ if isSome(arg.modification) then
1142 ✗ arg.modification := SOME(updateMovedModification(Util.getOption(arg.modification), env));
1143 end if;
1144 then
1145 ();
1146
1147 case Absyn.ElementArg.REDECLARATION()
1148 algorithm
1149 ✗ arg.elementSpec := updateMovedElementSpec(arg.elementSpec, env);
1150
1151 ✗ if isSome(arg.constrainClass) then
1152 ✗ arg.constrainClass := SOME(updateMovedConstrainClass(Util.getOption(arg.constrainClass), env));
1153 end if;
1154 then
1155 ();
1156
1157 else ();
1158 end match;
1159 end updateMovedElementArg;
1160
1161 function updateMovedModification
1162 input output Absyn.Modification mod;
1163 input MoveEnv env;
1164 protected
1165 Absyn.EqMod eq_mod;
1166 algorithm
1167
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
8 mod.elementArgLst := list(updateMovedElementArg(a, env) for a in mod.elementArgLst);
1168
1169 4 eq_mod := mod.eqMod;
1170 () := match eq_mod
1171 case Absyn.EqMod.EQMOD()
1172 algorithm
1173 4 eq_mod.exp := updateMovedExp(eq_mod.exp, env);
1174 4 mod.eqMod := eq_mod;
1175 then
1176 ();
1177
1178 else ();
1179 end match;
1180 end updateMovedModification;
1181
1182 function updateMovedComponentItem
1183 input output Absyn.ComponentItem item;
1184 input MoveEnv env;
1185 algorithm
1186 9 item.component := updateMovedComponent(item.component, env);
1187
1188
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 if isSome(item.condition) then
1189 ✗ item.condition := SOME(updateMovedExp(Util.getOption(item.condition), env));
1190 end if;
1191 end updateMovedComponentItem;
1192
1193 function updateMovedComponent
1194 input output Absyn.Component component;
1195 input MoveEnv env;
1196 algorithm
1197
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
9 if not listEmpty(component.arrayDim) then
1198
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
3 component.arrayDim := list(updateMovedSubscript(d, env) for d in component.arrayDim);
1199 end if;
1200
1201
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 5 times.
9 if isSome(component.modification) then
1202 8 component.modification := SOME(updateMovedModification(Util.getOption(component.modification), env));
1203 end if;
1204 end updateMovedComponent;
1205
1206 function updateMovedEquationItems
1207 input output list<Absyn.EquationItem> items;
1208 input MoveEnv env;
1209 algorithm
1210 1 items := AbsynUtil.traverseEquationItemListBidir(items,
1211 updateMovedExp_traverser, AbsynUtil.dummyTraverseExp, env);
1212 end updateMovedEquationItems;
1213
1214 function updateMovedAlgorithmItems
1215 input output list<Absyn.AlgorithmItem> items;
1216 input MoveEnv env;
1217 algorithm
1218 1 items := AbsynUtil.traverseAlgorithmItemListBidir(items,
1219 updateMovedExp_traverser, AbsynUtil.dummyTraverseExp, env);
1220 end updateMovedAlgorithmItems;
1221
1222 function updateMovedTypeSpec
1223 input output Absyn.TypeSpec ty;
1224 input MoveEnv env;
1225 algorithm
1226 () := match ty
1227 case Absyn.TypeSpec.TPATH()
1228 algorithm
1229 11 ty.path := updateMovedPath(ty.path, env);
1230
1231
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 if isSome(ty.arrayDim) then
1232 ✗ ty.arrayDim := SOME(list(updateMovedSubscript(s, env) for s in Util.getOption(ty.arrayDim)));
1233 end if;
1234 then
1235 ();
1236
1237 case Absyn.TypeSpec.TCOMPLEX()
1238 algorithm
1239 ✗ ty.path := updateMovedPath(ty.path, env);
1240
1241 ✗ if isSome(ty.arrayDim) then
1242 ✗ ty.arrayDim := SOME(list(updateMovedSubscript(s, env) for s in Util.getOption(ty.arrayDim)));
1243 end if;
1244 then
1245 ();
1246
1247 end match;
1248 end updateMovedTypeSpec;
1249
1250 function updateMovedPath
1251 input Absyn.Path path;
1252 input MoveEnv env;
1253 output Absyn.Path outPath = path;
1254 protected
1255 Absyn.Path qualified_path, new_path;
1256 Option<Absyn.Path> opt_path;
1257 InstNode node;
1258 algorithm
1259 // Try to look up the qualified path needed to be able to find the name in
1260 // this scope even if the root class that contains the scope is moved elsewhere.
1261 try
1262 11 qualified_path :=
1263 Lookup.lookupSimpleNameRootPath(AbsynUtil.pathFirstIdent(path), env.scope, FAST_CONTEXT);
1264 else
1265 // Lookup failed, leave the path unchanged.
1266 ✗ return;
1267 end try;
1268
1269 // If the path we found is fully qualified it means it refers to an element
1270 // outside the class' scope, and the original path needs to be updated so it
1271 // can be found from the destination scope.
1272
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 6 times.
11 if AbsynUtil.pathIsFullyQualified(qualified_path) then
1273
1274 5 qualified_path := AbsynUtil.makeNotFullyQualified(qualified_path);
1275
1276
3/6
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
5 if AbsynUtil.pathIsIdent(qualified_path) and
1277 AbsynUtil.pathFirstIdent(qualified_path) == AbsynUtil.pathFirstIdent(env.destinationPath) then
1278 // Special case, the path refers to the destination package, e.g. moving A.B.C into A.
1279 ✗ outPath := AbsynUtil.pathRest(path);
1280 else
1281 // Remove any part of the qualified path that's the same as the destination.
1282 5 opt_path := AbsynUtil.pathStripSamePrefix(qualified_path, env.destinationPath);
1283
1284 // Replace the first identifier in the original path with the remaining qualified path.
1285
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if isSome(opt_path) then
1286 5 SOME(new_path) := opt_path;
1287 5 outPath := AbsynUtil.pathReplaceFirst(path, new_path);
1288 end if;
1289 end if;
1290
1291 // Make sure the new path still refers to the same element, and isn't shadowed by another
1292 // element with the same name in the destination scope or any of its enclosing scopes.
1293 try
1294 // Look up the new path from the destination scope.
1295 5 node := Lookup.lookupSimpleName(AbsynUtil.pathFirstIdent(outPath), env.destination, FAST_CONTEXT);
1296
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 false := AbsynUtil.pathPrefixOf(InstNode.fullPath(node), qualified_path);
1297 // If the wrong element was found, then return the fully qualified path instead.
1298 ✗ outPath := AbsynUtil.pathReplaceFirst(path, qualified_path);
1299 ✗ outPath := AbsynUtil.makeFullyQualified(outPath);
1300 else
1301 end try;
1302 end if;
1303 end updateMovedPath;
1304
1305 function updateMovedCommentOpt
1306 input output Option<Absyn.Comment> cmt;
1307 input MoveEnv env;
1308 algorithm
1309
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if isSome(cmt) then
1310 ✗ cmt := SOME(updateMovedComment(Util.getOption(cmt), env));
1311 end if;
1312 end updateMovedCommentOpt;
1313
1314 function updateMovedComment
1315 input output Absyn.Comment cmt;
1316 input MoveEnv env;
1317 algorithm
1318 ✗ cmt.annotation_ := updateMovedAnnotationOpt(cmt.annotation_, env);
1319 end updateMovedComment;
1320
1321 function updateMovedAnnotationOpt
1322 input output Option<Absyn.Annotation> ann;
1323 input MoveEnv env;
1324 algorithm
1325 ✗ if isSome(ann) then
1326 ✗ ann := SOME(updateMovedAnnotation(Util.getOption(ann), env));
1327 end if;
1328 end updateMovedAnnotationOpt;
1329
1330 function updateMovedAnnotation
1331 input output Absyn.Annotation ann;
1332 input MoveEnv env;
1333 algorithm
1334 ✗ ann.elementArgs := list(updateMovedElementArg(a, env) for a in ann.elementArgs);
1335 end updateMovedAnnotation;
1336
1337 function updateMovedSubscript
1338 input output Absyn.Subscript sub;
1339 input MoveEnv env;
1340 algorithm
1341 () := match sub
1342 case Absyn.Subscript.SUBSCRIPT()
1343 algorithm
1344 ✗ sub.subscript := updateMovedExp(sub.subscript, env);
1345 then
1346 ();
1347
1348 else ();
1349 end match;
1350 end updateMovedSubscript;
1351
1352 function updateMovedExp
1353 input output Absyn.Exp exp;
1354 input MoveEnv env;
1355 algorithm
1356 4 exp := AbsynUtil.traverseExp(exp, updateMovedExp_traverser, env);
1357 end updateMovedExp;
1358
1359 function updateMovedExp_traverser
1360 input output Absyn.Exp exp;
1361 input output MoveEnv env;
1362 algorithm
1363 () := match exp
1364 case Absyn.Exp.CREF()
1365 algorithm
1366 7 exp.componentRef := updateMovedCref(exp.componentRef, env);
1367 then
1368 ();
1369
1370 case Absyn.Exp.CALL()
1371 algorithm
1372 4 exp.function_ := updateMovedCref(exp.function_, env);
1373 then
1374 ();
1375
1376 else ();
1377 end match;
1378 end updateMovedExp_traverser;
1379
1380 function updateMovedCref
1381 input output Absyn.ComponentRef cref;
1382 input MoveEnv env;
1383 protected
1384 Absyn.Path qualified_path;
1385 Absyn.ComponentRef qualified_cref;
1386 Option<Absyn.Path> opt_path;
1387 algorithm
1388
2/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
11 if AbsynUtil.crefIsFullyQualified(cref) or AbsynUtil.crefIsWild(cref) then
1389 ✗ return;
1390 end if;
1391
1392 // Try to look up the qualified path needed to be able to find the name in
1393 // this scope even if the root class that contains the scope is moved elsewhere.
1394 try
1395 11 qualified_path :=
1396 Lookup.lookupSimpleNameRootPath(AbsynUtil.crefFirstIdent(cref), env.scope, FAST_CONTEXT);
1397 else
1398 // Lookup failed, leave the path unchanged.
1399 ✗ return;
1400 end try;
1401
1402 // If the path we found is fully qualified it means we need to update the original cref.
1403
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 6 times.
11 if AbsynUtil.pathIsFullyQualified(qualified_path) then
1404 6 qualified_path := AbsynUtil.makeNotFullyQualified(qualified_path);
1405
1406
5/6
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 1 time.
✗ Branch 11 not taken.
6 if AbsynUtil.pathIsIdent(qualified_path) and
1407 AbsynUtil.pathFirstIdent(qualified_path) == AbsynUtil.pathFirstIdent(env.destinationPath) then
1408 // Special case, the cref refers to the destination package, e.g. moving A.B.C into A.
1409 1 cref := AbsynUtil.crefStripFirst(cref);
1410 else
1411 // Remove any part of the qualified path that's the same as the destination.
1412 5 opt_path := AbsynUtil.pathStripSamePrefix(qualified_path, env.destinationPath);
1413
1414 // Replace the first identifier in the original cref with the remaining qualified path.
1415
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if isSome(opt_path) then
1416 5 SOME(qualified_path) := opt_path;
1417 5 qualified_cref := AbsynUtil.pathToCref(qualified_path);
1418
1419
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
5 if AbsynUtil.crefIsQual(cref) then
1420 2 cref := AbsynUtil.joinCrefs(qualified_cref, AbsynUtil.crefStripFirst(cref));
1421 else
1422 cref := qualified_cref;
1423 end if;
1424 end if;
1425 end if;
1426 end if;
1427 end updateMovedCref;
1428
1429 public
1430 function translateResidualsDAE
1431 input Absyn.Path path;
1432 input String fileNamePrefix;
1433 output Boolean success = true;
1434 protected
1435 Boolean disable_single_flow_eq;
1436 list<String> non_std_flags;
1437 FlatModel flat_model;
1438 Flatten.FunctionTree funcs;
1439 Option<SimCode.SimulationSettings> simSettings;
1440 algorithm
1441 ✗ disable_single_flow_eq := FlagsUtil.set(Flags.DISABLE_SINGLE_FLOW_EQ, true);
1442 ✗ non_std_flags := FlagsUtil.appendConfigStringList(Flags.ALLOW_NON_STANDARD_MODELICA, "implicitParameterStartAttribute");
1443
1444 try
1445 ✗ (flat_model, funcs) := CevalScriptBackend.runFrontEndNF(path);
1446 ✗ (flat_model, funcs) := InstUtil.createExtractorModel(flat_model, funcs);
1447 ✗ InstUtil.dumpFlatModelDebug("translateResidualsDAE", flat_model, funcs);
1448
1449 ✗ simSettings := SOME(CevalScriptBackend.convertSimulationOptionsToSimCode(
1450 CevalScriptBackend.buildSimulationOptionsFromModelExperimentAnnotation(path, fileNamePrefix, NONE())));
1451 ✗ SimCodeMain.translateModelCallBackend(flat_model, funcs, path, fileNamePrefix, true, simSettings);
1452 else
1453 end try;
1454
1455 ✗ FlagsUtil.setConfigStringList(Flags.ALLOW_NON_STANDARD_MODELICA, non_std_flags);
1456 ✗ FlagsUtil.set(Flags.DISABLE_SINGLE_FLOW_EQ, disable_single_flow_eq);
1457 end translateResidualsDAE;
1458
1459 annotation(__OpenModelica_Interface="backend_main");
1460 end NFApi;
1461