Skip to content

Commit d33f6a4

Browse files
authored
Improve evaluation of dynamic annotations (#10435)
- Evaluate the expression that the variable evaluator returns when evaluating crefs. - Guard against infinite recursion when evaluating expressions. - Change the variable evaluator in DynamicAnnotation::evaluate to throw an exception when a variable can't be looked up instead of substituting it with the whole annotation expression.
1 parent dfb0987 commit d33f6a4

File tree

3 files changed

+62
-57
lines changed

3 files changed

+62
-57
lines changed

OMEdit/OMEditLIB/Annotations/DynamicAnnotation.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,9 @@ void DynamicAnnotation::evaluate(ModelInstance::Model *pModel)
138138
vname = StringHandler::getLastWordAfterDot(vname);
139139
FlatModelica::Expression exp = pModel->getVariableBinding(vname);
140140
if (exp.isNull()) {
141-
return mExp;
142-
} else {
143-
return exp;
141+
throw std::runtime_error(name + " could not be found");
144142
}
143+
return exp;
145144
}));
146145
} catch (const std::exception &e) {
147146
qDebug() << "Failed to evaluate expression.";

OMEdit/OMEditLIB/FlatModelica/Expression.cpp

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ namespace FlatModelica
491491
virtual bool isLiteral() const = 0;
492492

493493
virtual std::unique_ptr<ExpressionBase> clone() const = 0;
494-
virtual Expression eval(const Expression::VariableEvaluator &var_eval) const = 0;
494+
virtual Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level = 0) const = 0;
495495

496496
virtual void print(std::ostream &os) const = 0;
497497
};
@@ -503,7 +503,7 @@ namespace FlatModelica
503503
: _value(value) {}
504504

505505
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Integer>(*this); }
506-
Expression eval(const Expression::VariableEvaluator&) const override { return Expression(_value); }
506+
Expression eval(const Expression::VariableEvaluator&, int) const override { return Expression(_value); }
507507

508508
bool isInteger() const override { return true; }
509509
bool isBooleanish() const override { return true; }
@@ -526,7 +526,7 @@ namespace FlatModelica
526526
: _value(value) {}
527527

528528
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Real>(*this); }
529-
Expression eval(const Expression::VariableEvaluator&) const override { return Expression(_value); }
529+
Expression eval(const Expression::VariableEvaluator&, int) const override { return Expression(_value); }
530530

531531
bool isReal() const override { return true; }
532532
bool isBooleanish() const override { return true; }
@@ -549,7 +549,7 @@ namespace FlatModelica
549549
: _value(value) {}
550550

551551
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Boolean>(*this); }
552-
Expression eval(const Expression::VariableEvaluator&) const override { return Expression(_value); }
552+
Expression eval(const Expression::VariableEvaluator&, int) const override { return Expression(_value); }
553553

554554
bool isBoolean() const override { return true; }
555555
bool isBooleanish() const override { return true; }
@@ -572,7 +572,7 @@ namespace FlatModelica
572572
String(const QJsonValue &value);
573573

574574
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<String>(*this); }
575-
Expression eval(const Expression::VariableEvaluator&) const override { return Expression(_value); }
575+
Expression eval(const Expression::VariableEvaluator&, int) const override { return Expression(_value); }
576576

577577
bool isString() const override { return true; }
578578
bool isLiteral() const override { return true; }
@@ -596,7 +596,7 @@ namespace FlatModelica
596596
Enum(const QJsonObject &value);
597597

598598
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Enum>(*this); }
599-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
599+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
600600

601601
bool isEnum() const override { return true; }
602602
bool isLiteral() const override { return true; }
@@ -619,7 +619,7 @@ namespace FlatModelica
619619
Cref(const QJsonObject &value);
620620

621621
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Cref>(*this); }
622-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
622+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
623623

624624
bool isLiteral() const override { return false; }
625625
void print(std::ostream &os) const override;
@@ -640,7 +640,7 @@ namespace FlatModelica
640640
Array(const QJsonArray &value);
641641

642642
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Array>(*this); }
643-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
643+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
644644

645645
bool isArray() const override { return true; }
646646
bool isLiteral() const override;
@@ -664,7 +664,7 @@ namespace FlatModelica
664664
Range(const QJsonObject &value);
665665

666666
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Range>(*this); }
667-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
667+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
668668

669669
bool isLiteral() const override;
670670

@@ -686,7 +686,7 @@ namespace FlatModelica
686686
Call(const QJsonObject &value, bool isRecord);
687687

688688
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Call>(*this); }
689-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
689+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
690690

691691
bool isCall() const override { return true; }
692692
bool isLiteral() const override { return false; }
@@ -733,7 +733,7 @@ namespace FlatModelica
733733
IteratorCall(const QJsonObject &value);
734734

735735
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<IteratorCall>(*this); }
736-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
736+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
737737

738738
bool isLiteral() const override { return false; }
739739

@@ -755,7 +755,7 @@ namespace FlatModelica
755755
Binary(const QJsonObject &value);
756756

757757
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Binary>(*this); }
758-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
758+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
759759

760760
bool isLiteral() const override { return false; }
761761
void print(std::ostream &os) const override;
@@ -778,7 +778,7 @@ namespace FlatModelica
778778
Unary(const QJsonObject &value);
779779

780780
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<Unary>(*this); }
781-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
781+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
782782

783783
bool isLiteral() const override { return false; }
784784
void print(std::ostream &os) const override;
@@ -801,7 +801,7 @@ namespace FlatModelica
801801
IfExp(const QJsonObject &value);
802802

803803
std::unique_ptr<ExpressionBase> clone() const override { return std::make_unique<IfExp>(*this); }
804-
Expression eval(const Expression::VariableEvaluator &var_eval) const override;
804+
Expression eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const override;
805805

806806
bool isLiteral() const override { return false; }
807807
void print(std::ostream &os) const override;
@@ -1336,9 +1336,10 @@ namespace FlatModelica
13361336
_index = value["index"].toInt();
13371337
}
13381338

1339-
Expression Enum::eval(const Expression::VariableEvaluator &var_eval) const
1339+
Expression Enum::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
13401340
{
13411341
Q_UNUSED(var_eval);
1342+
Q_UNUSED(recursion_level);
13421343
return Expression(_name, _index);
13431344
}
13441345

@@ -1404,9 +1405,13 @@ namespace FlatModelica
14041405
}
14051406
}
14061407

1407-
Expression Cref::eval(const Expression::VariableEvaluator &var_eval) const
1408+
Expression Cref::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
14081409
{
1409-
return Expression(var_eval(_name));
1410+
if (recursion_level > 100) {
1411+
throw std::runtime_error("Recursion limit reached");
1412+
}
1413+
1414+
return var_eval(_name).evaluate(var_eval, recursion_level + 1);
14101415
}
14111416

14121417
void Cref::print(std::ostream &os) const
@@ -1467,13 +1472,13 @@ namespace FlatModelica
14671472
}
14681473
}
14691474

1470-
Expression Array::eval(const Expression::VariableEvaluator &var_eval) const
1475+
Expression Array::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
14711476
{
14721477
std::vector<Expression> elems;
14731478
elems.reserve(_elements.size());
14741479

14751480
for (auto &e: _elements) {
1476-
elems.emplace_back(e.evaluate(var_eval));
1481+
elems.emplace_back(e.evaluate(var_eval, recursion_level));
14771482
}
14781483

14791484
return Expression(std::move(elems));
@@ -1550,11 +1555,11 @@ namespace FlatModelica
15501555
}
15511556
}
15521557

1553-
Expression Range::eval(const Expression::VariableEvaluator &var_eval) const
1558+
Expression Range::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
15541559
{
1555-
auto start = _start.evaluate(var_eval);
1556-
auto stop = _stop.evaluate(var_eval);
1557-
auto step = _step.isNull() ? Expression() : _step.evaluate(var_eval);
1560+
auto start = _start.evaluate(var_eval, recursion_level);
1561+
auto stop = _stop.evaluate(var_eval, recursion_level);
1562+
auto step = _step.isNull() ? Expression() : _step.evaluate(var_eval, recursion_level);
15581563
return Expression(std::make_unique<Range>(std::move(start), std::move(step), std::move(stop)));
15591564
}
15601565

@@ -1610,13 +1615,13 @@ namespace FlatModelica
16101615
}
16111616
}
16121617

1613-
Expression Call::eval(const Expression::VariableEvaluator &var_eval) const
1618+
Expression Call::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
16141619
{
16151620
std::vector<Expression> args;
16161621
args.reserve(_args.size());
16171622

16181623
for (auto &a: _args) {
1619-
args.emplace_back(a.evaluate(var_eval));
1624+
args.emplace_back(a.evaluate(var_eval, recursion_level));
16201625
}
16211626

16221627
switch (djb2_hash(_name.c_str())) {
@@ -1797,9 +1802,10 @@ namespace FlatModelica
17971802
}
17981803
}
17991804

1800-
Expression IteratorCall::eval(const Expression::VariableEvaluator &var_eval) const
1805+
Expression IteratorCall::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
18011806
{
18021807
Q_UNUSED(var_eval);
1808+
Q_UNUSED(recursion_level);
18031809
return Expression(std::make_unique<IteratorCall>(*this));
18041810
}
18051811

@@ -1846,32 +1852,32 @@ namespace FlatModelica
18461852
_e2.deserialize(value["rhs"]);
18471853
}
18481854

1849-
Expression Binary::eval(const Expression::VariableEvaluator &var_eval) const
1855+
Expression Binary::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
18501856
{
18511857
switch (_op.type()) {
1852-
case Operator::Add: return _e1.evaluate(var_eval) + _e2.evaluate(var_eval);
1853-
case Operator::Sub: return _e1.evaluate(var_eval) - _e2.evaluate(var_eval);
1854-
case Operator::Mul: return _e1.evaluate(var_eval) * _e2.evaluate(var_eval);
1855-
case Operator::Div: return _e1.evaluate(var_eval) / _e2.evaluate(var_eval);
1856-
case Operator::Pow: return _e1.evaluate(var_eval) ^ _e2.evaluate(var_eval);
1857-
case Operator::AddEW: return Expression::addEw(_e1.evaluate(var_eval), _e2.evaluate(var_eval));
1858-
case Operator::SubEW: return Expression::subEw(_e1.evaluate(var_eval), _e2.evaluate(var_eval));
1859-
case Operator::MulEW: return Expression::mulEw(_e1.evaluate(var_eval), _e2.evaluate(var_eval));
1860-
case Operator::DivEW: return Expression::divEw(_e1.evaluate(var_eval), _e2.evaluate(var_eval));
1861-
case Operator::PowEW: return Expression::powEw(_e1.evaluate(var_eval), _e2.evaluate(var_eval));
1858+
case Operator::Add: return _e1.evaluate(var_eval, recursion_level) + _e2.evaluate(var_eval, recursion_level);
1859+
case Operator::Sub: return _e1.evaluate(var_eval, recursion_level) - _e2.evaluate(var_eval, recursion_level);
1860+
case Operator::Mul: return _e1.evaluate(var_eval, recursion_level) * _e2.evaluate(var_eval, recursion_level);
1861+
case Operator::Div: return _e1.evaluate(var_eval, recursion_level) / _e2.evaluate(var_eval, recursion_level);
1862+
case Operator::Pow: return _e1.evaluate(var_eval, recursion_level) ^ _e2.evaluate(var_eval, recursion_level);
1863+
case Operator::AddEW: return Expression::addEw(_e1.evaluate(var_eval, recursion_level), _e2.evaluate(var_eval, recursion_level));
1864+
case Operator::SubEW: return Expression::subEw(_e1.evaluate(var_eval, recursion_level), _e2.evaluate(var_eval, recursion_level));
1865+
case Operator::MulEW: return Expression::mulEw(_e1.evaluate(var_eval, recursion_level), _e2.evaluate(var_eval, recursion_level));
1866+
case Operator::DivEW: return Expression::divEw(_e1.evaluate(var_eval, recursion_level), _e2.evaluate(var_eval, recursion_level));
1867+
case Operator::PowEW: return Expression::powEw(_e1.evaluate(var_eval, recursion_level), _e2.evaluate(var_eval, recursion_level));
18621868
// Special handling of 'and' and 'or' to avoid evaluating both sides unless it's necessary.
18631869
case Operator::And: return expBinaryEWOp(_e1, _e2, [&] (auto &e1, auto &e2) {
1864-
return e1.evaluate(var_eval) && e2.evaluate(var_eval);
1870+
return e1.evaluate(var_eval, recursion_level) && e2.evaluate(var_eval, recursion_level);
18651871
}, "and");
18661872
case Operator::Or: return expBinaryEWOp(_e1, _e2, [&] (auto &e1, auto &e2) {
1867-
return e1.evaluate(var_eval) || e2.evaluate(var_eval);
1873+
return e1.evaluate(var_eval, recursion_level) || e2.evaluate(var_eval, recursion_level);
18681874
}, "or");
1869-
case Operator::Equal: return Expression(_e1.evaluate(var_eval) == _e2.evaluate(var_eval));
1870-
case Operator::NotEqual: return Expression(_e1.evaluate(var_eval) != _e2.evaluate(var_eval));
1871-
case Operator::Less: return Expression(_e1.evaluate(var_eval) < _e2.evaluate(var_eval));
1872-
case Operator::LessEq: return Expression(_e1.evaluate(var_eval) <= _e2.evaluate(var_eval));
1873-
case Operator::Greater: return Expression(_e1.evaluate(var_eval) > _e2.evaluate(var_eval));
1874-
case Operator::GreaterEq: return Expression(_e1.evaluate(var_eval) >= _e2.evaluate(var_eval));
1875+
case Operator::Equal: return Expression(_e1.evaluate(var_eval, recursion_level) == _e2.evaluate(var_eval, recursion_level));
1876+
case Operator::NotEqual: return Expression(_e1.evaluate(var_eval, recursion_level) != _e2.evaluate(var_eval, recursion_level));
1877+
case Operator::Less: return Expression(_e1.evaluate(var_eval, recursion_level) < _e2.evaluate(var_eval, recursion_level));
1878+
case Operator::LessEq: return Expression(_e1.evaluate(var_eval, recursion_level) <= _e2.evaluate(var_eval, recursion_level));
1879+
case Operator::Greater: return Expression(_e1.evaluate(var_eval, recursion_level) > _e2.evaluate(var_eval, recursion_level));
1880+
case Operator::GreaterEq: return Expression(_e1.evaluate(var_eval, recursion_level) >= _e2.evaluate(var_eval, recursion_level));
18751881
default: break;
18761882
}
18771883

@@ -1917,11 +1923,11 @@ namespace FlatModelica
19171923
_op.deserialize(value["op"]);
19181924
}
19191925

1920-
Expression Unary::eval(const Expression::VariableEvaluator &var_eval) const
1926+
Expression Unary::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
19211927
{
19221928
switch (_op.type()) {
1923-
case Operator::Sub: return -_e.evaluate(var_eval);
1924-
case Operator::Not: return !_e.evaluate(var_eval);
1929+
case Operator::Sub: return -_e.evaluate(var_eval, recursion_level);
1930+
case Operator::Not: return !_e.evaluate(var_eval, recursion_level);
19251931
default: break;
19261932
}
19271933

@@ -1981,10 +1987,10 @@ namespace FlatModelica
19811987
_false_e.deserialize(value["false"]);
19821988
}
19831989

1984-
Expression IfExp::eval(const Expression::VariableEvaluator &var_eval) const
1990+
Expression IfExp::eval(const Expression::VariableEvaluator &var_eval, int recursion_level) const
19851991
{
1986-
return _condition.evaluate(var_eval).boolValue() ?
1987-
_true_e.evaluate(var_eval) : _false_e.evaluate(var_eval);
1992+
return _condition.evaluate(var_eval, recursion_level).boolValue() ?
1993+
_true_e.evaluate(var_eval, recursion_level) : _false_e.evaluate(var_eval, recursion_level);
19881994
}
19891995

19901996
void IfExp::print(std::ostream &os) const
@@ -2227,9 +2233,9 @@ namespace FlatModelica
22272233
* name as a string and returns the variables value as a double.
22282234
* \param var_eval
22292235
*/
2230-
Expression Expression::evaluate(const VariableEvaluator &var_eval) const
2236+
Expression Expression::evaluate(const VariableEvaluator &var_eval, int recursion_level) const
22312237
{
2232-
return Expression(_value->eval(var_eval));
2238+
return Expression(_value->eval(var_eval, recursion_level));
22332239
}
22342240

22352241
/*!

OMEdit/OMEditLIB/FlatModelica/Expression.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ namespace FlatModelica
9999
void deserialize(const QJsonValue &value);
100100
QJsonValue serialize() const;
101101

102-
Expression evaluate(const VariableEvaluator &var_eval) const;
102+
Expression evaluate(const VariableEvaluator &var_eval, int recursion_level = 0) const;
103103

104104
bool isNull() const;
105105
bool isLiteral() const;

0 commit comments

Comments
 (0)