Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 82.6% 333 / 0 / 403
Functions: -% 0 / 1 / 1
Branches: 80.2% 215 / 0 / 268

OMCompiler/Compiler/NFFrontEnd/NFEvalFunction.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 NFEvalFunction
37
38 import Binding = NFBinding;
39 import Call = NFCall;
40 import Class = NFClass;
41 import Component = NFComponent;
42 import ComponentRef = NFComponentRef;
43 import Dimension = NFDimension;
44 import Expression = NFExpression;
45 import NFCeval.EvalTarget;
46 import NFClassTree.ClassTree;
47 import NFFunction.Function;
48 import NFInstNode.InstNode;
49 import NFInstNode.CachedData;
50 import Record = NFRecord;
51 import Sections = NFSections;
52 import Statement = NFStatement;
53 import Subscript = NFSubscript;
54 import Type = NFType;
55
56 protected
57 import Absyn;
58 import Array;
59 import Autoconf;
60 import Ceval = NFCeval;
61 import DAE;
62 import ElementSource;
63 import ErrorExt;
64 import EvalFunctionExt = NFEvalFunctionExt;
65 import FFI;
66 import Flags;
67 import Global;
68 import InstContext = NFInstContext;
69 import MetaModelica.Dangerous.*;
70 import NFPrefixes.Variability;
71 import RangeIterator = NFRangeIterator;
72 import SCode;
73 import SCodeUtil;
74 import Settings;
75 import System;
76 import Testsuite;
77 import UnorderedMap;
78
79 type FlowControl = enumeration(NEXT, CONTINUE, BREAK, RETURN, ASSERTION);
80 type ArgumentMap = UnorderedMap<InstNode, Expression>;
81
82 constant InstContext.Type STATEMENT_CONTEXT = intBitOr(NFInstContext.FUNCTION, NFInstContext.ALGORITHM);
83 constant InstContext.Type IF_COND_CONTEXT = intBitOr(STATEMENT_CONTEXT, intBitOr(NFInstContext.IF, NFInstContext.CONDITION));
84
85 public
86 function evaluate
87 input Function fn;
88 input list<Expression> args;
89 input EvalTarget target;
90 output Expression result;
91 algorithm
92
2/2
✓ Branch 1 taken 274 times.
✓ Branch 2 taken 22718 times.
22992 if Function.isExternal(fn) then
93 274 result := evaluateExternal(fn, args, target);
94 elseif Function.isPartialDerivative(fn) then
95 // Partial derivatives of functions are differentiated by the backend, so
96 // make sure we don't try to evaluate the non-differentiated function body.
97 1 fail();
98 else
99 22717 result := evaluateNormal(fn, args, target.context);
100 end if;
101 end evaluate;
102
103 function evaluateNormal
104 input Function fn;
105 input list<Expression> args;
106 input InstContext.Type context;
107 output Expression result;
108 protected
109 list<Statement> fn_body;
110 ArgumentMap arg_map;
111 Integer call_count, limit;
112 Pointer<Integer> call_counter = fn.callCounter;
113 FlowControl ctrl;
114 InstContext.Type body_context;
115 algorithm
116 // Functions contain a mutable call counter that's increased by one at the
117 // start of each evaluation, and decreased by one when the evalution is
118 // finished. This is used to limit the number of recursive functions calls.
119 22717 call_count := Pointer.access(call_counter) + 1;
120 22717 limit := Flags.getConfigInt(Flags.EVAL_RECURSION_LIMIT);
121
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22717 times.
22717 if call_count > limit then
123 ✗ Pointer.update(call_counter, 0);
124 ✗ Error.addSourceMessage(Error.EVAL_RECURSION_LIMIT_REACHED,
125 {String(limit), AbsynUtil.pathString(Function.name(fn))}, InstNode.info(InstNode.fromHandle(fn.node)));
126 ✗ fail();
127 end if;
128
129 22717 Pointer.update(call_counter, call_count);
130
131 22717 body_context := InstContext.clearScopeFlags(context);
132
133 try
134 22717 fn_body := Function.getBody(fn);
135 22717 arg_map := createArgumentMap(fn.inputs, fn.outputs, fn.locals, args, mutableParams = true);
136 // TODO: Also apply replacements to the replacements themselves, i.e. the
137 // bindings of the function parameters. But they probably need to be
138 // sorted by dependencies first.
139 22717 fn_body := applyReplacements(arg_map, fn_body);
140 22717 fn_body := optimizeBody(fn_body);
141 22717 ctrl := evaluateStatements(fn_body, body_context);
142
143
2/2
✓ Branch 0 taken 22695 times.
✓ Branch 1 taken 3 times.
22698 if ctrl <> FlowControl.ASSERTION then
144 22695 result := createResult(arg_map, fn.outputs);
145 else
146 3 fail();
147 end if;
148 else
149 // Make sure we always decrease the call counter even if the evaluation fails.
150 24 Pointer.update(call_counter, call_count - 1);
151 24 fail();
152 end try;
153
154
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22693 times.
22693 if Flags.isSet(Flags.EVAL_FUNC_DUMP) then
155 ✗ print(AbsynUtil.pathString(Function.name(fn)) + " => ");
156 ✗ print(Expression.toString(result));
157 ✗ print("\nArguments:\n");
158 ✗ print(UnorderedMap.toString(arg_map, InstNode.name, Expression.toString));
159 ✗ print("\n");
160 end if;
161
162 22693 Pointer.update(call_counter, call_count - 1);
163 end evaluateNormal;
164
165 function evaluateExternal
166 input Function fn;
167 input list<Expression> args;
168 input EvalTarget target;
169 output Expression result;
170 protected
171 String name, lang;
172 ComponentRef output_ref;
173 Option<SCode.Annotation> ann;
174 list<Expression> ext_args;
175 algorithm
176
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 274 times.
274 Sections.EXTERNAL(name = name, args = ext_args, outputRef = output_ref, language = lang, ann = ann) :=
177 Class.getSections(InstNode.getClass(InstNode.fromHandle(fn.node)));
178
179 result := matchcontinue lang
180 case "builtin"
181 // Functions defined as 'external "builtin"', delegate to Ceval.
182 ✗ then Ceval.evalBuiltinCall(fn, args, NFCeval.noTarget);
183
184 case "FORTRAN 77"
185 // This had better be a Lapack function.
186 7 then evaluateExternal2(name, fn, args, ext_args);
187
188 case _
189 guard not InstContext.inInstanceAPI(target.context)
190 // For anything else, try to call the function via FFI.
191 267 then callExternalFunction(name, fn, args, ext_args, output_ref, ann);
192
193 else
194 algorithm
195
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 7 times.
11 if EvalTarget.hasInfo(target) then
196 8 Error.addSourceMessage(Error.FAILED_TO_EVALUATE_FUNCTION,
197 {AbsynUtil.pathString(fn.path)}, EvalTarget.getInfo(target));
198 end if;
199 11 then
200 fail();
201 end matchcontinue;
202 end evaluateExternal;
203
204 function evaluateRecordConstructor
205 "Evaluates a default record constructor call by replacing any field references
206 with the given arguments, optionally constant evaluating the resulting expression.
207
208 Example:
209 record R
210 Real x;
211 constant Real y = x / 2.0;
212 Real z;
213 end R;
214
215 CALL(R, {1.0, 2.0}) => RECORD(R, {1.0, 0.5, 2.0});
216 "
217 input Function fn;
218 input Type ty;
219 input list<Expression> args;
220 input Boolean evaluate = true;
221 output Expression result;
222 protected
223 ArgumentMap arg_map;
224 list<Expression> expl = {};
225 InstNode out_ty;
226 algorithm
227 // Map the record fields to the arguments of the constructor.
228 832 arg_map := createArgumentMap(fn.inputs, {}, fn.locals, args, mutableParams = false);
229
230 // Use the node of the return type to determine the order of the variables,
231 // since they might be reordered in the record constructor.
232 832 out_ty := Type.complexNode(fn.returnType);
233
234 // Fetch the new binding expressions for all the variables, both inputs and locals.
235
2/2
✓ Branch 4 taken 3153 times.
✓ Branch 5 taken 832 times.
4817 for c in ClassTree.getComponents(Class.classTree(InstNode.getClass(out_ty))) loop
236 3153 expl := UnorderedMap.getOrFail(c, arg_map) :: expl;
237 end for;
238
239 // Create a new record expression from the mapped arguments.
240 832 result := Expression.makeRecord(Function.name(fn), ty, listReverseInPlace(expl));
241
242 // Constant evaluate the expression if requested.
243
1/2
✓ Branch 0 taken 832 times.
✗ Branch 1 not taken.
832 if evaluate then
244 ✗ result := Ceval.evalExp(result);
245 end if;
246 end evaluateRecordConstructor;
247
248 protected
249
250 function createArgumentMap
251 input list<InstNode> inputs;
252 input list<NFInstNode.NodeHandle> outputs;
253 input list<InstNode> locals;
254 input list<Expression> args;
255 input Boolean mutableParams;
256 input Boolean buildArrayBinding = true;
257 output ArgumentMap map;
258 protected
259 Expression arg;
260 list<Expression> rest_args = args;
261 Function fn;
262 algorithm
263 23818 map := UnorderedMap.new<Expression>(InstNode.hash, InstNode.refEqual);
264
265 // Add inputs to the argument map. Inputs are never mutable.
266
2/2
✓ Branch 0 taken 41181 times.
✓ Branch 1 taken 23818 times.
64999 for i in inputs loop
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41181 times.
41181 arg :: rest_args := rest_args;
268 41181 UnorderedMap.add(i, arg, map);
269
270 // If the argument is a function partial application, also add the function
271 // node to the map so we can replace calls to it with the correct function.
272
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 41179 times.
41181 if Expression.isFunctionPointer(arg) then
273
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 for fn in Function.getCachedFuncs(i) loop
274 2 UnorderedMap.add(InstNode.fromHandle(fn.node), arg, map);
275 end for;
276 end if;
277 end for;
278
279 // Add outputs and local variables to the argument map.
280 // They sometimes need to be mutable and sometimes not.
281
2/2
✓ Branch 0 taken 1094 times.
✓ Branch 1 taken 22724 times.
23818 if mutableParams then
282
2/2
✓ Branch 0 taken 24983 times.
✓ Branch 1 taken 22724 times.
47707 for o in outputs loop
283 24983 map := addMutableArgument(InstNode.fromHandle(o), map, buildArrayBinding);
284 end for;
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22724 times.
22724 List.fold(locals, function addMutableArgument(buildArrayBinding = buildArrayBinding), map);
286 else
287
2/2
✓ Branch 0 taken 279 times.
✓ Branch 1 taken 1091 times.
1370 for o in outputs loop
288 279 map := addImmutableArgument(InstNode.fromHandle(o), map, buildArrayBinding);
289 end for;
290
2/2
✓ Branch 0 taken 259 times.
✓ Branch 1 taken 832 times.
1350 List.fold(locals, function addImmutableArgument(buildArrayBinding = buildArrayBinding), map);
291 end if;
292
293 // Apply the arguments to the arguments themselves. This is done after
294 // building the map to make sure all the arguments are available.
295 23815 UnorderedMap.apply(map, function applyBindingReplacement(map = map));
296 // Evaluate the values of outputs and local variables.
297 23815 UnorderedMap.apply(map, evaluateReplacement);
298 end createArgumentMap;
299
300 function addMutableArgument
301 input InstNode node;
302 input output ArgumentMap map;
303 input Boolean buildArrayBinding;
304 protected
305 Expression exp;
306 algorithm
307 40728 exp := getBindingExp(node, map, mutableParams = true, buildArrayBinding = buildArrayBinding);
308 40728 exp := Expression.makeMutable(exp);
309 40728 UnorderedMap.add(node, exp, map);
310 end addMutableArgument;
311
312 function addImmutableArgument
313 input InstNode node;
314 input output ArgumentMap map;
315 input Boolean buildArrayBinding;
316 protected
317 Expression exp;
318 algorithm
319 288 exp := getBindingExp(node, map, mutableParams = false, buildArrayBinding = buildArrayBinding);
320 285 UnorderedMap.add(node, exp, map);
321 end addImmutableArgument;
322
323 function getBindingExp
324 input InstNode node;
325 input ArgumentMap map;
326 input Boolean mutableParams;
327 input Boolean buildArrayBinding;
328 output Expression bindingExp;
329 protected
330 Component comp;
331 Binding binding;
332 algorithm
333 134046 comp := InstNode.component(node);
334 134046 binding := Component.getBinding(comp);
335
336
2/2
✓ Branch 1 taken 11354 times.
✓ Branch 2 taken 122692 times.
134046 if Binding.isBound(binding) then
337 11354 bindingExp := Expression.clone(Binding.getExp(binding));
338 else
339 122692 bindingExp := buildBinding(node, map, mutableParams, buildArrayBinding);
340 end if;
341 end getBindingExp;
342
343 function buildBinding
344 input InstNode node;
345 input ArgumentMap map;
346 input Boolean mutableParams;
347 input Boolean buildArrayBinding;
348 output Expression result;
349 protected
350 Type ty;
351 algorithm
352 122692 ty := InstNode.getType(node);
353 122692 ty := Type.mapDims(ty, function applyReplacementsDim(map = map));
354
355 result := match ty
356 case Type.ARRAY() guard buildArrayBinding
357
2/2
✓ Branch 1 taken 5661 times.
✓ Branch 2 taken 3 times.
5664 then
358 if Type.hasKnownSize(ty) then
359 Expression.fillType(ty, Expression.EMPTY(Type.arrayElementType(ty)))
360 else
361 Expression.ARRAY(ty, listArray({}), true);
362
363 6601 case Type.COMPLEX() then buildRecordBinding(node, map, mutableParams);
364 110427 else Expression.EMPTY(ty);
365 end match;
366 end buildBinding;
367
368 function applyReplacementsDim
369 input ArgumentMap map;
370 input output Dimension dim;
371 algorithm
372 dim := match dim
373 local
374 Expression exp;
375
376 case Dimension.EXP()
377 algorithm
378 1797 exp := Expression.map(dim.exp, function applyReplacements2(map = map));
379 1797 exp := Ceval.evalExp(exp);
380 1797 then
381 Dimension.fromExp(exp, Variability.CONSTANT);
382
383 else dim;
384 end match;
385 end applyReplacementsDim;
386
387 function buildRecordBinding
388 "Builds a binding for a record instance that doesn't have an explicit binding.
389 Binding expressions will be taken from the record fields when available, and
390 filled with empty expressions when not."
391 input InstNode recordNode;
392 input ArgumentMap map;
393 input Boolean mutableParams;
394 output Expression result;
395 protected
396 InstNode cls_node = InstNode.classScope(recordNode);
397 Class cls = InstNode.getClass(cls_node);
398 array<InstNode> comps;
399 list<Expression> bindings;
400 Expression exp;
401 ArgumentMap local_map;
402 algorithm
403 result := match cls
404 case Class.INSTANCED_CLASS(elements = ClassTree.FLAT_TREE(components = comps))
405 algorithm
406 bindings := {};
407 // Create a replacement tree for just the record instance. This is
408 // needed for records that contain local references such as:
409 // record R
410 // Real x;
411 // Real y = x;
412 // end R;
413 // In that case we need to replace the 'x' in the binding of 'y' with
414 // the binding expression of 'x'.
415 6598 local_map := UnorderedMap.new<Expression>(InstNode.hash, InstNode.refEqual);
416
417
2/2
✓ Branch 1 taken 93030 times.
✓ Branch 2 taken 6598 times.
99628 for comp in comps loop
418 93030 exp := getBindingExp(comp, map, mutableParams, buildArrayBinding = true);
419
420
2/2
✓ Branch 0 taken 93019 times.
✓ Branch 1 taken 11 times.
93030 if mutableParams then
421 93019 exp := Expression.makeMutable(exp);
422 end if;
423
424 93030 UnorderedMap.add(comp, exp, local_map);
425 end for;
426
427 // Replace references to record fields with those fields' bindings in the tree.
428 6598 UnorderedMap.apply(local_map, function applyBindingReplacement(map = local_map));
429 6598 bindings := UnorderedMap.valueList(local_map);
430 6598 then
431 Expression.makeRecord(InstNode.fullPath(cls_node), cls.ty, bindings);
432
433 ✗ case Class.TYPED_DERIVED() then buildRecordBinding(cls.baseClass, map, mutableParams);
434 end match;
435 end buildRecordBinding;
436
437 function applyBindingReplacement
438 input Expression exp;
439 input ArgumentMap map;
440 output Expression outExp;
441 algorithm
442 175716 outExp := Expression.map(exp, function applyReplacements2(map = map));
443 end applyBindingReplacement;
444
445 function applyReplacements
446 input ArgumentMap map;
447 input output list<Statement> fnBody;
448 algorithm
449 22717 fnBody := Statement.mapExpList(fnBody,
450 function Expression.map(func = function applyReplacements2(map = map)));
451 end applyReplacements;
452
453 function applyReplacements2
454 input ArgumentMap map;
455 input output Expression exp;
456 algorithm
457 exp := match exp
458 533969 case Expression.CREF() then applyReplacementCref(map, exp.cref, exp);
459 208368 case Expression.CALL() then applyReplacementCall(map, exp.call, exp);
460 2 case Expression.UNBOX() then exp.exp;
461 else exp;
462 end match;
463 end applyReplacements2;
464
465 function applyReplacementCref
466 input ArgumentMap map;
467 input ComponentRef cref;
468 input Expression exp;
469 output Expression outExp;
470 protected
471 list<ComponentRef> cref_parts;
472 Option<Expression> repl_exp;
473 InstNode parent, node;
474 algorithm
475 // Explode the cref into a list of parts in reverse order.
476 533969 cref_parts := ComponentRef.toListReverse(cref, includeScope = true);
477
478 // If the list is empty it's probably an iterator or _, which shouldn't be replaced.
479
1/2
✓ Branch 0 taken 533969 times.
✗ Branch 1 not taken.
533969 if listEmpty(cref_parts) then
480 outExp := exp;
481 else
482 // Look up the replacement for the first part in the replacement tree.
483 533969 parent := ComponentRef.node(listHead(cref_parts));
484 533969 repl_exp := UnorderedMap.get(parent, map);
485
486
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 533969 times.
✓ Branch 2 taken 114699 times.
✓ Branch 3 taken 419270 times.
533969 if isSome(repl_exp) then
487 419270 SOME(outExp) := repl_exp;
488 else
489 outExp := exp;
490 114699 return;
491 end if;
492
493 419270 outExp := Expression.applySubscripts(ComponentRef.getSubscripts(listHead(cref_parts)), outExp);
494 419270 cref_parts := listRest(cref_parts);
495
496
2/2
✓ Branch 0 taken 171571 times.
✓ Branch 1 taken 247699 times.
419270 if not listEmpty(cref_parts) then
497 try
498 // If the cref consists of more than one identifier we need to look up
499 // the corresponding record field in the expression.
500
2/2
✓ Branch 0 taken 171578 times.
✓ Branch 1 taken 171571 times.
343149 for cr in cref_parts loop
501 171578 node := ComponentRef.node(cr);
502 171578 outExp := Expression.makeImmutable(outExp);
503 171578 outExp := Expression.recordElement(InstNode.name(node), outExp);
504 171578 outExp := Expression.applySubscripts(ComponentRef.getSubscripts(cr), outExp);
505 end for;
506 else
507 ✗ Error.terminate(getInstanceName() + " could not find replacement for " +
508 ComponentRef.toString(cref), sourceInfo());
509 end try;
510 end if;
511
512 419270 outExp := Expression.map(outExp, function applyReplacements2(map = map));
513 end if;
514 end applyReplacementCref;
515
516 function applyReplacementCall
517 "Checks if a function call refers to a function pointer given as a function
518 partial application expression, and if so replaces the call."
519 input ArgumentMap map;
520 input Call call;
521 input Expression exp;
522 output Expression outExp;
523 protected
524 Option<Expression> repl_oexp;
525 Expression repl_exp;
526 list<Expression> args;
527 Function fn;
528 algorithm
529 outExp := match call
530 case Call.TYPED_CALL()
531 algorithm
532 208228 repl_oexp := UnorderedMap.get(InstNode.fromHandle(call.fn.node), map);
533
534
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 208228 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 208226 times.
208228 if isSome(repl_oexp) then
535 2 SOME(repl_exp) := repl_oexp;
536
537 outExp := match repl_exp
538 case Expression.CREF(ty = Type.FUNCTION(fn = fn))
539 algorithm
540 // A function pointer is just a function partial application without any extra arguments.
541 1 call.arguments := mergeFunctionApplicationArgs(call.fn, call.arguments, fn, {}, {});
542 1 call.fn := fn;
543 1 then
544 Expression.CALL(call);
545
546 case Expression.PARTIAL_FUNCTION_APPLICATION()
547 algorithm
548 1 fn := listHead(Function.getCachedFuncs(ComponentRef.node(repl_exp.fn)));
549 // Merge the arguments from the original call with the ones in the function partial application.
550 1 call.arguments := mergeFunctionApplicationArgs(call.fn, call.arguments, fn, repl_exp.args, repl_exp.argNames);
551 // Replace the function with the one in the function partial application.
552 1 call.fn := fn;
553 1 then
554 Expression.CALL(call);
555
556 else exp;
557 end match;
558 else
559 outExp := exp;
560 end if;
561 then
562 outExp;
563
564 else exp;
565 end match;
566 end applyReplacementCall;
567
568 function evaluateReplacement
569 "Evaluates the values of mutable variables."
570 input output Expression exp;
571 algorithm
572 () := match exp
573 case Expression.MUTABLE()
574 algorithm
575 40728 Expression.applyMutable(exp, evaluateReplacement2);
576 then
577 ();
578
579 else ();
580 end match;
581 end evaluateReplacement;
582
583 function evaluateReplacement2
584 input output Expression exp;
585 algorithm
586 exp := match exp
587 // A mutable expression, only evaluate the expression it contains.
588 case Expression.MUTABLE()
589 algorithm
590 93019 Expression.applyMutable(exp, evaluateReplacement2);
591 then
592 exp;
593
594 // A record expression, evaluate the fields but keep them mutable if they are.
595 case Expression.RECORD()
596 algorithm
597
4/4
✓ Branch 0 taken 93021 times.
✓ Branch 1 taken 6595 times.
✓ Branch 2 taken 93021 times.
✓ Branch 3 taken 6595 times.
106211 exp.elements := list(evaluateReplacement2(e) for e in exp.elements);
598 then
599 exp;
600
601
2/2
✓ Branch 1 taken 11351 times.
✓ Branch 2 taken 115803 times.
127154 else if Expression.contains(exp, Expression.isEmpty) then exp else Ceval.evalExp(exp);
602 end match;
603 end evaluateReplacement2;
604
605 function mergeFunctionApplicationArgs
606 input Function oldFn;
607 input list<Expression> oldArgs;
608 input Function newFn;
609 input list<Expression> newArgs;
610 input list<String> argNames;
611 output list<Expression> outArgs = {};
612 protected
613 UnorderedMap<String, Expression> arg_map;
614 list<Expression> args;
615 algorithm
616 2 arg_map := UnorderedMap.new<Expression>(stringHashDjb2, stringEq);
617
618 // Add default arguments from the slots.
619
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for s in newFn.slots loop
620
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 3 times.
4 if isSome(s.default) then
621 1 UnorderedMap.add(InstNode.name(s.node), Expression.unbox(Util.getOption(s.default)), arg_map);
622 end if;
623 end for;
624
625 // Add arguments from the function call we're replacing.
626 args := oldArgs;
627
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for i in oldFn.inputs loop
628 2 UnorderedMap.add(InstNode.name(i), Expression.unbox(listHead(args)), arg_map);
629 2 args := listRest(args);
630 end for;
631
632 // Add arguments from the function partial application expression.
633 args := newArgs;
634
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 for n in argNames loop
635 1 UnorderedMap.add(n, Expression.unbox(listHead(args)), arg_map);
636 1 args := listRest(args);
637 end for;
638
639
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for i in newFn.inputs loop
640 4 outArgs := UnorderedMap.getOrFail(InstNode.name(i), arg_map) :: outArgs;
641 end for;
642
643 2 outArgs := listReverseInPlace(outArgs);
644 end mergeFunctionApplicationArgs;
645
646 function optimizeBody
647 input output list<Statement> body;
648 algorithm
649
4/4
✓ Branch 0 taken 108500 times.
✓ Branch 1 taken 22717 times.
✓ Branch 2 taken 108500 times.
✓ Branch 3 taken 22717 times.
131217 body := list(Statement.map(s, optimizeStatement) for s in body);
650 end optimizeBody;
651
652 function optimizeStatement
653 input output Statement stmt;
654 algorithm
655 () := match stmt
656 local
657 Expression iter_exp;
658
659 // Replace iterators in for loops with mutable expressions, so we don't need
660 // to do it each time we enter a for loop during evaluation.
661 case Statement.FOR()
662 algorithm
663 // Make a mutable expression with a placeholder value.
664 561 iter_exp := Expression.makeMutable(Expression.EMPTY(InstNode.getType(stmt.iterator)));
665 // Replace the iterator with the expression in the body of the for loop.
666 561 stmt.body := Statement.replaceIteratorList(stmt.body, stmt.iterator, iter_exp);
667 // Replace the iterator node with the mutable expression too.
668 1122 stmt.iterator := InstNode.ITERATOR_NODE(iter_exp);
669 then
670 ();
671
672 else ();
673 end match;
674 end optimizeStatement;
675
676 function createResult
677 input ArgumentMap map;
678 input list<NFInstNode.NodeHandle> outputs;
679 output Expression exp;
680 protected
681 list<Expression> expl;
682 list<Type> types;
683 Expression e;
684 InstNode node;
685 algorithm
686
2/2
✓ Branch 1 taken 1288 times.
✓ Branch 2 taken 21414 times.
22702 if listLength(outputs) == 1 then
687 21414 node := InstNode.fromHandle(listHead(outputs));
688 21414 exp := Ceval.evalExp(UnorderedMap.getOrFail(node, map));
689 42828 exp := assertAssignedOutput({InstNode.name(node)}, exp, InstNode.info(node));
690 else
691 expl := {};
692 types := {};
693
694
2/2
✓ Branch 0 taken 3548 times.
✓ Branch 1 taken 1288 times.
4836 for h in outputs loop
695 3548 node := InstNode.fromHandle(h);
696 3548 e := Ceval.evalExp(UnorderedMap.getOrFail(node, map));
697 7096 e := assertAssignedOutput({InstNode.name(node)}, e, InstNode.info(node));
698 expl := e :: expl;
699 end for;
700
701 1288 expl := listReverseInPlace(expl);
702
4/4
✓ Branch 0 taken 3548 times.
✓ Branch 1 taken 1288 times.
✓ Branch 2 taken 3548 times.
✓ Branch 3 taken 1288 times.
4836 types := list(Expression.typeOf(e) for e in expl);
703 1288 exp := Expression.TUPLE(Type.TUPLE(types, NONE()), expl);
704 end if;
705 end createResult;
706
707 function assertAssignedOutput
708 input list<String> name;
709 input output Expression value;
710 input SourceInfo info;
711 input Boolean error = true;
712 protected
713 list<Record.Field> fields;
714 list<Expression> expl;
715 String name_str;
716 Option<String> opt_indices;
717 algorithm
718 value := match value
719 case Expression.RECORD()
720 algorithm
721 6039 fields := Type.recordFields(value.ty);
722 expl := {};
723
724
2/2
✓ Branch 0 taken 87238 times.
✓ Branch 1 taken 6039 times.
93277 for e in value.elements loop
725 174476 e := assertAssignedOutput(Record.Field.name(listHead(fields)) :: name, e, info, error = false);
726 expl := e :: expl;
727 87238 fields := listRest(fields);
728 end for;
729
730 6039 value.elements := listReverseInPlace(expl);
731 then
732 value;
733
734 case _
735 algorithm
736 // Check if the value is uninitialized or contains an uninitialized array element.
737 106161 opt_indices := findUnassignedElement(value);
738
739
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 106161 times.
✓ Branch 2 taken 105871 times.
✓ Branch 3 taken 290 times.
106161 if isNone(opt_indices) then
740 105871 return;
741 end if;
742
743 290 name_str := stringDelimitList(listReverse(name), ".");
744 290 name_str := name_str + Util.getOption(opt_indices);
745
746
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 288 times.
290 if error then
747 2 Error.addSourceMessageAsError(Error.UNASSIGNED_FUNCTION_OUTPUT,
748 {name_str}, info);
749 2 fail();
750 else
751 288 Error.addSourceMessage(Error.UNASSIGNED_FUNCTION_OUTPUT,
752 {name_str}, info);
753 end if;
754 288 then
755 // This will fail if the type is one that makeZero doesn't handle,
756 // but this should really be an error anyway so that's fine.
757 Expression.makeZero(Expression.typeOf(value));
758
759 else value;
760 end match;
761 end assertAssignedOutput;
762
763 function findUnassignedElement
764 "Checks if an expression contains uninitialized values and returns a string
765 option with either the position of the uninitialized value or an empty
766 string if the expression is a scalar."
767 input Expression value;
768 input list<Integer> indices = {};
769 output Option<String> indicesStr;
770 algorithm
771 indicesStr := match value
772 case Expression.EMPTY()
773 290 then SOME(List.toStringCustom(listReverse(indices), intString, "", "[", ", ", "]", inPrintEmpty = false));
774
775 case Expression.ARRAY()
776 algorithm
777 indicesStr := NONE();
778
779
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9983 times.
✓ Branch 2 taken 9983 times.
✗ Branch 3 not taken.
52829 for i in 1:arrayLength(value.elements) loop
780 32867 indicesStr := findUnassignedElement(arrayGetNoBoundsChecking(value.elements, i), i :: indices);
781
782
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 32867 times.
✓ Branch 2 taken 32863 times.
✓ Branch 3 taken 4 times.
32867 if isSome(indicesStr) then
783 break;
784 end if;
785 end for;
786 then
787 indicesStr;
788
789 else NONE();
790 end match;
791 end findUnassignedElement;
792
793 function evaluateStatements
794 input list<Statement> stmts;
795 input InstContext.Type context;
796 output FlowControl ctrl = FlowControl.NEXT;
797 algorithm
798
2/2
✓ Branch 0 taken 121295 times.
✓ Branch 1 taken 32027 times.
153322 for s in stmts loop
799 121295 ctrl := evaluateStatement(s, context);
800
801
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 121269 times.
121276 if ctrl <> FlowControl.NEXT then
802
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if ctrl == FlowControl.CONTINUE then
803 ctrl := FlowControl.NEXT;
804 end if;
805
806 break;
807 end if;
808 end for;
809 end evaluateStatements;
810
811 function evaluateStatement
812 input Statement stmt;
813 input InstContext.Type context;
814 output FlowControl ctrl;
815 algorithm
816 // adrpo: we really need some error handling here to detect which statement cannot be evaluated
817 // try
818 ctrl := match stmt
819 112424 case Statement.ASSIGNMENT() then evaluateAssignment(stmt.lhs, stmt.rhs, stmt.source, context);
820 699 case Statement.FOR() then evaluateFor(stmt.iterator, stmt.range, stmt.body, stmt.source, context);
821 6167 case Statement.IF() then evaluateIf(stmt.branches, stmt.source, context);
822 1921 case Statement.ASSERT() then evaluateAssert(stmt.condition, stmt, stmt.source, context);
823 2 case Statement.NORETCALL() then evaluateNoRetCall(stmt.exp, stmt.source, context);
824 80 case Statement.WHILE() then evaluateWhile(stmt.condition, stmt.body, stmt.source, context);
825 case Statement.RETURN() then FlowControl.RETURN;
826 case Statement.BREAK() then FlowControl.BREAK;
827 else
828 algorithm
829 ✗ Error.terminate(getInstanceName() + " failed on " + anyString(stmt) + "\n", sourceInfo());
830 ✗ then
831 fail();
832
833 end match;
834 //else
835 // Error.terminate(getInstanceName() + " failed to evaluate statement " + Statement.toString(stmt) + "\n", sourceInfo());
836 // fail();
837 //end try;
838 end evaluateStatement;
839
840 function evaluateAssignment
841 input Expression lhsExp;
842 input Expression rhsExp;
843 input DAE.ElementSource source;
844 input InstContext.Type context;
845 output FlowControl ctrl = FlowControl.NEXT;
846 algorithm
847 112424 assignVariable(lhsExp,
848 Ceval.evalExp(rhsExp, evalTargetFromSource(source, STATEMENT_CONTEXT, context)));
849 end evaluateAssignment;
850
851 public
852 function assignVariable
853 input Expression variable;
854 input Expression value;
855 algorithm
856 () := match (variable, value)
857 local
858 Expression var, val;
859 list<Expression> vals;
860 Mutable<Expression> var_ptr;
861
862 // variable := value
863 case (Expression.MUTABLE(exp = var_ptr), _)
864 algorithm
865 132296 Mutable.update(var_ptr, assignExp(Mutable.access(var_ptr), value));
866 then
867 ();
868
869 // (var1, var2, ...) := (value1, value2, ...)
870 case (Expression.TUPLE(), Expression.TUPLE(elements = vals))
871 algorithm
872
2/2
✓ Branch 0 taken 3557 times.
✓ Branch 1 taken 1291 times.
4848 for var in variable.elements loop
873
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3557 times.
3557 val :: vals := vals;
874 3557 assignVariable(var, val);
875 end for;
876 then
877 ();
878
879 // variable[subscript1, subscript2, ...] := value
880 case (Expression.SUBSCRIPTED_EXP(exp = Expression.MUTABLE(exp = var_ptr)), _)
881 algorithm
882 19043 assignSubscriptedVariable(var_ptr, variable.subscripts, value);
883 then
884 ();
885
886 // _ := value
887 case (Expression.CREF(cref = ComponentRef.WILD()), _)
888 then ();
889
890 else
891 algorithm
892 ✗ Error.terminate(getInstanceName() + " failed on " +
893 Expression.toString(variable) + " := " + Expression.toString(value), sourceInfo());
894 ✗ then
895 fail();
896
897 end match;
898 end assignVariable;
899
900 protected
901 function assignSubscriptedVariable
902 input Mutable<Expression> variable;
903 input list<Subscript> subscripts;
904 input Expression value;
905 protected
906 list<Subscript> subs;
907 algorithm
908
4/4
✓ Branch 0 taken 21937 times.
✓ Branch 1 taken 19043 times.
✓ Branch 2 taken 21937 times.
✓ Branch 3 taken 19043 times.
40980 subs := list(Subscript.eval(s) for s in subscripts);
909 19043 Mutable.update(variable, assignArrayElement(Mutable.access(variable), subs, value));
910 end assignSubscriptedVariable;
911
912 function assignArrayElement
913 input Expression arrayExp;
914 input list<Subscript> subscripts;
915 input Expression value;
916 output Expression result;
917 protected
918 Expression sub, val;
919 list<Subscript> rest_subs;
920 Integer idx;
921 array<Expression> subs, vals;
922 algorithm
923 result := match (arrayExp, subscripts)
924 case (Expression.ARRAY(), Subscript.INDEX(sub) :: rest_subs) guard Expression.isScalarLiteral(sub)
925 algorithm
926 22085 idx := Expression.toInteger(sub);
927
928
2/2
✓ Branch 0 taken 19213 times.
✓ Branch 1 taken 2872 times.
22085 if listEmpty(rest_subs) then
929 19213 arrayUpdate(arrayExp.elements, idx, value);
930 else
931 2872 arrayUpdate(arrayExp.elements, idx,
932 assignArrayElement(arrayGet(arrayExp.elements, idx), rest_subs, value));
933 end if;
934 then
935 arrayExp;
936
937 case (Expression.ARRAY(), Subscript.SLICE(sub) :: rest_subs)
938 algorithm
939 ✗ subs := Expression.arrayElements(sub);
940 ✗ vals := Expression.arrayElements(value);
941
942 ✗ if arrayLength(subs) > arrayLength(vals) then
943 ✗ fail();
944 end if;
945
946 ✗ if listEmpty(rest_subs) then
947 ✗ for i in 1:arrayLength(subs) loop
948 ✗ sub := arrayGetNoBoundsChecking(subs, i);
949 ✗ val := arrayGetNoBoundsChecking(vals, i);
950 ✗ idx := Expression.toInteger(sub);
951 ✗ arrayUpdate(arrayExp.elements, idx, val);
952 end for;
953 else
954 ✗ for i in 1:arrayLength(subs) loop
955 ✗ sub := arrayGetNoBoundsChecking(subs, i);
956 ✗ val := arrayGetNoBoundsChecking(vals, i);
957 ✗ idx := Expression.toInteger(sub);
958 ✗ arrayUpdate(arrayExp.elements, idx,
959 assignArrayElement(arrayGet(arrayExp.elements, idx), rest_subs, val));
960 end for;
961 end if;
962 then
963 arrayExp;
964
965 case (Expression.ARRAY(), Subscript.WHOLE() :: rest_subs)
966 algorithm
967
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if listEmpty(rest_subs) then
968 ✗ arrayExp.elements := arrayCopy(Expression.arrayElements(value));
969 else
970
7/8
✓ Branch 1 taken 192 times.
✓ Branch 2 taken 22 times.
✓ Branch 4 taken 192 times.
✓ Branch 5 taken 22 times.
✓ Branch 7 taken 192 times.
✓ Branch 8 taken 22 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 22 times.
664 arrayExp.elements := listArray(list(assignArrayElement(e, rest_subs, v) threaded for
971 e in arrayExp.elements, v in Expression.arrayElements(value)));
972 end if;
973 then
974 arrayExp;
975
976 else
977 algorithm
978 ✗ Error.terminate(getInstanceName() + ": unimplemented case for " +
979 Expression.toString(arrayExp) +
980 Subscript.toStringList(subscripts) + " = " +
981 Expression.toString(value), sourceInfo());
982 ✗ then
983 fail();
984
985 end match;
986 end assignArrayElement;
987
988 function assignExp
989 input Expression lhs;
990 input Expression rhs;
991 output Expression result;
992 algorithm
993 result := match lhs
994 case Expression.RECORD()
995 2678 then assignRecord(lhs, rhs);
996
997 // TODO: Handle arrays.
998
999 else rhs;
1000 end match;
1001 end assignExp;
1002
1003 function assignRecord
1004 input Expression lhs;
1005 input Expression rhs;
1006 output Expression result;
1007 algorithm
1008 result := match rhs
1009 local
1010 list<Expression> elems;
1011 Expression e, val;
1012 ClassTree cls_tree;
1013 array<InstNode> comps;
1014 Type ty;
1015
1016 case Expression.RECORD()
1017 algorithm
1018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2678 times.
2678 Expression.RECORD(elements = elems) := lhs;
1019
1020
2/2
✓ Branch 0 taken 36626 times.
✓ Branch 1 taken 2678 times.
39304 for v in rhs.elements loop
1021
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36626 times.
36626 e :: elems := elems;
1022 36626 assignVariable(e, v);
1023 end for;
1024 then
1025 lhs;
1026
1027 case Expression.CREF()
1028 algorithm
1029 ✗ Expression.RECORD(elements = elems) := lhs;
1030 ✗ cls_tree := Class.classTree(InstNode.getClass(ComponentRef.node(rhs.cref)));
1031 ✗ comps := ClassTree.getComponents(cls_tree);
1032
1033 ✗ for c in comps loop
1034 ✗ e :: elems := elems;
1035 ✗ ty := InstNode.getType(c);
1036 ✗ val := Expression.CREF(Type.liftArrayLeftList(ty, Type.arrayDims(rhs.ty)),
1037 ComponentRef.prefixCref(c, ty, {}, rhs.cref));
1038 ✗ assignVariable(e, val);
1039 end for;
1040 then
1041 lhs;
1042
1043 else rhs;
1044 end match;
1045 end assignRecord;
1046
1047 function evaluateFor
1048 input InstNode iterator;
1049 input Option<Expression> range;
1050 input list<Statement> forBody;
1051 input DAE.ElementSource source;
1052 input InstContext.Type context;
1053 output FlowControl ctrl = FlowControl.NEXT;
1054 protected
1055 RangeIterator range_iter;
1056 Mutable<Expression> iter_exp;
1057 Expression range_exp, value;
1058 list<Statement> body = forBody;
1059 Integer i = 0, limit = Flags.getConfigInt(Flags.EVAL_LOOP_LIMIT);
1060 algorithm
1061 699 range_exp := Ceval.evalExp(Util.getOption(range),
1062 evalTargetFromSource(source, STATEMENT_CONTEXT, context));
1063 699 range_iter := RangeIterator.fromExp(range_exp);
1064
1065
1/2
✓ Branch 1 taken 699 times.
✗ Branch 2 not taken.
699 if RangeIterator.hasNext(range_iter) then
1066
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 699 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 699 times.
699 InstNode.ITERATOR_NODE(exp = Expression.MUTABLE(exp = iter_exp)) := iterator;
1067
1068 // Loop through each value in the iteration range.
1069
2/2
✓ Branch 1 taken 699 times.
✓ Branch 2 taken 7310 times.
8009 while RangeIterator.hasNext(range_iter) loop
1070 7310 (range_iter, value) := RangeIterator.next(range_iter);
1071 // Update the mutable expression with the iteration value and evaluate the statement.
1072 7310 Mutable.update(iter_exp, value);
1073 7310 ctrl := evaluateStatements(body, context);
1074
1075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7310 times.
7310 if ctrl <> FlowControl.NEXT then
1076 ✗ if ctrl == FlowControl.BREAK then
1077 ctrl := FlowControl.NEXT;
1078 end if;
1079
1080 break;
1081 end if;
1082
1083 7310 i := i + 1;
1084
1/2
✓ Branch 0 taken 7310 times.
✗ Branch 1 not taken.
7310 if i > limit then
1085 ✗ Error.addSourceMessage(Error.EVAL_LOOP_LIMIT_REACHED, {String(limit)},
1086 ElementSource.getInfo(source));
1087 ✗ fail();
1088 end if;
1089 end while;
1090 end if;
1091 end evaluateFor;
1092
1093 function evaluateIf
1094 input list<tuple<Expression, list<Statement>>> branches;
1095 input DAE.ElementSource source;
1096 input InstContext.Type context;
1097 output FlowControl ctrl;
1098 protected
1099 Expression cond;
1100 list<Statement> body;
1101 algorithm
1102
2/2
✓ Branch 0 taken 7229 times.
✓ Branch 1 taken 4277 times.
11506 for branch in branches loop
1103 7229 (cond, body) := branch;
1104
1105
2/2
✓ Branch 3 taken 1890 times.
✓ Branch 4 taken 5339 times.
7229 if Expression.isTrue(Ceval.evalExp(cond, evalTargetFromSource(source, IF_COND_CONTEXT, context))) then
1106 1890 ctrl := evaluateStatements(body, context);
1107 1890 return;
1108 end if;
1109 end for;
1110
1111 ctrl := FlowControl.NEXT;
1112 end evaluateIf;
1113
1114 function evaluateAssert
1115 input Expression condition;
1116 input Statement assertStmt;
1117 input DAE.ElementSource source;
1118 input InstContext.Type context;
1119 output FlowControl ctrl = FlowControl.NEXT;
1120 protected
1121 Expression msg, lvl;
1122 EvalTarget target = evalTargetFromSource(source, STATEMENT_CONTEXT, context);
1123 algorithm
1124
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1917 times.
1921 if Expression.isFalse(Ceval.evalExp(condition, target)) then
1125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 Statement.ASSERT(message = msg, level = lvl) := assertStmt;
1126 4 msg := Ceval.evalExp(msg, target);
1127 4 lvl := Ceval.evalExp(lvl, target);
1128
1129 () := match (msg, lvl)
1130 case (Expression.STRING(), Expression.ENUM_LITERAL(name = "warning"))
1131 algorithm
1132 1 Error.addSourceMessage(Error.ASSERT_TRIGGERED_WARNING, {msg.value}, EvalTarget.getInfo(target));
1133 then
1134 ();
1135
1136 case (Expression.STRING(), Expression.ENUM_LITERAL(name = "error"))
1137 algorithm
1138 3 Error.addSourceMessage(Error.ASSERT_TRIGGERED_ERROR, {msg.value}, EvalTarget.getInfo(target));
1139 ctrl := FlowControl.ASSERTION;
1140 then
1141 ();
1142
1143 else
1144 algorithm
1145 ✗ Error.terminate(getInstanceName() + " failed to evaluate assert(false, " +
1146 Expression.toString(msg) + ", " + Expression.toString(lvl) + ")", sourceInfo());
1147 ✗ then
1148 fail();
1149 end match;
1150 end if;
1151 end evaluateAssert;
1152
1153 function evaluateNoRetCall
1154 input Expression callExp;
1155 input DAE.ElementSource source;
1156 input InstContext.Type context;
1157 output FlowControl ctrl = FlowControl.NEXT;
1158 algorithm
1159 2 Ceval.evalExp(callExp, evalTargetFromSource(source, STATEMENT_CONTEXT, context));
1160 end evaluateNoRetCall;
1161
1162 function evaluateWhile
1163 input Expression condition;
1164 input list<Statement> body;
1165 input DAE.ElementSource source;
1166 input InstContext.Type context;
1167 output FlowControl ctrl = FlowControl.NEXT;
1168 protected
1169 Integer i = 0, limit = Flags.getConfigInt(Flags.EVAL_LOOP_LIMIT);
1170 EvalTarget target = evalTargetFromSource(source, STATEMENT_CONTEXT, context);
1171 algorithm
1172
2/2
✓ Branch 2 taken 136 times.
✓ Branch 3 taken 78 times.
214 while Expression.isTrue(Ceval.evalExp(condition, target)) loop
1173 136 ctrl := evaluateStatements(body, context);
1174
1175
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 134 times.
136 if ctrl <> FlowControl.NEXT then
1176
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if ctrl == FlowControl.BREAK then
1177 ctrl := FlowControl.NEXT;
1178 end if;
1179
1180 break;
1181 end if;
1182
1183 134 i := i + 1;
1184
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 if i > limit then
1185 ✗ Error.addSourceMessage(Error.EVAL_LOOP_LIMIT_REACHED, {String(limit)},
1186 ElementSource.getInfo(source));
1187 ✗ fail();
1188 end if;
1189 end while;
1190 end evaluateWhile;
1191
1192 function evalTargetFromSource
1193 input DAE.ElementSource source;
1194 input InstContext.Type context;
1195 input InstContext.Type currentContext;
1196 output EvalTarget target = EvalTarget.new(ElementSource.getInfo(source), InstContext.set(context, currentContext));
1197 end evalTargetFromSource;
1198
1199 function evaluateExternal2
1200 input String name;
1201 input Function fn;
1202 input list<Expression> args;
1203 input list<Expression> extArgs;
1204 output Expression result;
1205 protected
1206 ArgumentMap map;
1207 list<Expression> ext_args;
1208 algorithm
1209 7 map := createArgumentMap(fn.inputs, fn.outputs, fn.locals, args, mutableParams = true);
1210
4/4
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 91 times.
✓ Branch 3 taken 7 times.
98 ext_args := list(Expression.map(e, function applyReplacements2(map = map)) for e in extArgs);
1211 7 evaluateExternal3(name, ext_args);
1212 7 result := createResult(map, fn.outputs);
1213 end evaluateExternal2;
1214
1215 function evaluateExternal3
1216 input String name;
1217 input list<Expression> args;
1218 algorithm
1219 () := match name
1220 ✗ case "dgeev" algorithm EvalFunctionExt.Lapack_dgeev(args); then ();
1221 ✗ case "dgegv" algorithm EvalFunctionExt.Lapack_dgegv(args); then ();
1222 ✗ case "dgels" algorithm EvalFunctionExt.Lapack_dgels(args); then ();
1223 ✗ case "dgelsx" algorithm EvalFunctionExt.Lapack_dgelsx(args); then ();
1224 7 case "dgelsy" algorithm EvalFunctionExt.Lapack_dgelsy(args); then ();
1225 ✗ case "dgesv" algorithm EvalFunctionExt.Lapack_dgesv(args); then ();
1226 ✗ case "dgglse" algorithm EvalFunctionExt.Lapack_dgglse(args); then ();
1227 ✗ case "dgtsv" algorithm EvalFunctionExt.Lapack_dgtsv(args); then ();
1228 ✗ case "dgbsv" algorithm EvalFunctionExt.Lapack_dgtsv(args); then ();
1229 ✗ case "dgesvd" algorithm EvalFunctionExt.Lapack_dgesvd(args); then ();
1230 ✗ case "dgetrf" algorithm EvalFunctionExt.Lapack_dgetrf(args); then ();
1231 ✗ case "dgetrs" algorithm EvalFunctionExt.Lapack_dgetrs(args); then ();
1232 ✗ case "dgetri" algorithm EvalFunctionExt.Lapack_dgetri(args); then ();
1233 ✗ case "dgeqpf" algorithm EvalFunctionExt.Lapack_dgeqpf(args); then ();
1234 ✗ case "dorgqr" algorithm EvalFunctionExt.Lapack_dorgqr(args); then ();
1235 ✗ case "dhseqr" algorithm EvalFunctionExt.Lapack_dhseqr(args); then ();
1236 else fail();
1237 end match;
1238 end evaluateExternal3;
1239
1240 function callExternalFunction
1241 "Calls an external function using the FFI interface."
1242 input String extName;
1243 input Function fn;
1244 input list<Expression> args;
1245 input list<Expression> extArgs;
1246 input ComponentRef outputRef;
1247 input Option<SCode.Annotation> extAnnotation;
1248 input Boolean debug = false;
1249 output Expression result;
1250 protected
1251 SourceInfo info;
1252 String pkg_name;
1253 array<Expression> mapped_args;
1254 array<FFI.ArgSpec> specs;
1255 Type ret_ty;
1256 Expression res;
1257 list<Expression> output_vals;
1258 Integer fn_handle;
1259 algorithm
1260 267 info := InstNode.info(InstNode.fromHandle(fn.node));
1261 267 checkExtReturnValue(outputRef, info);
1262
1263 267 pkg_name := InstNode.name(InstNode.libraryScope(InstNode.fromHandle(fn.node)));
1264 267 fn_handle := loadLibraryFunction(pkg_name, extName, extAnnotation, debug, info);
1265
1266 try
1267 262 (mapped_args, specs) := mapExternalArgs(fn, args, extArgs);
1268
2/2
✓ Branch 1 taken 238 times.
✓ Branch 2 taken 21 times.
259 ret_ty := if ComponentRef.isCref(outputRef) then ComponentRef.nodeType(outputRef) else Type.NORETCALL();
1269 259 (res, output_vals) := FFI.callFunction(fn_handle, mapped_args, specs, ret_ty);
1270 256 freeLibraryFunction(fn_handle, debug);
1271 else
1272 6 freeLibraryFunction(fn_handle, debug);
1273 6 fail();
1274 end try;
1275
1276
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 234 times.
256 if listEmpty(output_vals) then
1277 // No output parameters, just return the return value.
1278 result := res;
1279 else
1280 // Some output parameters, might require constructing a tuple.
1281 22 result := makeExternalResult(res :: output_vals, outputRef, extArgs, fn.outputs);
1282 end if;
1283 end callExternalFunction;
1284
1285 function lookupLibraryInCache
1286 input String libName;
1287 output Integer libHandle;
1288 protected
1289 list<tuple<String, Integer>> cache;
1290 String name;
1291 algorithm
1292 2148 cache := getGlobalRoot(Global.sharedLibraryCacheIndex);
1293
1294
2/2
✓ Branch 0 taken 1918 times.
✓ Branch 1 taken 1904 times.
3822 for l in cache loop
1295 1918 (name, libHandle) := l;
1296
1297
4/4
✓ Branch 0 taken 478 times.
✓ Branch 1 taken 1440 times.
✓ Branch 3 taken 244 times.
✓ Branch 4 taken 234 times.
1918 if name == libName then
1298 244 return;
1299 end if;
1300 end for;
1301
1302 libHandle := -1;
1303 end lookupLibraryInCache;
1304
1305 function cacheLibrary
1306 input String libName;
1307 input Integer libHandle;
1308 protected
1309 list<tuple<String, Integer>> cache;
1310 algorithm
1311 34 cache := getGlobalRoot(Global.sharedLibraryCacheIndex);
1312 34 cache := (libName, libHandle) :: cache;
1313 34 setGlobalRoot(Global.sharedLibraryCacheIndex, cache);
1314 end cacheLibrary;
1315
1316 public
1317 function clearLibraryCache
1318 protected
1319 list<tuple<String, Integer>> cache;
1320 Integer lib_handle;
1321 algorithm
1322 6111 cache := getGlobalRoot(Global.sharedLibraryCacheIndex);
1323
1324
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 6111 times.
6136 for v in cache loop
1325 25 (_, lib_handle) := v;
1326 25 System.freeLibrary(lib_handle, false);
1327 end for;
1328
1329 6111 setGlobalRoot(Global.sharedLibraryCacheIndex, {});
1330 end clearLibraryCache;
1331
1332 protected
1333 function loadLibraryFunction
1334 "Tries to load the function with the given function that's either linked into
1335 the compiler itself or in a shared library provided by the user. Returns a
1336 handle to the function that should be freed with freeLibraryFunction when the
1337 function is no longer needed."
1338 input String libName;
1339 input String fnName;
1340 input Option<SCode.Annotation> extAnnotation;
1341 input Boolean debug;
1342 input SourceInfo info;
1343 output Integer fnHandle = -1;
1344 protected
1345 SCode.Annotation ann;
1346 list<String> libs = {}, dirs = {}, paths = {}, libs2 = {}, failures = {};
1347 Boolean found = false;
1348 String installLibDir;
1349 algorithm
1350 if Autoconf.os == "Windows_NT" then
1351 installLibDir := Settings.getInstallationDirectoryPath() + "/bin";
1352 else
1353 267 installLibDir := Settings.getInstallationDirectoryPath() + "/lib/" + Autoconf.triple + "/omc";
1354 end if;
1355
1356 // Read libraries and library directories from the annotation if it exists.
1357
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 267 times.
✓ Branch 2 taken 261 times.
✓ Branch 3 taken 6 times.
267 if isSome(extAnnotation) then
1358 261 SOME(ann) := extAnnotation;
1359 261 libs := parseExternalAnnotation("Library", ann);
1360 261 dirs := parseExternalAnnotation("LibraryDirectory", ann);
1361 end if;
1362
1363 // Append the default path and remove any duplicates.
1364 267 dirs := ("modelica://" + libName + "/Resources/Library") :: dirs;
1365 267 libs := List.unique(libs);
1366 267 dirs := List.unique(dirs);
1367
1368 // look for the lib names prefixed with 'lib' as well
1369
2/2
✓ Branch 0 taken 270 times.
✓ Branch 1 taken 267 times.
537 for lib in libs loop
1370
1/2
✓ Branch 0 taken 270 times.
✗ Branch 1 not taken.
270 if not stringEmpty(lib) then
1371 libs2 := lib :: libs2;
1372 270 libs2 := "lib" + lib :: libs2;
1373 end if;
1374 end for;
1375 libs := libs2;
1376
1377 // Add the special "ffi" directory inside the lib dir as the last search path.
1378 // This is where we put the ModelicaExternal libs right now. So that their shared
1379 // versions are not in the lib/omc dir complicating normal linking.
1380 // ( remembering we append to the front of the list as we process things )
1381 // A wasm build ships no such directory; its side modules serve that purpose.
1382 if not Autoconf.isWasm then
1383
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 267 times.
807 for lib in libs loop
1384 540 paths := (installLibDir + "/ffi/" + lib + Autoconf.dllExt) :: paths;
1385 end for;
1386 end if;
1387
1388 // Create paths for any combination of library and library directory.
1389
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 267 times.
807 for lib in libs loop
1390 // For functions that are linked into the compiler itself we pass an empty
1391 // string to loadLibrary.
1392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 540 times.
540 if stringEmpty(lib) then
1393 paths := "" :: paths;
1394 ✗ continue;
1395 end if;
1396
1397 540 lib := lib + Autoconf.dllExt;
1398
1399
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 540 times.
1080 for dir in dirs loop
1400 // Search for both dir/lib and e.g. dir/linux64/lib.
1401 540 paths := (dir + "/" + lib) :: paths;
1402 540 paths := (dir + "/" + System.modelicaPlatform() + "/" + lib) :: paths;
1403
1404 // Windows and macOS also install under the openModelicaPlatform name; a
1405 // wasm build searches wasm32-wasip1 and, for a module needing nothing of
1406 // the system, wasm32.
1407 if Autoconf.os == "Windows_NT" or Autoconf.os == "darwin" or Autoconf.isWasm then
1408 if not stringEmpty(System.openModelicaPlatformAlternative()) then
1409 paths := (dir + "/" + System.openModelicaPlatformAlternative() + "/" + lib) :: paths;
1410 end if;
1411 paths := (dir + "/" + System.openModelicaPlatform() + "/" + lib) :: paths;
1412 end if;
1413
1414 end for;
1415
1416 540 paths := installLibDir + "/" + lib :: paths;
1417 end for;
1418
1419 // If no Library annotation was given, append an empty string to search for
1420 // functions linked into the compiler itself. A wasm build always searches it:
1421 // the shared libraries it carries are what it has in place of those.
1422
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 261 times.
267 if listEmpty(libs) or Autoconf.isWasm then
1423 paths := "" :: paths;
1424 end if;
1425
1426 // The messages the search produces are about paths the user never asked for,
1427 // so keep them out of the way; what went wrong is collected separately and
1428 // reported below if nothing worked.
1429 267 ErrorExt.setCheckpoint(getInstanceName());
1430
1431 // First ask for every symbol in the library to be resolved, which is what
1432 // calling through it will need.
1433 267 (fnHandle, found, failures) := searchLibraryPaths(paths, fnName, lazy = false, debug = debug);
1434
1435
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 262 times.
267 if not found then
1436 // Nothing. Try again binding lazily: a library that has an unresolvable
1437 // symbol somewhere else in it can still provide this function.
1438 5 (fnHandle, found, failures) := searchLibraryPaths(paths, fnName, lazy = true, debug = debug);
1439 end if;
1440
1441 267 ErrorExt.rollBack(getInstanceName());
1442
1443
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 262 times.
267 if not found then
1444 10 Error.addSourceMessage(Error.EXTERNAL_FUNCTION_NOT_FOUND,
1445 {fnName, stringDelimitList(failures, "\n")}, info);
1446 5 fail();
1447 end if;
1448 end loadLibraryFunction;
1449
1450 function searchLibraryPaths
1451 "Tries each candidate path in turn, and says of each one why it did not
1452 provide the function: there is nothing there, it would not load, or it
1453 loaded and does not define it."
1454 input list<String> paths;
1455 input String fnName;
1456 input Boolean lazy;
1457 input Boolean debug;
1458 output Integer fnHandle = -1;
1459 output Boolean found = false;
1460 output list<String> failures = {};
1461 protected
1462 Integer lib_handle;
1463 String file, reason;
1464 Boolean resolved;
1465 algorithm
1466
2/2
✓ Branch 0 taken 2148 times.
✓ Branch 1 taken 10 times.
2158 for path in paths loop
1467 reason := "";
1468 resolved := true;
1469
1470 try
1471
2/2
✓ Branch 0 taken 2138 times.
✓ Branch 1 taken 10 times.
2148 file := if stringEmpty(path) then "" else uriToFilename(path);
1472 else
1473 ✗ file := path;
1474 resolved := false;
1475 reason := "not a usable file name";
1476 end try;
1477
1478
1/2
✓ Branch 0 taken 2148 times.
✗ Branch 1 not taken.
2148 if resolved then
1479 2148 lib_handle := lookupLibraryInCache(file);
1480
1481
2/2
✓ Branch 0 taken 1904 times.
✓ Branch 1 taken 244 times.
2148 if lib_handle == -1 then
1482 try
1483
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1897 times.
1904 lib_handle := if lazy then
1484 System.loadLibraryLazy(file, relativePath = false, printDebug = debug) else
1485 System.loadLibrary(file, relativePath = false, printDebug = debug);
1486 34 cacheLibrary(file, lib_handle);
1487 else
1488 1870 lib_handle := -1;
1489 1870 reason := System.getLoadLibraryError();
1490
1/2
✓ Branch 0 taken 1870 times.
✗ Branch 1 not taken.
1870 reason := if stringEmpty(reason) then "cannot be loaded" else
1491 "cannot be loaded: " + reason;
1492 end try;
1493 end if;
1494
1495
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 1870 times.
2148 if lib_handle <> -1 then
1496 try
1497 278 fnHandle := System.lookupFunction(lib_handle, fnName);
1498 found := true;
1499 else
1500 reason := "loaded, but does not define it";
1501 end try;
1502 end if;
1503 end if;
1504
1505
2/2
✓ Branch 0 taken 1886 times.
✓ Branch 1 taken 262 times.
2148 if found then
1506 break;
1507 end if;
1508
1509 // The empty path means the compiler's own image, which is not a path worth
1510 // listing back to the user.
1511
2/2
✓ Branch 0 taken 1878 times.
✓ Branch 1 taken 8 times.
1886 if not stringEmpty(file) then
1512 1878 failures := describeLibraryFailure(file, reason) :: failures;
1513 end if;
1514 end for;
1515
1516 272 failures := listReverse(failures);
1517 end searchLibraryPaths;
1518
1519 function describeLibraryFailure
1520 input String file;
1521 input String reason;
1522 output String str;
1523 algorithm
1524 1878 str := " " + Testsuite.friendly(file);
1525
1526
2/2
✓ Branch 1 taken 1870 times.
✓ Branch 2 taken 8 times.
1878 if not System.regularFileExists(file) then
1527 1870 str := str + " (no such file)";
1528 elseif not stringEmpty(reason) then
1529 8 str := str + " (" + Testsuite.friendly(reason) + ")";
1530 end if;
1531 end describeLibraryFailure;
1532
1533 function parseExternalAnnotation
1534 input String name;
1535 input SCode.Annotation ann;
1536 output list<String> strl = {};
1537 protected
1538 list<SCode.Mod> mods;
1539 Absyn.Exp exp;
1540 algorithm
1541 522 mods := SCodeUtil.lookupAnnotations(ann, name);
1542
1543
2/2
✓ Branch 0 taken 261 times.
✓ Branch 1 taken 522 times.
783 for m in mods loop
1544 strl := match m
1545 case SCode.Mod.MOD(binding = SOME(exp))
1546 261 then parseExternalAnnotationExp(exp, strl);
1547 else strl;
1548 end match;
1549 end for;
1550 end parseExternalAnnotation;
1551
1552 function parseExternalAnnotationExp
1553 input Absyn.Exp exp;
1554 input output list<String> strl;
1555 algorithm
1556 strl := match exp
1557 270 case Absyn.Exp.STRING() then exp.value :: strl;
1558 case Absyn.Exp.ARRAY()
1559 3 then List.fold(exp.arrayExp, parseExternalAnnotationExp, strl);
1560 else strl;
1561 end match;
1562 end parseExternalAnnotationExp;
1563
1564 function freeLibraryFunction
1565 "Frees the function loaded by loadLibraryFunction."
1566 input Integer fnHandle;
1567 input Boolean debug;
1568 algorithm
1569 262 System.freeFunction(fnHandle, debug);
1570 end freeLibraryFunction;
1571
1572 function mapExternalArgs
1573 "Maps the given input arguments to the arguments in the external function
1574 call specifier, returning an array of mapped arguments and a corresponding
1575 array of FFI.ArgSpec:s for use with the FFI interface."
1576 input Function fn;
1577 input list<Expression> inputArgs;
1578 input list<Expression> extArgs;
1579 output array<Expression> mappedArgs;
1580 output array<FFI.ArgSpec> argSpecs;
1581 protected
1582 ArgumentMap arg_map;
1583 Expression marg;
1584 FFI.ArgSpec arg_spec;
1585 Integer args_len, i = 1;
1586 list<Expression> input_args;
1587 algorithm
1588
4/4
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 262 times.
✓ Branch 2 taken 512 times.
✓ Branch 3 taken 262 times.
774 input_args := list(makeExternalArg(arg) for arg in inputArgs);
1589 262 arg_map := createArgumentMap(fn.inputs, fn.outputs, fn.locals, input_args,
1590 mutableParams = false, buildArrayBinding = false);
1591
1592 259 args_len := listLength(extArgs);
1593 259 mappedArgs := arrayCreateNoInit(args_len, Expression.INTEGER(0));
1594 259 argSpecs := arrayCreateNoInit(args_len, FFI.ArgSpec.INPUT);
1595
1596
2/2
✓ Branch 0 taken 523 times.
✓ Branch 1 taken 259 times.
782 for ext_arg in extArgs loop
1597 523 (marg, arg_spec) := mapExternalArg(ext_arg, arg_map, fn);
1598 523 mappedArgs[i] := marg;
1599 523 argSpecs[i] := arg_spec;
1600 523 i := i + 1;
1601 end for;
1602 end mapExternalArgs;
1603
1604 function makeExternalArg
1605 input Expression arg;
1606 output Expression extArg;
1607 algorithm
1608 extArg := match arg
1609 ✗ case Expression.FILENAME() then Expression.STRING(arg.filename);
1610 else arg;
1611 end match;
1612 end makeExternalArg;
1613
1614 function mapExternalArg
1615 input Expression extArg;
1616 input ArgumentMap argMap;
1617 input Function fn;
1618 output Expression arg;
1619 output FFI.ArgSpec spec;
1620 protected
1621 InstNode cr_node;
1622 algorithm
1623 523 arg := applyBindingReplacement(extArg, argMap);
1624 523 arg := Ceval.evalExp(arg);
1625
1626 spec := match extArg
1627 case Expression.CREF()
1628 algorithm
1629 519 cr_node := ComponentRef.node(ComponentRef.last(extArg.cref));
1630
1631
2/2
✓ Branch 1 taken 517 times.
✓ Branch 2 taken 2 times.
519 if InstNode.isProtected(cr_node) then
1632 spec := FFI.ArgSpec.LOCAL;
1633 elseif InstNode.isOutput(cr_node) then
1634 spec := FFI.ArgSpec.OUTPUT;
1635 else
1636 spec := FFI.ArgSpec.INPUT;
1637 end if;
1638 then
1639 spec;
1640
1641 else FFI.ArgSpec.INPUT;
1642 end match;
1643 end mapExternalArg;
1644
1645 function makeExternalResult
1646 "Constructs a tuple with the output values of an external function returning
1647 multiple values via output parameters. The first value in the list is assumed
1648 to be the value returned by the function, or Expression.EMPTY if the function
1649 doesn't return any value."
1650 input list<Expression> values;
1651 input ComponentRef outputRef;
1652 input list<Expression> extArgs;
1653 input list<NFInstNode.NodeHandle> outputs;
1654 output Expression outExp;
1655 protected
1656 ArgumentMap arg_map;
1657 Expression val;
1658 list<Expression> vals, ret_vals;
1659 ComponentRef cref;
1660 algorithm
1661 22 arg_map := UnorderedMap.new<Expression>(InstNode.hash, InstNode.refEqual);
1662
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 val :: vals := values;
1663
1664
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 21 times.
22 if ComponentRef.isCref(outputRef) then
1665 1 UnorderedMap.addUnique(ComponentRef.node(outputRef), val, arg_map);
1666 end if;
1667
1668
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 22 times.
121 for ext_arg in extArgs loop
1669 () := match ext_arg
1670 case Expression.CREF()
1671 guard InstNode.isOutput(ComponentRef.node(ComponentRef.last(ext_arg.cref)))
1672 algorithm
1673
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 val :: vals := vals;
1674 39 UnorderedMap.addUnique(ComponentRef.node(ext_arg.cref), val, arg_map);
1675 then
1676 ();
1677
1678 else ();
1679 end match;
1680 end for;
1681
1682
4/4
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 22 times.
61 ret_vals := list(getExternalOutputResult(InstNode.fromHandle(o), arg_map) for o in outputs);
1683 22 outExp := Expression.makeTuple(ret_vals);
1684 end makeExternalResult;
1685
1686 function getExternalOutputResult
1687 input InstNode outputNode;
1688 input ArgumentMap map;
1689 output Expression exp;
1690 protected
1691 Option<Expression> oexp;
1692 array<InstNode> comps;
1693 list<Expression> expl;
1694 InstNode cls_node;
1695 algorithm
1696 41 oexp := UnorderedMap.get(outputNode, map);
1697
1698
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 40 times.
41 if isSome(oexp) then
1699 40 SOME(exp) := oexp;
1700 elseif InstNode.isRecord(outputNode) then
1701 1 cls_node := InstNode.classScope(outputNode);
1702 1 comps := ClassTree.getComponents(Class.classTree(InstNode.getClass(cls_node)));
1703
1704 expl := {};
1705
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 for c in comps loop
1706 2 expl := getExternalOutputResult(c, map) :: expl;
1707 end for;
1708
1709 1 exp := Expression.makeRecord(InstNode.fullPath(cls_node),
1710 InstNode.getType(cls_node), listReverseInPlace(expl));
1711 else
1712 ✗ Error.terminate(getInstanceName() +
1713 " failed to find return value for output " + InstNode.name(outputNode), sourceInfo());
1714 end if;
1715 end getExternalOutputResult;
1716
1717 function checkExtReturnValue
1718 "Checks that an external function doesn't return something we don't yet
1719 support."
1720 input ComponentRef cref;
1721 input SourceInfo info;
1722 algorithm
1723
3/4
✓ Branch 1 taken 246 times.
✓ Branch 2 taken 21 times.
✓ Branch 5 taken 246 times.
✗ Branch 6 not taken.
267 if ComponentRef.isCref(cref) and Type.isRecord(ComponentRef.nodeType(cref)) then
1724 ✗ Error.addSourceMessage(Error.UNSUPPORTED_LANGUAGE_FEATURE,
1725 {"\"record return value in external function\"", "Pass the record as an output parameter"}, info);
1726 ✗ fail();
1727 end if;
1728 end checkExtReturnValue;
1729
1730 annotation(__OpenModelica_Interface="nf_frontend");
1731 end NFEvalFunction;
1732