diff --git a/CheckMemoryLeak.cpp b/CheckMemoryLeak.cpp new file mode 100644 index 000000000..136099e2b --- /dev/null +++ b/CheckMemoryLeak.cpp @@ -0,0 +1,278 @@ + +#include "CheckMemoryLeak.h" + +#include "Statements.h" + +#include "CommonCheck.h" + +#include +#include + +#include // <- memset + +//--------------------------------------------------------------------------- + +struct _variable +{ + int indentlevel; + unsigned int varindex; + enum {Any, Data, Malloc, New, NewA} value; +}; + +//--------------------------------------------------------------------------- + + +void CheckMemoryLeak() +{ + std::vector<_variable *> varlist; + + // if + int iflevel = 0; + bool endif = false; + std::vector iflist; + + // loop + int looplevel = 0; + bool endloop = false; + std::vector looplist; + + // switch + int switchlevel = 0; + bool endswitch = false; + std::vector switchlist; + + // Parse the statement list and locate memory leaks.. + int indentlevel = 0; + std::list::const_iterator it; + for (it = Statements.begin(); it != Statements.end(); it++) + { + switch (it->Type) + { + case STATEMENT::OBRACE: + indentlevel++; + iflist.push_back(endif); + if (endif) + iflevel++; + looplist.push_back(endloop); + if (endloop) + looplevel++; + switchlist.push_back(endswitch); + if (endswitch) + switchlevel++; + break; + + case STATEMENT::EBRACE: + // Check if any variables go out of scope.. + if (indentlevel >= 1) + { + for (unsigned int i = 0; i < varlist.size(); i++) + { + if (varlist[i]->indentlevel != indentlevel) + continue; + + if (varlist[i]->value == _variable::Malloc || varlist[i]->value == _variable::New || varlist[i]->value == _variable::NewA) + { + std::ostringstream ostr; + ostr << FileLine(it->Token) << ": Memory leak:" << VariableNames[varlist[i]->varindex]; + ReportErr(ostr.str()); + } + + // Delete this instance.. + delete varlist[i]; + varlist.erase(varlist.begin()+i); + } + } + + // if level.. + if (iflist.back()) + iflevel--; + iflist.pop_back(); + + // loop level.. + if (looplist.back()) + looplevel--; + looplist.pop_back(); + + // switch level.. + if (switchlist.back()) + switchlevel--; + switchlist.pop_back(); + + // Make sure the varlist is empty.. + if (indentlevel <= 1) + varlist.clear(); + + if (indentlevel > 0) + indentlevel--; + break; + + case STATEMENT::DECL: + if (indentlevel > 0) + { + _variable *NewVar = new _variable; + memset(NewVar, 0, sizeof(_variable)); + NewVar->indentlevel = indentlevel; + NewVar->varindex = it->VarIndex; + varlist.push_back(NewVar); + } + break; + + case STATEMENT::IF: + case STATEMENT::ELSEIF: + case STATEMENT::ELSE: + // Conditional statements.. + iflevel++; + break; + + case STATEMENT::ENDIF: + iflevel--; + break; + + // Not very interested in these.. + case STATEMENT::LOOP: + case STATEMENT::ENDLOOP: + case STATEMENT::SWITCH: + case STATEMENT::ENDSWITCH: + break; + + case STATEMENT::MALLOC: + case STATEMENT::NEW: + case STATEMENT::NEWARRAY: + case STATEMENT::FREE: + case STATEMENT::DELETE: + case STATEMENT::DELETEARRAY: + // Don't handle conditional allocation at the moment.. + if (iflevel>0 && (it->Type==STATEMENT::MALLOC || it->Type==STATEMENT::NEW || it->Type==STATEMENT::NEWARRAY)) + break; + + for (unsigned int i = 0; i < varlist.size(); i++) + { + if ( varlist[i]->varindex == it->VarIndex ) + { + bool isalloc = (varlist[i]->value==_variable::Malloc || varlist[i]->value==_variable::New || varlist[i]->value==_variable::NewA); + bool alloc = (it->Type==STATEMENT::MALLOC || it->Type==STATEMENT::NEW || it->Type==STATEMENT::NEWARRAY); + + // Already allocated and then allocated again.. + //bool err = (isalloc & alloc); + + if (isalloc && !alloc) + { + // Check that the deallocation matches.. + bool err = false; + + err |= (varlist[i]->value==_variable::Malloc && it->Type!=STATEMENT::FREE); + err |= (varlist[i]->value==_variable::New && it->Type!=STATEMENT::DELETE); + err |= (varlist[i]->value==_variable::NewA && it->Type!=STATEMENT::DELETEARRAY); + + if (err) + { + std::ostringstream ostr; + ostr << FileLine(it->Token) << ": Mismatching allocation and deallocation '" << VariableNames[varlist[i]->varindex] << "'"; + ReportErr(ostr.str()); + } + } + + if ( it->Type == STATEMENT::MALLOC ) + varlist[i]->value = _variable::Malloc; + + else if ( it->Type == STATEMENT::FREE ) + varlist[i]->value = _variable::Any; + + else if ( it->Type == STATEMENT::NEW ) + varlist[i]->value = _variable::New; + + else if ( it->Type == STATEMENT::NEWARRAY ) + varlist[i]->value = _variable::NewA; + + else if ( it->Type == STATEMENT::DELETE ) + varlist[i]->value = _variable::Any; + + else if ( it->Type == STATEMENT::DELETEARRAY ) + varlist[i]->value = _variable::Any; + + break; + } + } + break; + + case STATEMENT::ASSIGN: + case STATEMENT::USE: + for (unsigned int i = 0; i < varlist.size(); i++) + { + if ( varlist[i]->varindex == it->VarIndex ) + { + varlist[i]->value = _variable::Data; + break; + } + } + break; + + case STATEMENT::RETURN: + for (unsigned int i = 0; i < varlist.size(); i++) + { + if ( varlist[i]->varindex == it->VarIndex ) + { + varlist[i]->value = _variable::Any; + } + + else if (varlist[i]->value==_variable::New || + varlist[i]->value==_variable::NewA ) + { + std::ostringstream ostr; + ostr << FileLine(it->Token) << ": Memory leak:" << VariableNames[varlist[i]->varindex]; + ReportErr(ostr.str()); + varlist[i]->value = _variable::Any; + break; + } + } + break; + + case STATEMENT::CONTINUE: + case STATEMENT::BREAK: + // Memory leak if variable is allocated.. + if (looplevel>0) + { + // Find the last loop.. + for (int i = looplist.size() - 1; i >= 0; i--) + { + if (switchlist[i]) + break; + + if (!looplist[i]) + continue; + + int loop_indentlevel = i + 1; + + for (i = 0; i < (int)varlist.size(); i++) + { + if (varlist[i]->indentlevel < loop_indentlevel) + continue; + + if (varlist[i]->value==_variable::Malloc || + varlist[i]->value==_variable::New || + varlist[i]->value==_variable::NewA ) + { + std::ostringstream ostr; + ostr << FileLine(it->Token) << ": Memory leak:" << VariableNames[varlist[i]->varindex]; + ReportErr(ostr.str()); + } + + break; + } + + break; + } + } + break; + } + + endif = (it->Type == STATEMENT::ENDIF); + endloop = (it->Type == STATEMENT::ENDLOOP); + endswitch = (it->Type == STATEMENT::ENDSWITCH); + } +} +//--------------------------------------------------------------------------- + + + + diff --git a/CheckMemoryLeak.h b/CheckMemoryLeak.h new file mode 100644 index 000000000..088ead068 --- /dev/null +++ b/CheckMemoryLeak.h @@ -0,0 +1,11 @@ +//--------------------------------------------------------------------------- +#ifndef CheckMemoryLeakH +#define CheckMemoryLeakH +//--------------------------------------------------------------------------- + +/** \brief Check for memory leaks */ + +void CheckMemoryLeak(); + +//--------------------------------------------------------------------------- +#endif