From cb678e370e383de27f81fc16935a76f3f2d2f110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 8 Nov 2018 15:12:02 +0100 Subject: [PATCH] [poincare] Escape when recursion too deep --- poincare/include/poincare/expression.h | 9 +++ poincare/src/expression.cpp | 79 +++++++++++++++++++++++--- poincare/src/init.cpp | 4 ++ 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index de67877cc..c4c2f2575 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -90,7 +90,11 @@ class Expression : public TreeHandle { friend class SymbolNode; public: + static void TidyAfterException() { + UnlockRecursionCountReset(); + } static bool isExpression() { return true; } + /* Constructor & Destructor */ Expression() : TreeHandle() {} Expression clone() const; @@ -253,6 +257,11 @@ protected: private: /* Simplification */ + static constexpr int sRecursionLimit = 2000; //TODO value? + static bool ResetRecursionCountAndLockReset(); + static void UnlockRecursionCountReset(); + static void IncrementRecursionCount(); + static bool RecursionMaximalDepthExceeded(); Expression deepReduce(Context & context, Preferences::AngleUnit angleUnit, bool replaceSymbols = true); void deepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, bool replaceSymbols) { return node()->deepReduceChildren(context, angleUnit, replaceSymbols); diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index b3eab228b..51798cd9e 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -13,6 +13,9 @@ namespace Poincare { +static int sRecursionCount = 0; +static bool sRecursionCountReinitializationIsLocked = false; + /* Constructor & Destructor */ Expression Expression::clone() const { TreeHandle c = TreeHandle::clone(); return static_cast(c); } @@ -75,15 +78,32 @@ bool Expression::isRationalOne() const { } bool Expression::recursivelyMatches(ExpressionTest test, Context & context) const { - if (test(*this, context)) { - return true; - } - for (int i = 0; i < this->numberOfChildren(); i++) { - if (childAtIndex(i).recursivelyMatches(test, context)) { - return true; + // Reset recursion count if needed + bool willHaveToUnlock = ResetRecursionCountAndLockReset(); + + bool result = false; + + IncrementRecursionCount(); + if (RecursionMaximalDepthExceeded()) { + //TODO propagate recursion error? + } else { + if (test(*this, context)) { + result = true; + } else { + for (int i = 0; i < this->numberOfChildren(); i++) { + if (childAtIndex(i).recursivelyMatches(test, context)) { + result = true; + break; + } + } } } - return false; + + // Unlock recursion count reset if needed + if (willHaveToUnlock) { + UnlockRecursionCountReset(); + } + return result; } bool Expression::isApproximate(Context & context) const { @@ -223,6 +243,11 @@ void Expression::defaultSetChildrenInPlace(Expression other) { template Evaluation Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const { + assert(sRecursionCountReinitializationIsLocked == true); + IncrementRecursionCount(); + if (RecursionMaximalDepthExceeded()) { + return Complex::Undefined(); // TODO Propagate error "Recursion too deep" + } return node()->approximate(U(), context, angleUnit); } @@ -309,10 +334,47 @@ Expression Expression::simplify(Context & context, Preferences::AngleUnit angleU } Expression Expression::reduce(Context & context, Preferences::AngleUnit angleUnit, bool replaceSymbols) { - return deepReduce(context, angleUnit, replaceSymbols); + // Reset recursion count if needed + bool willHaveToUnlock = ResetRecursionCountAndLockReset(); + + Expression result = deepReduce(context, angleUnit, replaceSymbols); + + // Unlock recursion count reinitialization if needed + if (willHaveToUnlock) { + UnlockRecursionCountReset(); + } + + return result; +} + +bool Expression::ResetRecursionCountAndLockReset() { + if (!sRecursionCountReinitializationIsLocked) { + sRecursionCount = 0; + sRecursionCountReinitializationIsLocked = true; + return true; + } + return false; +} + +void Expression::UnlockRecursionCountReset() { + sRecursionCountReinitializationIsLocked = false; +} + +void Expression::IncrementRecursionCount() { + sRecursionCount++; +} + +bool Expression::RecursionMaximalDepthExceeded() { + return sRecursionCount >= Expression::sRecursionLimit; } Expression Expression::deepReduce(Context & context, Preferences::AngleUnit angleUnit, bool replaceSymbols) { + IncrementRecursionCount(); + if (RecursionMaximalDepthExceeded()) { + sSimplificationHasBeenInterrupted = true; + return *this; + } + #if MATRIX_EXACT_REDUCING #else if (IsMatrix(*this, context)) { @@ -320,6 +382,7 @@ Expression Expression::deepReduce(Context & context, Preferences::AngleUnit angl return *this; } #endif + deepReduceChildren(context, angleUnit, replaceSymbols); return shallowReduce(context, angleUnit, replaceSymbols); } diff --git a/poincare/src/init.cpp b/poincare/src/init.cpp index 338bb197b..bcaa62c1b 100644 --- a/poincare/src/init.cpp +++ b/poincare/src/init.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -13,6 +14,9 @@ void Init() { void Tidy() { // Clean Integer Integer::TidyIntegerBuffer(); + + // Unlock recursion count + Expression::TidyAfterException(); } }