2019-10-30 17:57:46 +01:00
|
|
|
|
|
|
|
#include "programmemory.h"
|
|
|
|
#include "astutils.h"
|
2021-01-21 19:49:37 +01:00
|
|
|
#include "mathlib.h"
|
2019-10-30 17:57:46 +01:00
|
|
|
#include "symboldatabase.h"
|
2021-01-21 19:49:37 +01:00
|
|
|
#include "token.h"
|
2021-03-30 14:02:28 +02:00
|
|
|
#include "valueflow.h"
|
2020-05-19 08:53:38 +02:00
|
|
|
#include <algorithm>
|
2019-10-30 17:57:46 +01:00
|
|
|
#include <cassert>
|
2021-07-24 22:44:18 +02:00
|
|
|
#include <functional>
|
2020-12-19 21:54:19 +01:00
|
|
|
#include <limits>
|
2020-09-11 23:03:57 +02:00
|
|
|
#include <memory>
|
2019-10-30 17:57:46 +01:00
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
void ProgramMemory::setValue(nonneg int exprid, const ValueFlow::Value& value)
|
2021-01-22 21:47:24 +01:00
|
|
|
{
|
|
|
|
values[exprid] = value;
|
|
|
|
}
|
2021-01-28 12:37:56 +01:00
|
|
|
const ValueFlow::Value* ProgramMemory::getValue(nonneg int exprid, bool impossible) const
|
2019-10-30 17:58:19 +01:00
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
const ProgramMemory::Map::const_iterator it = values.find(exprid);
|
2021-01-28 12:37:56 +01:00
|
|
|
const bool found = it != values.end() && (impossible || !it->second.isImpossible());
|
2019-10-30 17:57:46 +01:00
|
|
|
if (found)
|
2020-09-11 23:03:57 +02:00
|
|
|
return &it->second;
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
bool ProgramMemory::getIntValue(nonneg int exprid, MathLib::bigint* result) const
|
2020-09-11 23:03:57 +02:00
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
const ValueFlow::Value* value = getValue(exprid);
|
2020-09-11 23:03:57 +02:00
|
|
|
if (value && value->isIntValue()) {
|
|
|
|
*result = value->intvalue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
void ProgramMemory::setIntValue(nonneg int exprid, MathLib::bigint value)
|
2019-10-30 17:58:19 +01:00
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
values[exprid] = ValueFlow::Value(value);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
bool ProgramMemory::getTokValue(nonneg int exprid, const Token** result) const
|
2019-10-30 17:58:19 +01:00
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
const ValueFlow::Value* value = getValue(exprid);
|
2020-09-11 23:03:57 +02:00
|
|
|
if (value && value->isTokValue()) {
|
|
|
|
*result = value->tokvalue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
bool ProgramMemory::getContainerSizeValue(nonneg int exprid, MathLib::bigint* result) const
|
2020-08-11 03:08:49 +02:00
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
const ValueFlow::Value* value = getValue(exprid);
|
2020-09-11 23:03:57 +02:00
|
|
|
if (value && value->isContainerSizeValue()) {
|
|
|
|
*result = value->intvalue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2020-08-11 03:08:49 +02:00
|
|
|
}
|
2021-01-28 12:37:56 +01:00
|
|
|
bool ProgramMemory::getContainerEmptyValue(nonneg int exprid, MathLib::bigint* result) const
|
|
|
|
{
|
|
|
|
const ValueFlow::Value* value = getValue(exprid, true);
|
|
|
|
if (value && value->isContainerSizeValue()) {
|
|
|
|
if (value->isImpossible() && value->intvalue == 0) {
|
|
|
|
*result = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!value->isImpossible()) {
|
|
|
|
*result = (value->intvalue == 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-08-11 03:08:49 +02:00
|
|
|
|
2021-07-16 18:49:07 +02:00
|
|
|
void ProgramMemory::setContainerSizeValue(nonneg int exprid, MathLib::bigint value, bool isEqual)
|
|
|
|
{
|
|
|
|
ValueFlow::Value v(value);
|
|
|
|
v.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
|
|
|
if (!isEqual)
|
|
|
|
v.valueKind = ValueFlow::Value::ValueKind::Impossible;
|
|
|
|
values[exprid] = v;
|
|
|
|
}
|
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
void ProgramMemory::setUnknown(nonneg int exprid)
|
2020-02-13 17:04:05 +01:00
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
values[exprid].valueType = ValueFlow::Value::ValueType::UNINIT;
|
2020-02-13 17:04:05 +01:00
|
|
|
}
|
2020-02-13 16:27:06 +01:00
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
bool ProgramMemory::hasValue(nonneg int exprid)
|
2021-01-22 21:47:24 +01:00
|
|
|
{
|
|
|
|
return values.find(exprid) != values.end();
|
|
|
|
}
|
2019-10-30 17:57:46 +01:00
|
|
|
|
2019-10-30 17:58:19 +01:00
|
|
|
void ProgramMemory::swap(ProgramMemory &pm)
|
|
|
|
{
|
2019-10-30 17:57:46 +01:00
|
|
|
values.swap(pm.values);
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:58:19 +01:00
|
|
|
void ProgramMemory::clear()
|
|
|
|
{
|
2019-10-30 17:57:46 +01:00
|
|
|
values.clear();
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:58:19 +01:00
|
|
|
bool ProgramMemory::empty() const
|
|
|
|
{
|
2019-10-30 17:57:46 +01:00
|
|
|
return values.empty();
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:58:19 +01:00
|
|
|
void ProgramMemory::replace(const ProgramMemory &pm)
|
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
for (auto&& p : pm.values) {
|
2019-10-30 17:57:46 +01:00
|
|
|
values[p.first] = p.second;
|
2021-01-21 19:49:37 +01:00
|
|
|
}
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
2019-10-30 17:58:19 +01:00
|
|
|
void ProgramMemory::insert(const ProgramMemory &pm)
|
|
|
|
{
|
2019-10-30 17:57:46 +01:00
|
|
|
for (auto&& p:pm.values)
|
|
|
|
values.insert(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory)
|
|
|
|
{
|
|
|
|
if (!condition)
|
|
|
|
return false;
|
|
|
|
if (condition->str() == "&&") {
|
|
|
|
return conditionIsFalse(condition->astOperand1(), programMemory) ||
|
|
|
|
conditionIsFalse(condition->astOperand2(), programMemory);
|
|
|
|
}
|
|
|
|
ProgramMemory progmem(programMemory);
|
|
|
|
MathLib::bigint result = 0;
|
|
|
|
bool error = false;
|
|
|
|
execute(condition, &progmem, &result, &error);
|
|
|
|
return !error && result == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory)
|
|
|
|
{
|
|
|
|
if (!condition)
|
|
|
|
return false;
|
|
|
|
if (condition->str() == "||") {
|
|
|
|
return conditionIsTrue(condition->astOperand1(), programMemory) ||
|
|
|
|
conditionIsTrue(condition->astOperand2(), programMemory);
|
|
|
|
}
|
|
|
|
ProgramMemory progmem(programMemory);
|
|
|
|
bool error = false;
|
|
|
|
MathLib::bigint result = 0;
|
|
|
|
execute(condition, &progmem, &result, &error);
|
|
|
|
return !error && result == 1;
|
|
|
|
}
|
|
|
|
|
2021-07-16 18:49:07 +02:00
|
|
|
static const Token* getContainerFromEmpty(const Token* tok)
|
|
|
|
{
|
|
|
|
if (!Token::Match(tok->tokAt(-2), ". %name% ("))
|
|
|
|
return nullptr;
|
|
|
|
const Token* containerTok = tok->tokAt(-2)->astOperand1();
|
|
|
|
if (!astIsContainer(containerTok))
|
|
|
|
return nullptr;
|
|
|
|
if (containerTok->valueType()->container &&
|
|
|
|
containerTok->valueType()->container->getYield(tok->strAt(-1)) == Library::Container::Yield::EMPTY)
|
|
|
|
return containerTok;
|
|
|
|
if (Token::simpleMatch(tok->tokAt(-1), "empty ( )"))
|
|
|
|
return containerTok;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const Token* getContainerFromSize(const Token* tok)
|
|
|
|
{
|
|
|
|
if (!Token::Match(tok->tokAt(-2), ". %name% ("))
|
|
|
|
return nullptr;
|
|
|
|
const Token* containerTok = tok->tokAt(-2)->astOperand1();
|
|
|
|
if (!astIsContainer(containerTok))
|
|
|
|
return nullptr;
|
|
|
|
if (containerTok->valueType()->container &&
|
|
|
|
containerTok->valueType()->container->getYield(tok->strAt(-1)) == Library::Container::Yield::SIZE)
|
|
|
|
return containerTok;
|
|
|
|
if (Token::Match(tok->tokAt(-1), "size|length ( )"))
|
|
|
|
return containerTok;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-01-11 08:00:13 +01:00
|
|
|
void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then)
|
2019-10-30 17:57:46 +01:00
|
|
|
{
|
|
|
|
if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
|
2021-01-11 08:00:13 +01:00
|
|
|
if (then && !Token::Match(tok, "==|>=|<=|<|>"))
|
2019-10-30 17:57:46 +01:00
|
|
|
return;
|
|
|
|
if (!then && !Token::Match(tok, "<|>|!="))
|
|
|
|
return;
|
|
|
|
ValueFlow::Value truevalue;
|
|
|
|
ValueFlow::Value falsevalue;
|
2021-01-11 08:00:13 +01:00
|
|
|
const Token* vartok = parseCompareInt(tok, truevalue, falsevalue, [&](const Token* t) -> std::vector<MathLib::bigint> {
|
|
|
|
if (t->hasKnownIntValue())
|
|
|
|
return {t->values().front().intvalue};
|
|
|
|
MathLib::bigint result = 0;
|
|
|
|
bool error = false;
|
|
|
|
execute(t, &pm, &result, &error);
|
|
|
|
if (!error)
|
|
|
|
return {result};
|
|
|
|
return std::vector<MathLib::bigint>{};
|
|
|
|
});
|
2019-10-30 17:57:46 +01:00
|
|
|
if (!vartok)
|
|
|
|
return;
|
2021-01-21 19:49:37 +01:00
|
|
|
if (vartok->exprId() == 0)
|
2019-10-30 17:57:46 +01:00
|
|
|
return;
|
|
|
|
if (!truevalue.isIntValue())
|
|
|
|
return;
|
2021-01-21 19:49:37 +01:00
|
|
|
if (endTok && isExpressionChanged(vartok, tok->next(), endTok, settings, true))
|
2019-10-30 17:57:46 +01:00
|
|
|
return;
|
2021-07-16 18:49:07 +02:00
|
|
|
bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then);
|
|
|
|
if (!impossible)
|
|
|
|
pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue);
|
|
|
|
const Token* containerTok = getContainerFromSize(vartok);
|
|
|
|
if (containerTok)
|
|
|
|
pm.setContainerSizeValue(containerTok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, !impossible);
|
2019-10-30 17:57:46 +01:00
|
|
|
} else if (Token::simpleMatch(tok, "!")) {
|
|
|
|
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then);
|
|
|
|
} else if (then && Token::simpleMatch(tok, "&&")) {
|
|
|
|
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
|
|
|
|
programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
|
|
|
|
} else if (!then && Token::simpleMatch(tok, "||")) {
|
|
|
|
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
|
|
|
|
programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
|
2021-01-21 19:49:37 +01:00
|
|
|
} else if (tok->exprId() > 0) {
|
|
|
|
if (then && !astIsPointer(tok) && !astIsBool(tok))
|
|
|
|
return;
|
|
|
|
if (endTok && isExpressionChanged(tok, tok->next(), endTok, settings, true))
|
|
|
|
return;
|
|
|
|
pm.setIntValue(tok->exprId(), then);
|
2021-07-16 18:49:07 +02:00
|
|
|
const Token* containerTok = getContainerFromEmpty(tok);
|
|
|
|
if (containerTok)
|
|
|
|
pm.setContainerSizeValue(containerTok->exprId(), 0, then);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings)
|
|
|
|
{
|
|
|
|
if (!scope)
|
|
|
|
return;
|
|
|
|
if (!scope->isLocal())
|
|
|
|
return;
|
|
|
|
assert(scope != scope->nestedIn);
|
|
|
|
fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings);
|
2021-01-11 08:00:13 +01:00
|
|
|
if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse || scope->type == Scope::eFor) {
|
|
|
|
const Token* condTok = getCondTokFromEnd(scope->bodyEnd);
|
|
|
|
if (!condTok)
|
2019-10-30 17:57:46 +01:00
|
|
|
return;
|
2021-06-09 09:20:43 +02:00
|
|
|
MathLib::bigint result = 0;
|
|
|
|
bool error = false;
|
|
|
|
execute(condTok, &pm, &result, &error);
|
|
|
|
if (error)
|
|
|
|
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings)
|
|
|
|
{
|
|
|
|
fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings);
|
|
|
|
}
|
|
|
|
|
2021-01-16 19:05:51 +01:00
|
|
|
static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, const ProgramMemory::Map& vars)
|
2019-10-30 17:57:46 +01:00
|
|
|
{
|
|
|
|
int indentlevel = 0;
|
|
|
|
for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) {
|
2021-01-21 19:49:37 +01:00
|
|
|
if ((Token::simpleMatch(tok2, "=") || Token::Match(tok2->previous(), "%var% (|{")) && tok2->astOperand1() &&
|
|
|
|
tok2->astOperand2()) {
|
|
|
|
bool setvar = false;
|
|
|
|
const Token* vartok = tok2->astOperand1();
|
|
|
|
const Token* valuetok = tok2->astOperand2();
|
2021-01-16 19:05:51 +01:00
|
|
|
for (const auto& p:vars) {
|
2021-01-21 19:49:37 +01:00
|
|
|
if (p.first != vartok->exprId())
|
2019-10-30 17:57:46 +01:00
|
|
|
continue;
|
2020-11-10 16:00:55 +01:00
|
|
|
if (vartok == tok)
|
|
|
|
continue;
|
2021-01-21 19:49:37 +01:00
|
|
|
pm.setValue(vartok->exprId(), p.second);
|
2019-10-30 17:57:46 +01:00
|
|
|
setvar = true;
|
|
|
|
}
|
2021-01-21 19:49:37 +01:00
|
|
|
if (!setvar) {
|
|
|
|
if (!pm.hasValue(vartok->exprId())) {
|
|
|
|
MathLib::bigint result = 0;
|
|
|
|
bool error = false;
|
|
|
|
execute(valuetok, &pm, &result, &error);
|
|
|
|
if (!error)
|
|
|
|
pm.setIntValue(vartok->exprId(), result);
|
|
|
|
else
|
|
|
|
pm.setUnknown(vartok->exprId());
|
|
|
|
}
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
2021-01-21 19:49:37 +01:00
|
|
|
} else if (tok2->exprId() > 0 && Token::Match(tok2, ".|(|[|*|%var%") && !pm.hasValue(tok2->exprId()) &&
|
|
|
|
isVariableChanged(tok2, 0, nullptr, true)) {
|
|
|
|
pm.setUnknown(tok2->exprId());
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tok2->str() == "{") {
|
|
|
|
if (indentlevel <= 0)
|
|
|
|
break;
|
|
|
|
--indentlevel;
|
2021-06-19 13:58:57 +02:00
|
|
|
if (Token::simpleMatch(tok2->previous(), "else {"))
|
|
|
|
tok2 = tok2->linkAt(-2)->previous();
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
if (tok2->str() == "}") {
|
2021-06-19 13:58:57 +02:00
|
|
|
const Token *cond = getCondTokFromEnd(tok2);
|
|
|
|
const bool inElse = Token::simpleMatch(tok2->link()->previous(), "else {");
|
|
|
|
if (cond) {
|
|
|
|
if (conditionIsFalse(cond, state)) {
|
|
|
|
if (inElse) {
|
|
|
|
++indentlevel;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tok2 = cond->astParent()->previous();
|
2021-06-19 14:47:35 +02:00
|
|
|
} else if (conditionIsTrue(cond, state)) {
|
2021-06-19 13:58:57 +02:00
|
|
|
if (inElse)
|
|
|
|
tok2 = tok2->link()->tokAt(-2);
|
|
|
|
++indentlevel;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-05 16:25:33 +01:00
|
|
|
static void removeModifiedVars(ProgramMemory& pm, const Token* tok, const Token* origin)
|
|
|
|
{
|
|
|
|
for (auto i = pm.values.begin(), last = pm.values.end(); i != last;) {
|
|
|
|
if (isVariableChanged(origin, tok, i->first, false, nullptr, true)) {
|
|
|
|
i = pm.values.erase(i);
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ProgramMemory getInitialProgramState(const Token* tok,
|
2020-01-05 17:00:34 +01:00
|
|
|
const Token* origin,
|
2020-02-19 07:55:04 +01:00
|
|
|
const ProgramMemory::Map& vars = ProgramMemory::Map {})
|
2020-01-05 16:25:33 +01:00
|
|
|
{
|
|
|
|
ProgramMemory pm;
|
|
|
|
if (origin) {
|
|
|
|
fillProgramMemoryFromConditions(pm, origin, nullptr);
|
|
|
|
const ProgramMemory state = pm;
|
|
|
|
fillProgramMemoryFromAssignments(pm, tok, state, vars);
|
|
|
|
removeModifiedVars(pm, tok, origin);
|
|
|
|
}
|
|
|
|
return pm;
|
|
|
|
}
|
|
|
|
|
2020-02-19 07:55:04 +01:00
|
|
|
void ProgramMemoryState::insert(const ProgramMemory &pm, const Token* origin)
|
|
|
|
{
|
2020-02-21 16:18:41 +01:00
|
|
|
if (origin)
|
|
|
|
for (auto&& p:pm.values)
|
2020-02-19 07:55:04 +01:00
|
|
|
origins.insert(std::make_pair(p.first, origin));
|
|
|
|
state.insert(pm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProgramMemoryState::replace(const ProgramMemory &pm, const Token* origin)
|
|
|
|
{
|
2020-02-21 16:18:41 +01:00
|
|
|
if (origin)
|
|
|
|
for (auto&& p:pm.values)
|
2020-02-19 07:55:04 +01:00
|
|
|
origins[p.first] = origin;
|
|
|
|
state.replace(pm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProgramMemoryState::addState(const Token* tok, const ProgramMemory::Map& vars)
|
|
|
|
{
|
2021-01-21 19:49:37 +01:00
|
|
|
ProgramMemory pm = state;
|
2020-02-19 07:55:04 +01:00
|
|
|
fillProgramMemoryFromConditions(pm, tok, nullptr);
|
|
|
|
for (const auto& p:vars) {
|
2021-01-27 19:49:13 +01:00
|
|
|
nonneg int exprid = p.first;
|
2020-02-19 07:55:04 +01:00
|
|
|
const ValueFlow::Value &value = p.second;
|
2021-01-21 19:49:37 +01:00
|
|
|
pm.setValue(exprid, value);
|
2020-02-19 07:55:04 +01:00
|
|
|
if (value.varId)
|
|
|
|
pm.setIntValue(value.varId, value.varvalue);
|
|
|
|
}
|
2021-01-16 19:05:51 +01:00
|
|
|
ProgramMemory local = pm;
|
2020-02-19 07:55:04 +01:00
|
|
|
fillProgramMemoryFromAssignments(pm, tok, local, vars);
|
|
|
|
replace(pm, tok);
|
|
|
|
}
|
|
|
|
|
2021-07-16 18:49:07 +02:00
|
|
|
void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty)
|
2020-02-19 07:55:04 +01:00
|
|
|
{
|
|
|
|
ProgramMemory pm = state;
|
2021-07-16 18:49:07 +02:00
|
|
|
if (isEmpty)
|
|
|
|
pm.setContainerSizeValue(tok->exprId(), 0, b);
|
|
|
|
else
|
|
|
|
programMemoryParseCondition(pm, tok, nullptr, nullptr, b);
|
2021-06-09 09:20:43 +02:00
|
|
|
const Token* origin = tok;
|
|
|
|
const Token* top = tok->astTop();
|
|
|
|
if (top && Token::Match(top->previous(), "for|while ("))
|
|
|
|
origin = top->link();
|
|
|
|
replace(pm, origin);
|
2020-02-19 07:55:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProgramMemoryState::removeModifiedVars(const Token* tok)
|
|
|
|
{
|
|
|
|
for (auto i = state.values.begin(), last = state.values.end(); i != last;) {
|
|
|
|
if (isVariableChanged(origins[i->first], tok, i->first, false, nullptr, true)) {
|
|
|
|
origins.erase(i->first);
|
|
|
|
i = state.values.erase(i);
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 09:20:43 +02:00
|
|
|
ProgramMemory ProgramMemoryState::get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const
|
2020-02-19 07:55:04 +01:00
|
|
|
{
|
|
|
|
ProgramMemoryState local = *this;
|
2021-06-09 09:20:43 +02:00
|
|
|
if (ctx)
|
|
|
|
local.addState(ctx, vars);
|
|
|
|
const Token* start = previousBeforeAstLeftmostLeaf(tok);
|
|
|
|
if (!start)
|
|
|
|
start = tok;
|
|
|
|
|
|
|
|
if (!ctx || precedes(start, ctx)) {
|
|
|
|
local.removeModifiedVars(start);
|
2021-07-10 07:35:16 +02:00
|
|
|
local.addState(start, vars);
|
2021-06-09 09:20:43 +02:00
|
|
|
} else {
|
|
|
|
local.removeModifiedVars(ctx);
|
|
|
|
}
|
2020-02-19 07:55:04 +01:00
|
|
|
return local.state;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProgramMemory getProgramMemory(const Token *tok, const ProgramMemory::Map& vars)
|
2020-02-16 16:02:22 +01:00
|
|
|
{
|
|
|
|
ProgramMemory programMemory;
|
2020-02-16 19:58:09 +01:00
|
|
|
for (const auto& p:vars) {
|
2020-02-16 16:02:22 +01:00
|
|
|
const ValueFlow::Value &value = p.second;
|
|
|
|
programMemory.replace(getInitialProgramState(tok, value.tokvalue));
|
|
|
|
programMemory.replace(getInitialProgramState(tok, value.condition));
|
|
|
|
}
|
|
|
|
fillProgramMemoryFromConditions(programMemory, tok, nullptr);
|
|
|
|
ProgramMemory state;
|
2020-02-16 19:58:09 +01:00
|
|
|
for (const auto& p:vars) {
|
2021-01-27 19:49:13 +01:00
|
|
|
nonneg int exprid = p.first;
|
2020-02-16 16:02:22 +01:00
|
|
|
const ValueFlow::Value &value = p.second;
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory.setValue(exprid, value);
|
2020-02-16 16:02:22 +01:00
|
|
|
if (value.varId)
|
|
|
|
programMemory.setIntValue(value.varId, value.varvalue);
|
|
|
|
}
|
2020-02-19 07:55:04 +01:00
|
|
|
state = programMemory;
|
2020-02-16 16:02:22 +01:00
|
|
|
fillProgramMemoryFromAssignments(programMemory, tok, state, vars);
|
|
|
|
return programMemory;
|
|
|
|
}
|
|
|
|
|
2021-01-27 19:49:13 +01:00
|
|
|
ProgramMemory getProgramMemory(const Token* tok, nonneg int exprid, const ValueFlow::Value& value)
|
2019-10-30 17:57:46 +01:00
|
|
|
{
|
|
|
|
ProgramMemory programMemory;
|
2020-01-05 16:25:33 +01:00
|
|
|
programMemory.replace(getInitialProgramState(tok, value.tokvalue));
|
|
|
|
programMemory.replace(getInitialProgramState(tok, value.condition));
|
2020-02-13 16:27:06 +01:00
|
|
|
fillProgramMemoryFromConditions(programMemory, tok, nullptr);
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory.setValue(exprid, value);
|
2019-10-30 17:57:46 +01:00
|
|
|
if (value.varId)
|
|
|
|
programMemory.setIntValue(value.varId, value.varvalue);
|
|
|
|
const ProgramMemory state = programMemory;
|
2021-01-21 19:49:37 +01:00
|
|
|
fillProgramMemoryFromAssignments(programMemory, tok, state, {{exprid, value}});
|
2019-10-30 17:57:46 +01:00
|
|
|
return programMemory;
|
|
|
|
}
|
|
|
|
|
2021-07-24 22:44:18 +02:00
|
|
|
static PMEvaluateFunction evaluateAsInt(PMEvaluateFunction f, ValueFlow::Value::ValueType t)
|
|
|
|
{
|
|
|
|
return [=](const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result) -> bool {
|
|
|
|
const ValueFlow::Value* value = expr->getKnownValue(t);
|
|
|
|
if (!value)
|
|
|
|
value = programMemory->getValue(expr->exprId());
|
|
|
|
if (value && value->valueType == t) {
|
|
|
|
*result = value->intvalue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return f && f(expr, programMemory, result);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::set<ValueFlow::Value::ValueType> findIteratorTypes(const ProgramMemory& pm)
|
|
|
|
{
|
|
|
|
std::set<ValueFlow::Value::ValueType> result;
|
|
|
|
for (auto&& p : pm.values) {
|
|
|
|
if (p.second.isIteratorValue())
|
|
|
|
result.insert(p.second.valueType);
|
|
|
|
if (result.size() == 2)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isIterator(const Token* expr)
|
|
|
|
{
|
|
|
|
if (!expr)
|
|
|
|
return false;
|
|
|
|
if (astIsIterator(expr))
|
|
|
|
return true;
|
|
|
|
return std::any_of(expr->values().begin(), expr->values().end(), std::mem_fn(&ValueFlow::Value::isIteratorValue));
|
|
|
|
}
|
|
|
|
|
|
|
|
void execute(const Token* expr,
|
|
|
|
ProgramMemory* const programMemory,
|
|
|
|
MathLib::bigint* result,
|
|
|
|
bool* error,
|
|
|
|
const PMEvaluateFunction& f)
|
2019-10-30 17:57:46 +01:00
|
|
|
{
|
|
|
|
if (!expr)
|
|
|
|
*error = true;
|
|
|
|
|
2021-07-24 22:44:18 +02:00
|
|
|
else if (f && f(expr, programMemory, result))
|
|
|
|
*error = false;
|
|
|
|
|
2021-01-13 12:36:26 +01:00
|
|
|
else if (expr->hasKnownIntValue() && !expr->isAssignmentOp()) {
|
2019-10-30 17:57:46 +01:00
|
|
|
*result = expr->values().front().intvalue;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->isNumber()) {
|
|
|
|
*result = MathLib::toLongNumber(expr->str());
|
|
|
|
if (MathLib::isFloat(expr->str()))
|
|
|
|
*error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->varId() > 0) {
|
|
|
|
if (!programMemory->getIntValue(expr->varId(), result))
|
|
|
|
*error = true;
|
|
|
|
}
|
|
|
|
|
2021-02-10 08:11:06 +01:00
|
|
|
else if (Token::Match(expr->tokAt(-3), "%var% . %name% (") && astIsContainer(expr->tokAt(-3))) {
|
|
|
|
const Token* containerTok = expr->tokAt(-3);
|
|
|
|
Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));
|
|
|
|
if (yield == Library::Container::Yield::SIZE) {
|
|
|
|
if (!programMemory->getContainerSizeValue(containerTok->exprId(), result))
|
|
|
|
*error = true;
|
|
|
|
} else if (yield == Library::Container::Yield::EMPTY) {
|
|
|
|
if (!programMemory->getContainerEmptyValue(containerTok->exprId(), result))
|
|
|
|
*error = true;
|
|
|
|
} else {
|
|
|
|
*error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 19:49:37 +01:00
|
|
|
else if (expr->exprId() > 0 && programMemory->hasValue(expr->exprId())) {
|
|
|
|
if (!programMemory->getIntValue(expr->exprId(), result))
|
|
|
|
*error = true;
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->isComparisonOp()) {
|
|
|
|
MathLib::bigint result1(0), result2(0);
|
2021-04-18 21:51:47 +02:00
|
|
|
bool error1 = false;
|
|
|
|
bool error2 = false;
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand1(), programMemory, &result1, &error1, f);
|
|
|
|
execute(expr->astOperand2(), programMemory, &result2, &error2, f);
|
|
|
|
if (error1 && error2 && (isIterator(expr->astOperand1()) || isIterator(expr->astOperand2()))) {
|
|
|
|
for (ValueFlow::Value::ValueType t : findIteratorTypes(*programMemory)) {
|
|
|
|
execute(expr->astOperand1(), programMemory, &result1, &error1, evaluateAsInt(f, t));
|
|
|
|
execute(expr->astOperand2(), programMemory, &result2, &error2, evaluateAsInt(f, t));
|
|
|
|
if (!error1 && !error2)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-03-30 14:02:28 +02:00
|
|
|
if (error1 && error2) {
|
|
|
|
*error = true;
|
|
|
|
} else if (error1 && !error2) {
|
|
|
|
ValueFlow::Value v = inferCondition(expr->str(), expr->astOperand1(), result2);
|
|
|
|
*error = !v.isKnown();
|
|
|
|
*result = v.intvalue;
|
|
|
|
} else if (!error1 && error2) {
|
|
|
|
ValueFlow::Value v = inferCondition(expr->str(), result1, expr->astOperand2());
|
|
|
|
*error = !v.isKnown();
|
|
|
|
*result = v.intvalue;
|
|
|
|
} else {
|
|
|
|
if (expr->str() == "<")
|
|
|
|
*result = result1 < result2;
|
|
|
|
else if (expr->str() == "<=")
|
|
|
|
*result = result1 <= result2;
|
|
|
|
else if (expr->str() == ">")
|
|
|
|
*result = result1 > result2;
|
|
|
|
else if (expr->str() == ">=")
|
|
|
|
*result = result1 >= result2;
|
|
|
|
else if (expr->str() == "==")
|
|
|
|
*result = result1 == result2;
|
|
|
|
else if (expr->str() == "!=")
|
|
|
|
*result = result1 != result2;
|
|
|
|
}
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->isAssignmentOp()) {
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand2(), programMemory, result, error, f);
|
2021-01-21 19:49:37 +01:00
|
|
|
if (!expr->astOperand1() || !expr->astOperand1()->exprId())
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true;
|
|
|
|
if (*error)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (expr->str() == "=") {
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
long long intValue;
|
2021-01-21 19:49:37 +01:00
|
|
|
if (!programMemory->getIntValue(expr->astOperand1()->exprId(), &intValue)) {
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (expr->str() == "+=")
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue + *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "-=")
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue - *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "*=")
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue * *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "/=" && *result != 0)
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue / *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "%=" && *result != 0)
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue % *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "&=")
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue & *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "|=")
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue | *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
else if (expr->str() == "^=")
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), intValue ^ *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (Token::Match(expr, "++|--")) {
|
2021-01-27 19:49:13 +01:00
|
|
|
if (!expr->astOperand1() || expr->astOperand1()->exprId() == 0)
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true;
|
|
|
|
else {
|
|
|
|
long long intValue;
|
2021-01-21 19:49:37 +01:00
|
|
|
if (!programMemory->getIntValue(expr->astOperand1()->exprId(), &intValue))
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true;
|
|
|
|
else {
|
|
|
|
if (intValue == 0 &&
|
|
|
|
expr->str() == "--" &&
|
|
|
|
expr->astOperand1()->variable() &&
|
2020-01-11 14:00:41 +01:00
|
|
|
expr->astOperand1()->variable()->isUnsigned())
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true; // overflow
|
|
|
|
*result = intValue + (expr->str() == "++" ? 1 : -1);
|
2021-01-21 19:49:37 +01:00
|
|
|
programMemory->setIntValue(expr->astOperand1()->exprId(), *result);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) {
|
|
|
|
MathLib::bigint result1(0), result2(0);
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand1(), programMemory, &result1, error, f);
|
|
|
|
execute(expr->astOperand2(), programMemory, &result2, error, f);
|
2019-10-30 17:57:46 +01:00
|
|
|
if (expr->str() == "+")
|
|
|
|
*result = result1 + result2;
|
|
|
|
else if (expr->str() == "-")
|
|
|
|
*result = result1 - result2;
|
|
|
|
else if (expr->str() == "*") {
|
|
|
|
if (result2 && (result1 > std::numeric_limits<MathLib::bigint>::max()/result2))
|
|
|
|
*error = true;
|
|
|
|
else
|
|
|
|
*result = result1 * result2;
|
|
|
|
} else if (result2 == 0)
|
|
|
|
*error = true;
|
|
|
|
else if (expr->str() == "/")
|
|
|
|
*result = result1 / result2;
|
|
|
|
else if (expr->str() == "%")
|
|
|
|
*result = result1 % result2;
|
|
|
|
else if (expr->str() == "<<") {
|
|
|
|
if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
|
|
|
|
*error= true;
|
|
|
|
} else {
|
|
|
|
*result = result1 << result2;
|
|
|
|
}
|
|
|
|
} else if (expr->str() == ">>") {
|
2021-04-28 11:57:28 +02:00
|
|
|
if (result2 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
|
2019-10-30 17:57:46 +01:00
|
|
|
*error=true;
|
|
|
|
} else {
|
|
|
|
*result = result1 >> result2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->str() == "&&") {
|
|
|
|
bool error1 = false;
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand1(), programMemory, result, &error1, f);
|
2019-10-30 17:57:46 +01:00
|
|
|
if (!error1 && *result == 0)
|
|
|
|
*result = 0;
|
|
|
|
else {
|
|
|
|
bool error2 = false;
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand2(), programMemory, result, &error2, f);
|
2020-12-16 17:25:21 +01:00
|
|
|
if (error1 || error2)
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->str() == "||") {
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand1(), programMemory, result, error, f);
|
2020-12-16 17:25:21 +01:00
|
|
|
if (*result == 1 && *error == false)
|
|
|
|
*result = 1;
|
|
|
|
else if (*result == 0 && *error == false)
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand2(), programMemory, result, error, f);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->str() == "!") {
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand1(), programMemory, result, error, f);
|
2019-10-30 17:57:46 +01:00
|
|
|
*result = !(*result);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand1(), programMemory, result, error, f);
|
|
|
|
execute(expr->astOperand2(), programMemory, result, error, f);
|
2019-10-30 17:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
|
|
|
|
const Token *tokvalue = nullptr;
|
2021-01-21 19:49:37 +01:00
|
|
|
if (!programMemory->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) {
|
2019-10-30 17:57:46 +01:00
|
|
|
auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(),
|
|
|
|
expr->astOperand1()->values().end(),
|
|
|
|
std::mem_fn(&ValueFlow::Value::isTokValue));
|
|
|
|
if (tokvalue_it == expr->astOperand1()->values().end()) {
|
|
|
|
*error = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
tokvalue = tokvalue_it->tokvalue;
|
|
|
|
}
|
|
|
|
if (!tokvalue || !tokvalue->isLiteral()) {
|
|
|
|
*error = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const std::string strValue = tokvalue->strValue();
|
|
|
|
MathLib::bigint index = 0;
|
2021-07-24 22:44:18 +02:00
|
|
|
execute(expr->astOperand2(), programMemory, &index, error, f);
|
2019-10-30 17:57:46 +01:00
|
|
|
if (index >= 0 && index < strValue.size())
|
|
|
|
*result = strValue[index];
|
|
|
|
else if (index == strValue.size())
|
|
|
|
*result = 0;
|
|
|
|
else
|
|
|
|
*error = true;
|
2021-02-10 11:42:00 +01:00
|
|
|
} else
|
2019-10-30 17:57:46 +01:00
|
|
|
*error = true;
|
|
|
|
}
|