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 |