From 1a6cd75cc227c23ba1262b561ba2a064522ebffc Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Fri, 12 Aug 2022 12:43:41 +0300 Subject: [PATCH 01/19] fix constraints printing --- .../src/printers/KleeConstraintsPrinter.cpp | 14 +++++----- server/src/types/Types.cpp | 26 +++++++++---------- server/src/types/Types.h | 3 ++- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/server/src/printers/KleeConstraintsPrinter.cpp b/server/src/printers/KleeConstraintsPrinter.cpp index 1161e1fd8..c58e67319 100644 --- a/server/src/printers/KleeConstraintsPrinter.cpp +++ b/server/src/printers/KleeConstraintsPrinter.cpp @@ -5,6 +5,8 @@ #include "loguru.h" +#include + using namespace types; using printer::KleeConstraintsPrinter; @@ -191,15 +193,15 @@ void KleeConstraintsPrinter::genConstraintsForStruct(const ConstraintsState &sta } std::string KleeConstraintsPrinter::cexConstraints(const std::string &name, const types::Type &type) { - std::stringstream ssCex; if (!CollectionUtils::containsKey(TypesHandler::preferredConstraints(), type.baseType())) { return ""; } - for (int i = 0; i < TypesHandler::preferredConstraints().at(type.baseType()).size(); i++) { - ssCex << name; - ssCex << TypesHandler::preferredConstraints().at(type.baseType())[i]; - if (i < (int)TypesHandler::preferredConstraints().at(type.baseType()).size() - 1) { - ssCex << " & "; + std::stringstream ssCex; + const std::vector &constraints = TypesHandler::preferredConstraints().at(type.baseType()); + for (size_t i = 0; i < constraints.size(); i++) { + ssCex << name << " " << constraints[i]; + if (i + 1 < constraints.size()) { + ssCex << " && "; } } return ssCex.str(); diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index b6eb4d357..7d1befc42 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -730,23 +730,23 @@ std::unordered_map types::TypesHandler::boolTypesToSize return boolTypes; } -std::unordered_map> types::TypesHandler::preferredConstraints() noexcept { - static std::unordered_map> constraints = { - {"char", {" >= 'a'", " <= 'z'", " != '\\0'"}}, - {"signed char", {" >= 'a'", " <= 'z'", " != '\\0'"}}, - {"unsigned char", {" >= 'a'", " <= 'z'", " != '\\0'"}}, - {"short", {" >= -10 ", " <= 10"}}, - {"int", {" >= -10 ", " <= 10"}}, - {"long", {" >= -10 ", " <= 10"}}, - {"long long", {" >= -10 ", " <= 10"}}, +const std::unordered_map> &types::TypesHandler::preferredConstraints() noexcept { + static const std::unordered_map> constraints = { + {"char", {">= 'a'", "<= 'z'", "!= '\\0'"}}, + {"signed char", {">= 'a'", "<= 'z'", "!= '\\0'"}}, + {"unsigned char", {">= 'a'", "<= 'z'", "!= '\\0'"}}, + {"short", {">= -10", "<= 10"}}, + {"int", {">= -10", "<= 10"}}, + {"long", {">= -10", "<= 10"}}, + {"long long", {">= -10", "<= 10"}}, {"unsigned short", {"<= 10"}}, {"unsigned int", {"<= 10"}}, {"unsigned long", {"<= 10"}}, {"unsigned long long", {"<= 10"}}, - {"float", {" >= -10 ", " <= 10"}}, - {"double", {" >= -10 ", " <= 10"}}, - {"long double", {" >= -10 ", " <= 10"}}, - {"void", {" <= 10"}}, + {"float", {">= -10", "<= 10"}}, + {"double", {">= -10", "<= 10"}}, + {"long double", {">= -10", "<= 10"}}, + {"void", {"<= 10"}}, }; return constraints; diff --git a/server/src/types/Types.h b/server/src/types/Types.h index e80b2fe1c..081f9efdb 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -543,7 +544,7 @@ namespace types { * 'klee_prefer_cex' function. * @return map type -> constraints. */ - static std::unordered_map> preferredConstraints() noexcept; + static const std::unordered_map> &preferredConstraints() noexcept; size_t getPointerSize() const noexcept { return sizeContext.pointerSize; From 61ce24c65821c1e5d1d688d48b1c0d6117db99d5 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Fri, 12 Aug 2022 13:02:04 +0300 Subject: [PATCH 02/19] fix size_t --- server/src/Tests.h | 8 ++++---- server/src/fetchers/Fetcher.cpp | 4 ++-- server/src/fetchers/Fetcher.h | 8 ++++---- server/src/types/Types.cpp | 6 +++--- server/src/types/Types.h | 14 +++++++------- server/src/types/TypesResolver.cpp | 5 +++-- server/src/types/TypesResolver.h | 3 ++- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/server/src/Tests.h b/server/src/Tests.h index 124523cec..42db4d6fb 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -321,13 +321,13 @@ namespace tests { struct MethodParam { types::Type type; std::string name; - std::optional alignment; + std::optional alignment; bool hasIncompleteType = false; MethodParam(types::Type type, std::string name, - std::optional alignment, + std::optional alignment, bool hasIncompleteType = false) : type(std::move(type)), name(std::move(name)), alignment(std::move(alignment)), hasIncompleteType(hasIncompleteType) { @@ -356,14 +356,14 @@ namespace tests { struct TestCaseParamValue { std::string name; - std::optional alignment; + std::optional alignment; std::shared_ptr view; std::vector lazyParams; std::vector lazyValues; TestCaseParamValue() = default; TestCaseParamValue(const std::string &_name, - const std::optional &_alignment, + const std::optional &_alignment, const std::shared_ptr &_view) : name(_name), alignment(_alignment), diff --git a/server/src/fetchers/Fetcher.cpp b/server/src/fetchers/Fetcher.cpp index 06b304479..ac53c00f6 100644 --- a/server/src/fetchers/Fetcher.cpp +++ b/server/src/fetchers/Fetcher.cpp @@ -24,8 +24,8 @@ Fetcher::Fetcher(Options options, const std::shared_ptr &compilationDatabase, tests::TestsMap &tests, types::TypeMaps *types, - uint64_t *pointerSize, - uint64_t *maximumAlignment, + size_t *pointerSize, + size_t *maximumAlignment, const fs::path &compileCommandsJsonPath, bool fetchFunctionBodies) : options(options), projectTests(&tests), projectTypes(types), diff --git a/server/src/fetchers/Fetcher.h b/server/src/fetchers/Fetcher.h index f354c55d0..3ea6a1b2f 100644 --- a/server/src/fetchers/Fetcher.h +++ b/server/src/fetchers/Fetcher.h @@ -59,8 +59,8 @@ class Fetcher { const std::shared_ptr &compilationDatabase, tests::TestsMap &tests, types::TypeMaps *types, - uint64_t *pointerSize, - uint64_t *maximumAlignment, + size_t *pointerSize, + size_t *maximumAlignment, const fs::path &compileCommandsJsonPath, bool fetchFunctionBodies); @@ -76,8 +76,8 @@ class Fetcher { tests::TestsMap *const projectTests; types::TypeMaps *const projectTypes; - uint64_t *const pointerSize; - uint64_t *const maximumAlignment; + size_t *const pointerSize; + size_t *const maximumAlignment; fs::path buildRootPath; std::shared_ptr structsToDeclare = std::make_shared(); diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 7d1befc42..177898491 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -621,9 +621,9 @@ std::string types::TypesHandler::removeConstPrefix(const TypeName &type) { std::vector tmp = StringUtils::split(type); if (tmp[0] == CONST_QUALIFIER) { std::string res; - for (int i = 1; i < (int) tmp.size(); i++) { + for (size_t i = 1; i < tmp.size(); i++) { res += tmp[i]; - if (i < (int) tmp.size() - 1) { + if (i + 1 < tmp.size()) { res.push_back(' '); } } @@ -698,7 +698,7 @@ testsgen::ValidationType types::TypesHandler::getIntegerValidationType(const Typ } else if (size == 8) { return (isUnsigned) ? testsgen::UINT64_T : testsgen::INT64_T; } else { - ABORT_F("Unknown integer size: %d", (int)size); + ABORT_F("Unknown integer size: %zu", size); } } diff --git a/server/src/types/Types.h b/server/src/types/Types.h index 081f9efdb..e52499822 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -259,9 +259,9 @@ namespace types { struct Field { types::Type type; std::string name; - unsigned int size; + size_t size; // reassigned in structFields - unsigned int offset = 0; + size_t offset = 0; enum AccessSpecifier { AS_pubic, AS_protected, @@ -275,8 +275,8 @@ namespace types { fs::path filePath; std::string name; std::string definition; - uint64_t size; - uint64_t alignment; + size_t size; + size_t alignment; }; typedef std::unordered_map> FPointerMap; @@ -333,8 +333,8 @@ namespace types { class TypesHandler { public: struct SizeContext { - uint64_t pointerSize = 8; - uint64_t maximumAlignment = 16; + size_t pointerSize = 8; + size_t maximumAlignment = 16; }; explicit TypesHandler(TypeMaps &types, SizeContext sizeContext) @@ -550,7 +550,7 @@ namespace types { return sizeContext.pointerSize; } - uint64_t getMaximumAlignment() const noexcept { + size_t getMaximumAlignment() const noexcept { return sizeContext.maximumAlignment; } diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index e505ef58d..8debe6f07 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -236,8 +236,9 @@ void TypesResolver::resolveEnum(const clang::EnumDecl *EN, const std::string &na << "\tFile path: " << enumInfo.filePath.string(); LOG_S(DEBUG) << ss.str(); } -void TypesResolver::updateMaximumAlignment(uint64_t alignment) const { - uint64_t &maximumAlignment = *(this->parent->maximumAlignment); + +void TypesResolver::updateMaximumAlignment(size_t alignment) const { + size_t &maximumAlignment = *(this->parent->maximumAlignment); maximumAlignment = std::max(maximumAlignment, alignment); } diff --git a/server/src/types/TypesResolver.h b/server/src/types/TypesResolver.h index f1c7c3b81..be9d3a7a8 100644 --- a/server/src/types/TypesResolver.h +++ b/server/src/types/TypesResolver.h @@ -5,6 +5,7 @@ #include +#include #include #include @@ -29,7 +30,7 @@ class TypesResolver { std::string getFullname(const clang::TagDecl *TD, const clang::QualType &canonicalType, uint64_t id, const fs::path &sourceFilePath); - void updateMaximumAlignment(uint64_t alignment) const; + void updateMaximumAlignment(size_t alignment) const; }; From 8027219141a2398c78ed5cf8f9b01cf0d74316b3 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Fri, 12 Aug 2022 19:01:04 +0300 Subject: [PATCH 03/19] fix TL --- server/src/printers/KleeConstraintsPrinter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/printers/KleeConstraintsPrinter.cpp b/server/src/printers/KleeConstraintsPrinter.cpp index c58e67319..a71c249e8 100644 --- a/server/src/printers/KleeConstraintsPrinter.cpp +++ b/server/src/printers/KleeConstraintsPrinter.cpp @@ -201,7 +201,7 @@ std::string KleeConstraintsPrinter::cexConstraints(const std::string &name, cons for (size_t i = 0; i < constraints.size(); i++) { ssCex << name << " " << constraints[i]; if (i + 1 < constraints.size()) { - ssCex << " && "; + ssCex << " & "; } } return ssCex.str(); From 47b37fbe4fab0251931b8db6a4fec02300118b13 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Mon, 15 Aug 2022 12:15:00 +0300 Subject: [PATCH 04/19] minor refactoring --- server/src/Tests.cpp | 2 +- server/src/types/Types.cpp | 40 ++++++++++++------------------ server/src/types/Types.h | 19 ++++++-------- server/src/types/TypesResolver.cpp | 12 ++++----- 4 files changed, 31 insertions(+), 42 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 888530d4d..43c65b2e8 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -367,7 +367,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector } std::optional entryValue; - if(curStruct.hasUnnamedFields) { + if(curStruct.hasAnonymousStructOrUnion) { auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte"); const std::shared_ptr rawDataView = arrayView(byteArray, bytesType, curStruct.size, offset, usage); entryValue = PrinterUtils::convertBytesToUnion(curStruct.name, rawDataView->getEntryValue(nullptr)); diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 177898491..1b971a65e 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -312,21 +312,13 @@ bool types::Type::isConstQualifiedValue() const { } bool types::Type::isTypeContainsPointer() const { - for (const auto &kind : pointerArrayKinds()) { - if (kind->getKind() == AbstractType::OBJECT_POINTER) { - return true; - } - } - return false; + return std::any_of(pointerArrayKinds().cbegin(), pointerArrayKinds().cend(), + [](auto const &kind){ return kind->getKind() == AbstractType::OBJECT_POINTER; }); } bool types::Type::isTypeContainsFunctionPointer() const { - for (const auto &kind : mKinds) { - if (kind->getKind() == AbstractType::FUNCTION_POINTER) { - return true; - } - } - return false; + return std::any_of(pointerArrayKinds().cbegin(), pointerArrayKinds().cend(), + [](auto const &kind){ return kind->getKind() == AbstractType::FUNCTION_POINTER; }); } int types::Type::indexOfFirstPointerInTypeKinds() const { @@ -665,9 +657,9 @@ std::string types::TypesHandler::removeArrayBrackets(TypeName type) { return type; } -std::unordered_map types::TypesHandler::integerTypesToSizes() noexcept { - static std::unordered_map integerTypes = { - {"utbot_byte", sizeof(char)}, //we use different name to not trigger char processing +const std::unordered_map &types::TypesHandler::integerTypesToSizes() noexcept { + static const std::unordered_map integerTypes = { + {"utbot_byte", sizeof(char)}, // we use different name to not trigger char processing {"short", sizeof(short)}, {"int", sizeof(int)}, {"long", sizeof(long)}, @@ -676,7 +668,7 @@ std::unordered_map types::TypesHandler::integerTypesToSizes {"unsigned int", sizeof(unsigned int)}, {"unsigned long", sizeof(unsigned long)}, {"unsigned long long", sizeof(unsigned long long)}, - {"unsigned char", sizeof(unsigned char)} // we do not want to treat an unsigned char as character literal + {"unsigned char", sizeof(unsigned char)} // we do not want to treat an unsigned char as character literal }; return integerTypes; } @@ -702,8 +694,8 @@ testsgen::ValidationType types::TypesHandler::getIntegerValidationType(const Typ } } -std::unordered_map types::TypesHandler::floatingPointTypesToSizes() noexcept { - static std::unordered_map floatingPointTypes = { +const std::unordered_map &types::TypesHandler::floatingPointTypesToSizes() noexcept { + static const std::unordered_map floatingPointTypes = { {"float", sizeof(float)}, {"double", sizeof(double)}, {"long double", sizeof(long double)} @@ -712,8 +704,8 @@ std::unordered_map types::TypesHandler::floatingPointTypesT return floatingPointTypes; } -std::unordered_map types::TypesHandler::characterTypesToSizes() noexcept { - static std::unordered_map characterTypes = { +const std::unordered_map &types::TypesHandler::characterTypesToSizes() noexcept { + static const std::unordered_map characterTypes = { {"char", sizeof(char)}, {"signed char", sizeof(signed char)}, }; @@ -721,8 +713,8 @@ std::unordered_map types::TypesHandler::characterTypesT return characterTypes; } -std::unordered_map types::TypesHandler::boolTypesToSizes() noexcept { - static std::unordered_map boolTypes = { +const std::unordered_map &types::TypesHandler::boolTypesToSizes() noexcept { + static const std::unordered_map boolTypes = { {"bool", sizeof(bool)}, {"_Bool", sizeof(bool)} }; @@ -900,7 +892,7 @@ types::TypesHandler::isSupportedType(const Type &type, TypeUsage usage, int dept } if (isStruct(type)) { auto structInfo = getStructInfo(type); - return !structInfo.hasUnnamedFields && unsupportedFields(structInfo.fields); + return !structInfo.hasAnonymousStructOrUnion && unsupportedFields(structInfo.fields); } return false; } }, @@ -956,7 +948,7 @@ types::TypesHandler::isSupportedType(const Type &type, TypeUsage usage, int dept }; if (isStruct(type)) { auto structInfo = getStructInfo(type); - return !structInfo.hasUnnamedFields && unsupportedFields(structInfo.fields); + return !structInfo.hasAnonymousStructOrUnion && unsupportedFields(structInfo.fields); } if (isUnion(type)) { diff --git a/server/src/types/Types.h b/server/src/types/Types.h index e52499822..d8c009783 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -281,19 +281,16 @@ namespace types { typedef std::unordered_map> FPointerMap; - struct StructInfo: TypeInfo { + struct UnionInfo: TypeInfo { std::vector fields{}; + bool hasAnonymousStructOrUnion; + }; + struct StructInfo: UnionInfo { FPointerMap functionFields{}; - bool hasUnnamedFields; bool isCLike; }; - struct UnionInfo: TypeInfo { - std::vector fields{}; - bool hasUnnamedFields; - }; - struct EnumInfo: TypeInfo { struct EnumEntry { std::string name; @@ -640,10 +637,10 @@ namespace types { IsSupportedTypeArgumentsHash> isSupportedTypeHash{}; - static std::unordered_map integerTypesToSizes() noexcept; - static std::unordered_map floatingPointTypesToSizes() noexcept; - static std::unordered_map characterTypesToSizes() noexcept; - static std::unordered_map boolTypesToSizes() noexcept; + static const std::unordered_map &integerTypesToSizes() noexcept; + static const std::unordered_map &floatingPointTypesToSizes() noexcept; + static const std::unordered_map &characterTypesToSizes() noexcept; + static const std::unordered_map &boolTypesToSizes() noexcept; template bool typeIsInMap(uint64_t id, const std::unordered_map& someMap) const { diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index 8debe6f07..c1768fc33 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -66,7 +66,7 @@ std::string TypesResolver::getFullname(const clang::TagDecl *TD, const clang::Qu fullname.insert(std::make_pair(id, currentStructName)); if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::C) { - if (const clang::RecordDecl *parentNode = llvm::dyn_cast(TD->getLexicalParent())) { + if (const auto *parentNode = llvm::dyn_cast(TD->getLexicalParent())) { clang::QualType parentCanonicalType = parentNode->getASTContext().getTypeDeclType(parentNode).getCanonicalType(); uint64_t parentID = types::Type::getIdFromCanonicalType(parentCanonicalType); if (!fullname[parentID].empty()) { @@ -93,9 +93,9 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string fs::path sourceFilePath = sourceManager.getFileEntryForID(sourceManager.getMainFileID())->tryGetRealPathName().str(); structInfo.filePath = Paths::getCCJsonFileFullPath(filename, parent->buildRootPath); structInfo.name = getFullname(D, canonicalType, id, sourceFilePath); - structInfo.hasUnnamedFields = false; + structInfo.hasAnonymousStructOrUnion = false; if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::CXX) { - const clang::CXXRecordDecl *cppD = dynamic_cast(D); + const auto *cppD = dynamic_cast(D); structInfo.isCLike = cppD != nullptr && cppD->isCLike(); } else { @@ -142,7 +142,7 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string if (LogUtils::isMaxVerbosity()) { ss << "\n\t" << field.type.typeName() << " " << field.name << ";"; } - structInfo.hasUnnamedFields |= F->isAnonymousStructOrUnion(); + structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::CXX) { switch (F->getAccess()) { case clang::AccessSpecifier::AS_private : @@ -267,7 +267,7 @@ void TypesResolver::resolveUnion(const clang::RecordDecl *D, const std::string & ss << "Union: " << unionInfo.name << "\n" << "\tFile path: " << unionInfo.filePath.string() << ""; std::vector fields; - unionInfo.hasUnnamedFields = false; + unionInfo.hasAnonymousStructOrUnion = false; for (const clang::FieldDecl *F : D->fields()) { if (F->isUnnamedBitfield()) { continue; @@ -281,7 +281,7 @@ void TypesResolver::resolveUnion(const clang::RecordDecl *D, const std::string & if (LogUtils::isMaxVerbosity()) { ss << "\n\t" << field.type.typeName() << " " << field.name << ";"; } - unionInfo.hasUnnamedFields |= F->isAnonymousStructOrUnion(); + unionInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); fields.push_back(field); } unionInfo.fields = fields; From 54946684f5d0c50f625b857d6bf71642eac83f83 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Tue, 16 Aug 2022 16:24:21 +0300 Subject: [PATCH 05/19] bit fields basic support added --- server/src/Tests.cpp | 42 +-- server/src/Tests.h | 71 ++++- server/src/types/Types.cpp | 41 +-- server/src/types/Types.h | 10 +- server/src/types/TypesResolver.cpp | 20 +- server/test/framework/Utils_Tests.cpp | 356 ++++++++++++++++++++++++++ 6 files changed, 472 insertions(+), 68 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 43c65b2e8..4560ccb47 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -7,6 +7,8 @@ #include "loguru.h" +#include + using namespace tests; using namespace types; @@ -114,7 +116,7 @@ std::shared_ptr KTestObjectParser::enumView(const std::vector KTestObjectParser::unionView(const std::vector &byteArray, types::UnionInfo &unionInfo, - unsigned int offset, + size_t offset, PointerUsage usage) { auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte"); auto view = arrayView(byteArray, bytesType, unionInfo.size, offset, usage); @@ -228,7 +230,7 @@ std::shared_ptr KTestObjectParser::functionPointerView(cons std::shared_ptr KTestObjectParser::arrayView(const std::vector &byteArray, const types::Type &type, size_t arraySize, - unsigned int offset, + size_t offset, PointerUsage usage) { types::StructInfo structInfo; types::EnumInfo enumInfo; @@ -274,7 +276,7 @@ std::shared_ptr KTestObjectParser::arrayView(const std::vector KTestObjectParser::structView(const std::vector &byteArray, types::StructInfo &curStruct, - unsigned int offset, + size_t offset, types::PointerUsage usage) { std::vector tmpInitReferences; return structView(byteArray, curStruct, offset, usage, {}, "", {}, tmpInitReferences); @@ -282,7 +284,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector std::shared_ptr KTestObjectParser::structView(const std::vector &byteArray, StructInfo &curStruct, - unsigned int offset, + size_t offset, PointerUsage usage, const std::optional &testingMethod, const std::string &name, @@ -290,12 +292,12 @@ std::shared_ptr KTestObjectParser::structView(const std::vector std::vector &initReferences) { std::vector> subViews; std::vector fields; - unsigned int curPos = offset; + size_t structOffset = offset; for (const auto &field: curStruct.fields) { fields.push_back(field.name); - size_t len = typesHandler.typeSize(field.type); - unsigned int offsetField = field.offset; + size_t fieldLen = typesHandler.typeSize(field.type); + size_t fieldOffset = structOffset + field.offset; types::EnumInfo innerEnum; types::UnionInfo innerUnion; types::StructInfo innerStruct; @@ -303,20 +305,22 @@ std::shared_ptr KTestObjectParser::structView(const std::vector switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), curPos + offsetField, len)); + // tdm_todo unnamed bitfield + subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), fieldOffset, + std::min(field.size, fieldLen))); break; case TypeKind::STRUCT: innerStruct = typesHandler.getStructInfo(field.type); - subViews.push_back(structView(byteArray, innerStruct, curPos + offsetField, usage, testingMethod, + subViews.push_back(structView(byteArray, innerStruct, fieldOffset, usage, testingMethod, PrinterUtils::getFieldAccess(name, field.name), fromAddressToName, initReferences)); break; case TypeKind::ENUM: innerEnum = typesHandler.getEnumInfo(field.type); - subViews.push_back(enumView(byteArray, innerEnum, curPos + offsetField, len)); + subViews.push_back(enumView(byteArray, innerEnum, fieldOffset, fieldLen)); break; case TypeKind::UNION: innerUnion = typesHandler.getUnionInfo(field.type); - subViews.push_back(unionView(byteArray, innerUnion, curPos + offsetField, usage)); + subViews.push_back(unionView(byteArray, innerUnion, fieldOffset, usage)); break; case TypeKind::ARRAY: if (field.type.pointerArrayKinds().size() > 1) { @@ -333,19 +337,19 @@ std::shared_ptr KTestObjectParser::structView(const std::vector if (onlyArrays) { size *= typesHandler.typeSize(field.type.baseTypeObj()); subViews.push_back(multiArrayView(byteArray, field.type, size, - curPos + offsetField, usage)); + fieldOffset, usage)); } else { std::vector> nullViews( size, std::make_shared(PrinterUtils::C_NULL)); subViews.push_back(std::make_shared(nullViews)); } } else { - auto view = arrayView(byteArray, field.type.baseTypeObj(), len, curPos + offsetField, usage); + auto view = arrayView(byteArray, field.type.baseTypeObj(), fieldLen, fieldOffset, usage); subViews.push_back(view); } break; case TypeKind::OBJECT_POINTER: - res = readBytesAsValueForType(byteArray, PointerWidthType, curPos + offsetField, + res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset, PointerWidthSize); subViews.push_back(getLazyPointerView(fromAddressToName, initReferences, PrinterUtils::getFieldAccess(name, field.name), res, field.type)); @@ -367,7 +371,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector } std::optional entryValue; - if(curStruct.hasAnonymousStructOrUnion) { + if (curStruct.hasAnonymousStructOrUnion) { auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte"); const std::shared_ptr rawDataView = arrayView(byteArray, bytesType, curStruct.size, offset, usage); entryValue = PrinterUtils::convertBytesToUnion(curStruct.name, rawDataView->getEntryValue(nullptr)); @@ -391,8 +395,8 @@ std::string KTestObjectParser::primitiveBoolView(const std::string &value) { std::string readBytesAsValueForType(const std::vector &byteArray, const std::string &typeName, - unsigned int offset, - unsigned int len) { + size_t offset, + size_t len) { if (typeName == "utbot_byte") { //we use different name to not trigger char processing return readBytesAsValue(byteArray, offset, len); @@ -1000,7 +1004,7 @@ std::shared_ptr KTestObjectParser::testParameterView( const auto ¶mType = param.type; switch (typesHandler.getTypeKind(paramType)) { case TypeKind::PRIMITIVE: - return primitiveView(rawData, paramType.baseTypeObj(), 0, rawData.size()); + return primitiveView(rawData, paramType.baseTypeObj(), 0, rawData.size() * CHAR_BIT); case TypeKind::STRUCT: structInfo = typesHandler.getStructInfo(paramType); name = param.varName; @@ -1062,7 +1066,7 @@ KTestObjectParser::getLazyPointerView(const MapAddressName &fromAddressToName, std::vector> KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, const types::UnionInfo &info, - unsigned int offset, + size_t offset, types::PointerUsage usage) { std::vector> subViews; for (const auto &field : info.fields) { diff --git a/server/src/Tests.h b/server/src/Tests.h index 42db4d6fb..363738ded 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -13,9 +13,13 @@ #include #include +#include +#include +#include #include #include #include +#include #include #include #include @@ -661,7 +665,7 @@ namespace tests { std::shared_ptr arrayView(const std::vector &byteArray, const types::Type &type, size_t arraySize, - unsigned int offset, + size_t offset, types::PointerUsage usage); static std::shared_ptr stringLiteralView(const std::vector &byteArray, @@ -676,17 +680,17 @@ namespace tests { std::shared_ptr unionView(const std::vector &byteArray, types::UnionInfo &unionInfo, - unsigned int offset, + size_t offset, types::PointerUsage usage); std::shared_ptr structView(const std::vector &byteArray, types::StructInfo &curStruct, - unsigned int offset, + size_t offset, types::PointerUsage usage); std::shared_ptr structView(const std::vector &byteArray, types::StructInfo &curStruct, - unsigned int offset, + size_t offset, types::PointerUsage usage, const std::optional &testingMethod, const std::string &name, @@ -708,7 +712,7 @@ namespace tests { constexpr static const char *const KLEE_PATH_FLAG = "kleePathFlag"; const std::string PointerWidthType = "unsigned long long"; - const size_t PointerWidthSize = 8; + const size_t PointerWidthSize = 8 * CHAR_BIT; //tdm_todo remove hardcoded constant constexpr static const char *const KLEE_PATH_FLAG_SYMBOLIC = "kleePathFlagSymbolic"; static std::vector::const_iterator @@ -722,7 +726,7 @@ namespace tests { const std::stringstream &traceStream); std::vector> collectUnionSubViews(const std::vector &byteArray, const types::UnionInfo &info, - unsigned int offset, + size_t offset, types::PointerUsage usage); void processGlobalParamPreValue(Tests::TestCaseDescription &testCaseDescription, const Tests::MethodParam &globalParam, @@ -792,6 +796,18 @@ namespace tests { ss << value; return ss.str(); } + + template + void sext(T* bytes, size_t len, size_t signPos) { + int bit = (bytes[signPos / CHAR_BIT] >> (signPos % CHAR_BIT)) & 1; + if (bit) { + T mask = static_cast((1 << CHAR_BIT) - 1); + bytes[signPos / CHAR_BIT] |= mask ^ ((1 << (signPos % CHAR_BIT)) - 1); + for (size_t i = signPos / CHAR_BIT + 1; i < len; ++i) { + bytes[i] = mask; + } + } + } /** * This function is used for converting sequence of bytes to specific type. * Returns string representation of a value recorded in byteArray. @@ -803,11 +819,42 @@ namespace tests { */ template std::string readBytesAsValue(const std::vector &byteArray, size_t offset, size_t len) { - char bytes[len]; - for (int j = 0; j < len; j++) { - bytes[j] = byteArray[offset + j]; + char bytes[sizeof(T)] = {}; + if (offset % CHAR_BIT != 0 || len % CHAR_BIT != 0) { + // tdm_todo unnamed bitfield // if (len == 0) + // WARNING: assuming little endian and two's complement + size_t lo = offset % CHAR_BIT; + size_t hi = CHAR_BIT - lo; + size_t stop = (offset + len + CHAR_BIT - 1) / CHAR_BIT; + for (size_t i = offset / CHAR_BIT, j = 0; i < stop; ++i, ++j) { + std::cout << "i = " << i << ", j = " << j << ", byte = " << (unsigned)byteArray[i] << std::endl; + auto byte = static_cast(byteArray[i]); + if (i + 1 == stop) { + size_t top = (offset + len) % CHAR_BIT; + if (top == 0) { + top = CHAR_BIT; + } + byte &= ((1 << top) - 1); + } + unsigned char low = byte & ((1 << lo) - 1); + unsigned char high = byte >> lo; + if (j > 0) { + bytes[j - 1] |= low << hi; + } + if (j < sizeof(T)) { + bytes[j] = high; + } + std::cout << "low = " << (unsigned)low << ", high = " << (unsigned)high << std::endl; + } + if constexpr(std::is_signed_v) { + sext(bytes, sizeof(T), len - 1); + } + } else { + for (size_t j = 0; j < len / CHAR_BIT; j++) { + bytes[j] = byteArray[offset / CHAR_BIT + j]; + } } - T *pTypeValue = (T *)bytes; + T *pTypeValue = (T *) bytes; T pValue = *pTypeValue; return primitiveValueToString(pValue); } @@ -822,7 +869,7 @@ namespace tests { */ std::string readBytesAsValueForType(const std::vector &byteArray, const std::string &typeName, - unsigned int offset, - unsigned int len); + size_t offset, + size_t len); } #endif // UNITTESTBOT_TESTS_H diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 1b971a65e..5cf5ec676 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -8,6 +8,9 @@ #include "utils/PrinterUtils.h" #include "loguru.h" + +#include + /* * class Type */ @@ -591,7 +594,7 @@ size_t types::TypesHandler::typeSize(const types::Type &type) const { } if (isObjectPointerType(type)) { - return getPointerSize(); + return getPointerSize() * CHAR_BIT; } if (isEnum(type)) { @@ -603,7 +606,7 @@ size_t types::TypesHandler::typeSize(const types::Type &type) const { } if (isPointerToFunction(type)) { - return sizeof(char *); + return sizeof(char *) * CHAR_BIT; } throw UnImplementedException("Type is unknown for: " + type.typeName()); @@ -659,16 +662,16 @@ std::string types::TypesHandler::removeArrayBrackets(TypeName type) { const std::unordered_map &types::TypesHandler::integerTypesToSizes() noexcept { static const std::unordered_map integerTypes = { - {"utbot_byte", sizeof(char)}, // we use different name to not trigger char processing - {"short", sizeof(short)}, - {"int", sizeof(int)}, - {"long", sizeof(long)}, - {"long long", sizeof(long long)}, - {"unsigned short", sizeof(unsigned short)}, - {"unsigned int", sizeof(unsigned int)}, - {"unsigned long", sizeof(unsigned long)}, - {"unsigned long long", sizeof(unsigned long long)}, - {"unsigned char", sizeof(unsigned char)} // we do not want to treat an unsigned char as character literal + {"utbot_byte", sizeof(char) * CHAR_BIT}, // we use different name to not trigger char processing + {"short", sizeof(short) * CHAR_BIT}, + {"int", sizeof(int) * CHAR_BIT}, + {"long", sizeof(long) * CHAR_BIT}, + {"long long", sizeof(long long) * CHAR_BIT}, + {"unsigned short", sizeof(unsigned short) * CHAR_BIT}, + {"unsigned int", sizeof(unsigned int) * CHAR_BIT}, + {"unsigned long", sizeof(unsigned long) * CHAR_BIT}, + {"unsigned long long", sizeof(unsigned long long) * CHAR_BIT}, + {"unsigned char", sizeof(unsigned char) * CHAR_BIT} // we do not want to treat an unsigned char as character literal }; return integerTypes; } @@ -696,9 +699,9 @@ testsgen::ValidationType types::TypesHandler::getIntegerValidationType(const Typ const std::unordered_map &types::TypesHandler::floatingPointTypesToSizes() noexcept { static const std::unordered_map floatingPointTypes = { - {"float", sizeof(float)}, - {"double", sizeof(double)}, - {"long double", sizeof(long double)} + {"float", sizeof(float) * CHAR_BIT}, + {"double", sizeof(double) * CHAR_BIT}, + {"long double", sizeof(long double) * CHAR_BIT} }; return floatingPointTypes; @@ -706,8 +709,8 @@ const std::unordered_map &types::TypesHandler::floatingPoin const std::unordered_map &types::TypesHandler::characterTypesToSizes() noexcept { static const std::unordered_map characterTypes = { - {"char", sizeof(char)}, - {"signed char", sizeof(signed char)}, + {"char", sizeof(char) * CHAR_BIT}, + {"signed char", sizeof(signed char) * CHAR_BIT}, }; return characterTypes; @@ -715,8 +718,8 @@ const std::unordered_map &types::TypesHandler::characte const std::unordered_map &types::TypesHandler::boolTypesToSizes() noexcept { static const std::unordered_map boolTypes = { - {"bool", sizeof(bool)}, - {"_Bool", sizeof(bool)} + {"bool", sizeof(bool) * CHAR_BIT}, + {"_Bool", sizeof(bool) * CHAR_BIT} }; return boolTypes; diff --git a/server/src/types/Types.h b/server/src/types/Types.h index d8c009783..bf3e0dbc1 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -259,9 +259,9 @@ namespace types { struct Field { types::Type type; std::string name; - size_t size; + size_t size; // size in bits // reassigned in structFields - size_t offset = 0; + size_t offset = 0; // offset in bits enum AccessSpecifier { AS_pubic, AS_protected, @@ -275,8 +275,8 @@ namespace types { fs::path filePath; std::string name; std::string definition; - size_t size; - size_t alignment; + size_t size; // size in bits + size_t alignment; // alignment in **bytes** }; typedef std::unordered_map> FPointerMap; @@ -340,7 +340,7 @@ namespace types { /** * This functions calculates size of a given type. For structs in it calculates sum of sizes of its fields, * ignoring alignment. - * @return size of given type. + * @return size of given type in bits. */ size_t typeSize(const types::Type &type) const; diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index c1768fc33..64ffdead9 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -33,7 +33,7 @@ bool isCandidateToReplace(uint64_t id, } static size_t getRecordSize(const clang::RecordDecl *D) { - return D->getASTContext().getTypeSize(D->getASTContext().getRecordType(D)) / 8; + return D->getASTContext().getTypeSize(D->getASTContext().getRecordType(D)); } static size_t getDeclAlignment(const clang::TagDecl *T) { @@ -112,9 +112,7 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string << "\tFile path: " << structInfo.filePath.string() << ""; std::vector fields; for (const clang::FieldDecl *F : D->fields()) { - if (F->isUnnamedBitfield()) { - continue; - } + structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; field.name = F->getNameAsString(); const clang::QualType paramType = F->getType().getCanonicalType(); @@ -137,12 +135,11 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string F->getType()->getPointeeType()->getPointeeType()->getAs(), field.name, sourceManager, field.type.isArrayOfPointersToFunction()); } - field.size = context.getTypeSize(F->getType()) / 8; - field.offset = context.getFieldOffset(F) / 8; + field.size = F->isBitField() ? F->getBitWidthValue(context) : context.getTypeSize(F->getType()); + field.offset = context.getFieldOffset(F); if (LogUtils::isMaxVerbosity()) { ss << "\n\t" << field.type.typeName() << " " << field.name << ";"; } - structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::CXX) { switch (F->getAccess()) { case clang::AccessSpecifier::AS_private : @@ -269,19 +266,16 @@ void TypesResolver::resolveUnion(const clang::RecordDecl *D, const std::string & std::vector fields; unionInfo.hasAnonymousStructOrUnion = false; for (const clang::FieldDecl *F : D->fields()) { - if (F->isUnnamedBitfield()) { - continue; - } + unionInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; - std::string fieldName = F->getNameAsString(); - field.name = fieldName; + field.name = F->getNameAsString(); const clang::QualType paramType = F->getType().getCanonicalType(); field.type = types::Type(paramType, paramType.getAsString(), sourceManager); // TODO: add flag in logger that prevents line ending if (LogUtils::isMaxVerbosity()) { ss << "\n\t" << field.type.typeName() << " " << field.name << ";"; } - unionInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); + field.size = F->isBitField() ? F->getBitWidthValue(context) : context.getTypeSize(F->getType()); fields.push_back(field); } unionInfo.fields = fields; diff --git a/server/test/framework/Utils_Tests.cpp b/server/test/framework/Utils_Tests.cpp index b176a0f4d..313ab95aa 100644 --- a/server/test/framework/Utils_Tests.cpp +++ b/server/test/framework/Utils_Tests.cpp @@ -7,10 +7,366 @@ #include "utils/StringUtils.h" #include +#include +#include +#include +#include namespace { auto projectPath = fs::current_path().parent_path() / testUtils::getRelativeTestSuitePath("server"); + TEST(readBytesAsValue, unsigned1) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[0] ^= 1 << 5; + size_t offset = 3; + size_t len = 15; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8196"); + } + + TEST(readBytesAsValue, unsigned2) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[0] ^= 1 << 5; + size_t offset = 3; + size_t len = 14; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8196"); + } + + TEST(readBytesAsValue, signed1) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[0] ^= 1 << 5; + size_t offset = 3; + size_t len = 14; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-8188"); + } + + TEST(readBytesAsValue, signed2) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + size_t offset = 3; + size_t len = 14; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-7676"); + } + + TEST(readBytesAsValue, signed3) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + size_t offset = 0; + size_t len = 14; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "4128"); + } + + TEST(readBytesAsValue, signed4) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + size_t offset = 4; + size_t len = 3; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2"); + } + + TEST(readBytesAsValue, signed5) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + size_t offset = 4; + size_t len = 2; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-2"); + } + + TEST(readBytesAsValue, signed6) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + size_t offset = 4; + size_t len = 1; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "0"); + } + + TEST(readBytesAsValue, signed7) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 2; + size_t len = 2; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-2"); + } + + TEST(readBytesAsValue, signed8) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 2; + size_t len = 6; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "10"); + } + + TEST(readBytesAsValue, signed9) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 1; + size_t len = 15; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2068"); + } + + TEST(readBytesAsValue, unsigned3) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 1; + size_t len = 15; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2068"); + } + + TEST(readBytesAsValue, unsigned4) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 3; + size_t len = 15; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8709"); + } + + TEST(readBytesAsValue, unsigned5) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 3; + size_t len = 14; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8709"); + } + + TEST(readBytesAsValue, unsigned6) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 3; + size_t len = 16; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); + } + + TEST(readBytesAsValue, unsigned7) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 3; + size_t len = 17; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); + } + + TEST(readBytesAsValue, unsigned8) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 3; + size_t len = 26; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); + } + + TEST(readBytesAsValue, unsigned9) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 3; + size_t len = 26; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); + } + + TEST(readBytesAsValue, unsigned10) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 15; + size_t len = 2; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2"); + } + + TEST(readBytesAsValue, bool1) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 15; + size_t len = 1; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "0"); + } + + TEST(readBytesAsValue, bool2) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 16; + size_t len = 1; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "1"); + } + + TEST(readBytesAsValue, bool3) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[2] ^= 1 << 2; + bytes[2] ^= 1 << 0; + bytes[1] ^= 1 << 4; + bytes[0] ^= 1 << 5; + bytes[0] ^= 1 << 3; + size_t offset = 12; + size_t len = 1; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "1"); + } + + template + void readBytesAsValue_test_template(T val) { + srand(42); + for (size_t tcount = 0; tcount < 5; ++tcount) { +// std::cout << "\ttest #" << tcount << ", val = " << val; + auto add = static_cast(rand() % 10); + auto start = static_cast(rand() % add); +// std::cout << "additional bytes: " << add << ", start position: " << start << std::endl; + size_t const len = sizeof(T); + std::vector bytes(len + add); + for (size_t i = 1; i <= len; ++i) { + bytes[start + i - 1] = (val & ((1LL << (CHAR_BIT * i)) - 1)) >> (CHAR_BIT * (i - 1)); +// std::cout << (unsigned)bytes[start + i - 1] << " "; + } +// std::cout << std::endl; +// for (char c : bytes) { +// std::cout << (unsigned)c << " "; +// } +// std::cout << std::endl; + EXPECT_EQ(tests::readBytesAsValue(bytes, start * CHAR_BIT, len * CHAR_BIT), std::to_string(val)); + } + } + + TEST(readBytesAsValue, common_int) { + readBytesAsValue_test_template(13); + readBytesAsValue_test_template(26); + readBytesAsValue_test_template(42); + readBytesAsValue_test_template(0); + readBytesAsValue_test_template(1); + readBytesAsValue_test_template(-13); + readBytesAsValue_test_template(-26); + readBytesAsValue_test_template(-42); + readBytesAsValue_test_template(-1); + readBytesAsValue_test_template(std::numeric_limits::max()); + readBytesAsValue_test_template(std::numeric_limits::min()); + } + + TEST(readBytesAsValue, common_uint) { + readBytesAsValue_test_template(13); + readBytesAsValue_test_template(26); + readBytesAsValue_test_template(42); + readBytesAsValue_test_template(0); + readBytesAsValue_test_template(1); + readBytesAsValue_test_template(std::numeric_limits::min()); + readBytesAsValue_test_template(std::numeric_limits::max()); + } + + TEST(readBytesAsValue, common_char) { + readBytesAsValue_test_template(13); + readBytesAsValue_test_template(26); + readBytesAsValue_test_template(42); + readBytesAsValue_test_template(0); + readBytesAsValue_test_template(1); + readBytesAsValue_test_template(-13); + readBytesAsValue_test_template(-26); + readBytesAsValue_test_template(-42); + readBytesAsValue_test_template(-1); + readBytesAsValue_test_template(std::numeric_limits::min()); + readBytesAsValue_test_template(std::numeric_limits::max()); + } + + TEST(readBytesAsValue, common_short) { + readBytesAsValue_test_template(13); + readBytesAsValue_test_template(26); + readBytesAsValue_test_template(42); + readBytesAsValue_test_template(0); + readBytesAsValue_test_template(1); + readBytesAsValue_test_template(-13); + readBytesAsValue_test_template(-26); + readBytesAsValue_test_template(-42); + readBytesAsValue_test_template(-1); + readBytesAsValue_test_template(std::numeric_limits::min()); + readBytesAsValue_test_template(std::numeric_limits::max()); + } + + TEST(readBytesAsValue, common_ushort) { + readBytesAsValue_test_template(13); + readBytesAsValue_test_template(26); + readBytesAsValue_test_template(42); + readBytesAsValue_test_template(0); + readBytesAsValue_test_template(1); + readBytesAsValue_test_template(-13); + readBytesAsValue_test_template(-26); + readBytesAsValue_test_template(-42); + readBytesAsValue_test_template(-1); + readBytesAsValue_test_template(std::numeric_limits::min()); + readBytesAsValue_test_template(std::numeric_limits::max()); + } + TEST(Utils_Test, Split) { std::string s = "a,b,c,d,"; std::vector vs = StringUtils::split(s, ','); From cbe372f125a8c688f502cf5e19d17d6e908e764a Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Tue, 16 Aug 2022 17:32:57 +0300 Subject: [PATCH 06/19] refactoring fix --- server/src/Tests.cpp | 21 ++++++++++++--------- server/src/types/Types.cpp | 27 ++++++++++++++------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 4560ccb47..d598ab30c 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -322,13 +322,14 @@ std::shared_ptr KTestObjectParser::structView(const std::vector innerUnion = typesHandler.getUnionInfo(field.type); subViews.push_back(unionView(byteArray, innerUnion, fieldOffset, usage)); break; - case TypeKind::ARRAY: - if (field.type.pointerArrayKinds().size() > 1) { + case TypeKind::ARRAY: { + const std::vector> pointerArrayKinds = field.type.pointerArrayKinds(); + if (pointerArrayKinds.size() > 1) { size_t size = 1; bool onlyArrays = true; - for (size_t i = 0; i < field.type.pointerArrayKinds().size(); i++) { - if (field.type.pointerArrayKinds()[i]->getKind() == AbstractType::ARRAY) { - size *= field.type.pointerArrayKinds()[i]->getSize(); + for (size_t i = 0; i < pointerArrayKinds.size(); i++) { + if (pointerArrayKinds[i]->getKind() == AbstractType::ARRAY) { + size *= pointerArrayKinds[i]->getSize(); } else { onlyArrays = false; break; @@ -347,6 +348,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector auto view = arrayView(byteArray, field.type.baseTypeObj(), fieldLen, fieldOffset, usage); subViews.push_back(view); } + } break; case TypeKind::OBJECT_POINTER: res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset, @@ -1070,13 +1072,14 @@ KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, types::PointerUsage usage) { std::vector> subViews; for (const auto &field : info.fields) { - size_t len = typesHandler.typeSize(field.type); + size_t fieldLen = typesHandler.typeSize(field.type); types::EnumInfo innerEnum; types::UnionInfo innerUnion; types::StructInfo innerStruct; switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offset, len)); + subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offset, + std::min(field.size, fieldLen))); break; case TypeKind::STRUCT: innerStruct = typesHandler.getStructInfo(field.type); @@ -1084,14 +1087,14 @@ KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, break; case TypeKind::ENUM: innerEnum = typesHandler.getEnumInfo(field.type); - subViews.push_back(enumView(byteArray, innerEnum, offset, len)); + subViews.push_back(enumView(byteArray, innerEnum, offset, fieldLen)); break; case TypeKind::UNION: innerUnion = typesHandler.getUnionInfo(field.type); subViews.push_back(unionView(byteArray, innerUnion, offset, usage)); break; case TypeKind::ARRAY: - subViews.push_back(arrayView(byteArray, field.type.baseTypeObj(), len, offset, usage)); + subViews.push_back(arrayView(byteArray, field.type.baseTypeObj(), fieldLen, offset, usage)); break; case TypeKind::OBJECT_POINTER: subViews.push_back(std::make_shared(PrinterUtils::C_NULL)); diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 5cf5ec676..57d1912d4 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -149,11 +149,9 @@ bool types::Type::maybeReturnArray() const { size_t types::Type::countReturnPointers(bool decrementIfArray) const { size_t returnPointer = 0; - for (size_t i = 0; i < this->pointerArrayKinds().size(); ++i) { - returnPointer += - this->pointerArrayKinds()[i]->getKind() == AbstractType::OBJECT_POINTER - ? 1 - : 0; + const std::vector> pointerArrayKinds = this->pointerArrayKinds(); + for (size_t i = 0; i < pointerArrayKinds.size(); ++i) { + returnPointer += pointerArrayKinds[i]->getKind() == AbstractType::OBJECT_POINTER; } if (decrementIfArray && maybeReturnArray()) { returnPointer--; @@ -282,9 +280,10 @@ const std::string &types::Type::getStdinParamName() { } bool types::Type::isPointerToPointer() const { - return pointerArrayKinds().size() > 1 && - pointerArrayKinds()[0]->getKind() == AbstractType::OBJECT_POINTER && - pointerArrayKinds()[1]->getKind() == AbstractType::OBJECT_POINTER; + const std::vector> pointerArrayKinds = this->pointerArrayKinds(); + return pointerArrayKinds.size() > 1 && + pointerArrayKinds[0]->getKind() == AbstractType::OBJECT_POINTER && + pointerArrayKinds[1]->getKind() == AbstractType::OBJECT_POINTER; } @@ -293,9 +292,10 @@ bool types::Type::isTwoDimensionalPointer() const { } bool types::Type::isPointerToArray() const { - return pointerArrayKinds().size() > 1 && - pointerArrayKinds()[0]->getKind() == AbstractType::OBJECT_POINTER && - pointerArrayKinds()[1]->getKind() == AbstractType::ARRAY; + const std::vector> pointerArrayKinds = this->pointerArrayKinds(); + return pointerArrayKinds.size() > 1 && + pointerArrayKinds[0]->getKind() == AbstractType::OBJECT_POINTER && + pointerArrayKinds[1]->getKind() == AbstractType::ARRAY; } bool types::Type::isConstQualifiedValue() const { @@ -315,12 +315,13 @@ bool types::Type::isConstQualifiedValue() const { } bool types::Type::isTypeContainsPointer() const { - return std::any_of(pointerArrayKinds().cbegin(), pointerArrayKinds().cend(), + const std::vector> pointerArrayKinds = this->pointerArrayKinds(); + return std::any_of(pointerArrayKinds.cbegin(), pointerArrayKinds.cend(), [](auto const &kind){ return kind->getKind() == AbstractType::OBJECT_POINTER; }); } bool types::Type::isTypeContainsFunctionPointer() const { - return std::any_of(pointerArrayKinds().cbegin(), pointerArrayKinds().cend(), + return std::any_of(mKinds.cbegin(), mKinds.cend(), [](auto const &kind){ return kind->getKind() == AbstractType::FUNCTION_POINTER; }); } From 44e0b640a977672de6cbc9cf74b5c940b7e22e98 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Wed, 17 Aug 2022 11:10:20 +0300 Subject: [PATCH 07/19] bit fields parsing fix --- server/src/Tests.cpp | 4 +- server/src/Tests.h | 6 +- server/test/framework/Utils_Tests.cpp | 245 +++++++++++++++----------- 3 files changed, 151 insertions(+), 104 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index d598ab30c..1828d3c0b 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -1036,9 +1036,9 @@ std::shared_ptr KTestObjectParser::testParameterView( return unionView(rawData, unionInfo, 0, usage); case TypeKind::ARRAY: if (paramType.kinds().size() > 2) { - return multiArrayView(rawData, paramType, rawData.size(), 0, usage); + return multiArrayView(rawData, paramType, rawData.size() * CHAR_BIT, 0, usage); } else { - return arrayView(rawData, paramType.baseTypeObj(), rawData.size(), 0, usage); + return arrayView(rawData, paramType.baseTypeObj(), rawData.size() * CHAR_BIT, 0, usage); } case TypeKind::UNKNOWN: throw UnImplementedException("No such type"); diff --git a/server/src/Tests.h b/server/src/Tests.h index 363738ded..a1230d293 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -846,14 +846,14 @@ namespace tests { } std::cout << "low = " << (unsigned)low << ", high = " << (unsigned)high << std::endl; } - if constexpr(std::is_signed_v) { - sext(bytes, sizeof(T), len - 1); - } } else { for (size_t j = 0; j < len / CHAR_BIT; j++) { bytes[j] = byteArray[offset / CHAR_BIT + j]; } } + if constexpr(std::is_signed_v) { + sext(bytes, sizeof(T), len - 1); + } T *pTypeValue = (T *) bytes; T pValue = *pTypeValue; return primitiveValueToString(pValue); diff --git a/server/test/framework/Utils_Tests.cpp b/server/test/framework/Utils_Tests.cpp index 313ab95aa..38856027d 100644 --- a/server/test/framework/Utils_Tests.cpp +++ b/server/test/framework/Utils_Tests.cpp @@ -15,7 +15,7 @@ namespace { auto projectPath = fs::current_path().parent_path() / testUtils::getRelativeTestSuitePath("server"); - TEST(readBytesAsValue, unsigned1) { + TEST(ReadBytesAsValueTest, Unsigned1) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -25,7 +25,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8196"); } - TEST(readBytesAsValue, unsigned2) { + TEST(ReadBytesAsValueTest, Unsigned2) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -35,7 +35,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8196"); } - TEST(readBytesAsValue, signed1) { + TEST(ReadBytesAsValueTest, Signed1) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -45,7 +45,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-8188"); } - TEST(readBytesAsValue, signed2) { + TEST(ReadBytesAsValueTest, Signed2) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -56,7 +56,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-7676"); } - TEST(readBytesAsValue, signed3) { + TEST(ReadBytesAsValueTest, Signed3) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -67,7 +67,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "4128"); } - TEST(readBytesAsValue, signed4) { + TEST(ReadBytesAsValueTest, Signed4) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -78,7 +78,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2"); } - TEST(readBytesAsValue, signed5) { + TEST(ReadBytesAsValueTest, Signed5) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -89,7 +89,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-2"); } - TEST(readBytesAsValue, signed6) { + TEST(ReadBytesAsValueTest, Signed6) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -100,7 +100,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "0"); } - TEST(readBytesAsValue, signed7) { + TEST(ReadBytesAsValueTest, Signed7) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -112,7 +112,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-2"); } - TEST(readBytesAsValue, signed8) { + TEST(ReadBytesAsValueTest, Signed8) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -124,7 +124,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "10"); } - TEST(readBytesAsValue, signed9) { + TEST(ReadBytesAsValueTest, Signed9) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -136,7 +136,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2068"); } - TEST(readBytesAsValue, unsigned3) { + TEST(ReadBytesAsValueTest, Unsigned3) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 0; @@ -148,7 +148,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2068"); } - TEST(readBytesAsValue, unsigned4) { + TEST(ReadBytesAsValueTest, Unsigned4) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -161,7 +161,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8709"); } - TEST(readBytesAsValue, unsigned5) { + TEST(ReadBytesAsValueTest, Unsigned5) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -174,7 +174,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "8709"); } - TEST(readBytesAsValue, unsigned6) { + TEST(ReadBytesAsValueTest, Unsigned6) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -187,7 +187,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); } - TEST(readBytesAsValue, unsigned7) { + TEST(ReadBytesAsValueTest, Unsigned7) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -200,7 +200,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); } - TEST(readBytesAsValue, unsigned8) { + TEST(ReadBytesAsValueTest, Unsigned8) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -213,20 +213,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); } - TEST(readBytesAsValue, unsigned9) { - size_t const LEN = 4; - std::vector bytes(LEN); - bytes[2] ^= 1 << 2; - bytes[2] ^= 1 << 0; - bytes[1] ^= 1 << 4; - bytes[0] ^= 1 << 5; - bytes[0] ^= 1 << 3; - size_t offset = 3; - size_t len = 26; - EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "41477"); - } - - TEST(readBytesAsValue, unsigned10) { + TEST(ReadBytesAsValueTest, Unsigned9) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -239,7 +226,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "2"); } - TEST(readBytesAsValue, bool1) { + TEST(ReadBytesAsValueTest, Bool1) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -252,7 +239,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "0"); } - TEST(readBytesAsValue, bool2) { + TEST(ReadBytesAsValueTest, Bool2) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -265,7 +252,7 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "1"); } - TEST(readBytesAsValue, bool3) { + TEST(ReadBytesAsValueTest, Bool3) { size_t const LEN = 4; std::vector bytes(LEN); bytes[2] ^= 1 << 2; @@ -278,8 +265,58 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "1"); } + TEST(ReadBytesAsValueTest, ByteOffsetInt) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[3] = 1; + bytes[2] = bytes[1] = bytes[0] = -1; + size_t offset = 0; + size_t len = 24; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "-1"); + } + + TEST(ReadBytesAsValueTest, ByteOffsetInt2) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[3] = 1; + bytes[2] = bytes[1] = bytes[0] = -1; + size_t offset = 8; + size_t len = 24; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "131071"); + } + + TEST(ReadBytesAsValueTest, ByteOffsetUnsignedInt) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[3] = 1; + bytes[2] = bytes[1] = bytes[0] = -1; + size_t offset = 0; + size_t len = 24; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "16777215"); + } + + TEST(ReadBytesAsValueTest, ByteOffsetUnsignedInt2) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[3] = 1; + bytes[2] = bytes[1] = bytes[0] = -1; + size_t offset = 8; + size_t len = 24; + EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "131071"); + } + + TEST(ReadBytesAsValueTestDeathTest, InvalidLen) { + size_t const LEN = 4; + std::vector bytes(LEN); + bytes[3] = 1; + bytes[2] = bytes[1] = bytes[0] = -1; + size_t offset = 0; + size_t len = CHAR_BIT * 2; + EXPECT_DEATH(tests::readBytesAsValue(bytes, offset, len), ""); + } + template - void readBytesAsValue_test_template(T val) { + void readBytesAsValueTestTemplate(T val) { srand(42); for (size_t tcount = 0; tcount < 5; ++tcount) { // std::cout << "\ttest #" << tcount << ", val = " << val; @@ -301,70 +338,80 @@ namespace { } } - TEST(readBytesAsValue, common_int) { - readBytesAsValue_test_template(13); - readBytesAsValue_test_template(26); - readBytesAsValue_test_template(42); - readBytesAsValue_test_template(0); - readBytesAsValue_test_template(1); - readBytesAsValue_test_template(-13); - readBytesAsValue_test_template(-26); - readBytesAsValue_test_template(-42); - readBytesAsValue_test_template(-1); - readBytesAsValue_test_template(std::numeric_limits::max()); - readBytesAsValue_test_template(std::numeric_limits::min()); - } - - TEST(readBytesAsValue, common_uint) { - readBytesAsValue_test_template(13); - readBytesAsValue_test_template(26); - readBytesAsValue_test_template(42); - readBytesAsValue_test_template(0); - readBytesAsValue_test_template(1); - readBytesAsValue_test_template(std::numeric_limits::min()); - readBytesAsValue_test_template(std::numeric_limits::max()); - } - - TEST(readBytesAsValue, common_char) { - readBytesAsValue_test_template(13); - readBytesAsValue_test_template(26); - readBytesAsValue_test_template(42); - readBytesAsValue_test_template(0); - readBytesAsValue_test_template(1); - readBytesAsValue_test_template(-13); - readBytesAsValue_test_template(-26); - readBytesAsValue_test_template(-42); - readBytesAsValue_test_template(-1); - readBytesAsValue_test_template(std::numeric_limits::min()); - readBytesAsValue_test_template(std::numeric_limits::max()); - } - - TEST(readBytesAsValue, common_short) { - readBytesAsValue_test_template(13); - readBytesAsValue_test_template(26); - readBytesAsValue_test_template(42); - readBytesAsValue_test_template(0); - readBytesAsValue_test_template(1); - readBytesAsValue_test_template(-13); - readBytesAsValue_test_template(-26); - readBytesAsValue_test_template(-42); - readBytesAsValue_test_template(-1); - readBytesAsValue_test_template(std::numeric_limits::min()); - readBytesAsValue_test_template(std::numeric_limits::max()); - } - - TEST(readBytesAsValue, common_ushort) { - readBytesAsValue_test_template(13); - readBytesAsValue_test_template(26); - readBytesAsValue_test_template(42); - readBytesAsValue_test_template(0); - readBytesAsValue_test_template(1); - readBytesAsValue_test_template(-13); - readBytesAsValue_test_template(-26); - readBytesAsValue_test_template(-42); - readBytesAsValue_test_template(-1); - readBytesAsValue_test_template(std::numeric_limits::min()); - readBytesAsValue_test_template(std::numeric_limits::max()); + TEST(ReadBytesAsValueTest, CommonInt) { + readBytesAsValueTestTemplate(13); + readBytesAsValueTestTemplate(26); + readBytesAsValueTestTemplate(42); + readBytesAsValueTestTemplate(0); + readBytesAsValueTestTemplate(1); + readBytesAsValueTestTemplate(-13); + readBytesAsValueTestTemplate(-26); + readBytesAsValueTestTemplate(-42); + readBytesAsValueTestTemplate(-1); + readBytesAsValueTestTemplate(std::numeric_limits::max()); + readBytesAsValueTestTemplate(std::numeric_limits::min()); + } + + TEST(ReadBytesAsValueTest, CommonUnsignedInt) { + readBytesAsValueTestTemplate(13); + readBytesAsValueTestTemplate(26); + readBytesAsValueTestTemplate(42); + readBytesAsValueTestTemplate(0); + readBytesAsValueTestTemplate(1); + readBytesAsValueTestTemplate(std::numeric_limits::min()); + readBytesAsValueTestTemplate(std::numeric_limits::max()); + } + + TEST(ReadBytesAsValueTest, CommonChar) { + readBytesAsValueTestTemplate(13); + readBytesAsValueTestTemplate(26); + readBytesAsValueTestTemplate(42); + readBytesAsValueTestTemplate(0); + readBytesAsValueTestTemplate(1); + readBytesAsValueTestTemplate(-13); + readBytesAsValueTestTemplate(-26); + readBytesAsValueTestTemplate(-42); + readBytesAsValueTestTemplate(-1); + readBytesAsValueTestTemplate(std::numeric_limits::min()); + readBytesAsValueTestTemplate(std::numeric_limits::max()); + } + + TEST(ReadBytesAsValueTest, CommonUnsignedChar) { + readBytesAsValueTestTemplate(13); + readBytesAsValueTestTemplate(26); + readBytesAsValueTestTemplate(42); + readBytesAsValueTestTemplate(0); + readBytesAsValueTestTemplate(1); + readBytesAsValueTestTemplate(std::numeric_limits::min()); + readBytesAsValueTestTemplate(std::numeric_limits::max()); + } + + TEST(ReadBytesAsValueTest, CommonShort) { + readBytesAsValueTestTemplate(13); + readBytesAsValueTestTemplate(26); + readBytesAsValueTestTemplate(42); + readBytesAsValueTestTemplate(0); + readBytesAsValueTestTemplate(1); + readBytesAsValueTestTemplate(-13); + readBytesAsValueTestTemplate(-26); + readBytesAsValueTestTemplate(-42); + readBytesAsValueTestTemplate(-1); + readBytesAsValueTestTemplate(std::numeric_limits::min()); + readBytesAsValueTestTemplate(std::numeric_limits::max()); + } + + TEST(ReadBytesAsValueTest, CommonUnsignedShort) { + readBytesAsValueTestTemplate(13); + readBytesAsValueTestTemplate(26); + readBytesAsValueTestTemplate(42); + readBytesAsValueTestTemplate(0); + readBytesAsValueTestTemplate(1); + readBytesAsValueTestTemplate(-13); + readBytesAsValueTestTemplate(-26); + readBytesAsValueTestTemplate(-42); + readBytesAsValueTestTemplate(-1); + readBytesAsValueTestTemplate(std::numeric_limits::min()); + readBytesAsValueTestTemplate(std::numeric_limits::max()); } TEST(Utils_Test, Split) { From 858e8688e8d938cab42bf014d1b004d6b611e694 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Thu, 18 Aug 2022 14:25:46 +0300 Subject: [PATCH 08/19] huge variable renaming, bits/bytes correctness fix --- server/src/Tests.cpp | 241 +++++++++++++++++---------------- server/src/Tests.h | 116 +++++++++------- server/src/types/Types.cpp | 120 ++++++++-------- server/src/types/Types.h | 24 ++-- server/src/utils/SizeUtils.cpp | 9 ++ server/src/utils/SizeUtils.h | 14 ++ 6 files changed, 275 insertions(+), 249 deletions(-) create mode 100644 server/src/utils/SizeUtils.cpp create mode 100644 server/src/utils/SizeUtils.h diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 1828d3c0b..a25a2ce6d 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -8,6 +8,7 @@ #include "loguru.h" #include +#include using namespace tests; using namespace types; @@ -20,16 +21,15 @@ const std::string Tests::ERROR_SUITE_NAME = "error"; const Tests::MethodParam &tests::Tests::getStdinMethodParam() { static const Tests::MethodParam stdinMethodParam = - MethodParam(types::Type::CStringType(), types::Type::getStdinParamName(), std::nullopt); + MethodParam(types::Type::CStringType(), types::Type::getStdinParamName(), std::nullopt); return stdinMethodParam; } Tests::MethodDescription::MethodDescription() - : suiteTestCases{ { Tests::DEFAULT_SUITE_NAME, std::vector() }, - { Tests::ERROR_SUITE_NAME, std::vector() } }, - codeText{ { Tests::DEFAULT_SUITE_NAME, std::string() }, - { Tests::ERROR_SUITE_NAME, std::string() } } { -} + : suiteTestCases{{ Tests::DEFAULT_SUITE_NAME, std::vector() }, + { Tests::ERROR_SUITE_NAME, std::vector() }}, + codeText{{ Tests::DEFAULT_SUITE_NAME, std::string() }, + { Tests::ERROR_SUITE_NAME, std::string() }} { } static std::string makeDecimalConstant(std::string value, const std::string &typeName) { if (typeName == "long") { @@ -86,10 +86,10 @@ std::string processFPSpecialValue(const std::string &value) { std::shared_ptr KTestObjectParser::primitiveView(const std::vector &byteArray, const types::Type &type, - size_t offset, - size_t len) { + size_t offsetInBits, + size_t lenInBits) { Type readType = types::TypesHandler::isVoid(type) ? Type::minimalScalarType() : type; - std::string value = readBytesAsValueForType(byteArray, readType.baseType(), offset, len); + std::string value = readBytesAsValueForType(byteArray, readType.baseType(), offsetInBits, lenInBits); value = makeDecimalConstant(value, type.baseType()); value = processFPSpecialValue(value); if (types::TypesHandler::isBoolType(type)) { @@ -101,9 +101,9 @@ std::shared_ptr KTestObjectParser::primitiveView(const std:: std::shared_ptr KTestObjectParser::enumView(const std::vector &byteArray, types::EnumInfo &enumInfo, - size_t offset, - size_t len) { - std::string value = readBytesAsValue(byteArray, offset, len); + size_t offsetInBits, + size_t lenInBits) { + std::string value = readBytesAsValue(byteArray, offsetInBits, lenInBits); if (CollectionUtils::containsKey(enumInfo.valuesToEntries, value)) { auto name = enumInfo.getEntryName(value, utbot::Language::CXX); value = NameDecorator::decorate(name); @@ -116,11 +116,11 @@ std::shared_ptr KTestObjectParser::enumView(const std::vector KTestObjectParser::unionView(const std::vector &byteArray, types::UnionInfo &unionInfo, - size_t offset, + size_t offsetInBits, PointerUsage usage) { auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte"); - auto view = arrayView(byteArray, bytesType, unionInfo.size, offset, usage); - auto subViews = collectUnionSubViews(byteArray, unionInfo, offset, usage); + auto view = arrayView(byteArray, bytesType, unionInfo.size, offsetInBits, usage); + auto subViews = collectUnionSubViews(byteArray, unionInfo, offsetInBits, usage); return std::make_shared(unionInfo.name, std::move(view), std::move(subViews)); } @@ -149,10 +149,9 @@ std::shared_ptr KTestObjectParser::stringLiteralView(const std: std::shared_ptr KTestObjectParser::multiArrayView(const std::vector &byteArray, const types::Type &type, - size_t arraySize, - size_t offset, + size_t arraySizeInBits, + size_t offsetInBits, PointerUsage usage) { - size_t len; std::string message; types::EnumInfo enumInfo; types::UnionInfo unionInfo; @@ -161,27 +160,28 @@ std::shared_ptr KTestObjectParser::multiArrayView(const std::vec const types::Type baseType = type.baseTypeObj(); + size_t elementLenInBits; if (types::TypesHandler::isVoid(baseType)) { - len = typesHandler.typeSize(Type::minimalScalarType()); + elementLenInBits = typesHandler.typeSize(Type::minimalScalarType()); } else { - len = typesHandler.typeSize(baseType); + elementLenInBits = typesHandler.typeSize(baseType); } - for (size_t curPos = 0; curPos < arraySize; curPos += len) { + for (size_t curPos = 0; curPos < arraySizeInBits; curPos += elementLenInBits) { switch (typesHandler.getTypeKind(baseType)) { case TypeKind::STRUCT: structInfo = typesHandler.getStructInfo(baseType); - views.push_back(structView(byteArray, structInfo, curPos + offset, usage)); + views.push_back(structView(byteArray, structInfo, curPos + offsetInBits, usage)); break; case TypeKind::PRIMITIVE: - views.push_back(primitiveView(byteArray, baseType, curPos + offset, len)); + views.push_back(primitiveView(byteArray, baseType, curPos + offsetInBits, elementLenInBits)); break; case TypeKind::ENUM: enumInfo = typesHandler.getEnumInfo(type); - views.push_back(enumView(byteArray, enumInfo, curPos + offset, len)); + views.push_back(enumView(byteArray, enumInfo, curPos + offsetInBits, elementLenInBits)); break; case TypeKind::UNION: unionInfo = typesHandler.getUnionInfo(type); - views.push_back(unionView(byteArray, unionInfo, curPos + offset, usage)); + views.push_back(unionView(byteArray, unionInfo, curPos + offsetInBits, usage)); break; case TypeKind::OBJECT_POINTER: case TypeKind::ARRAY: @@ -204,8 +204,11 @@ std::shared_ptr KTestObjectParser::multiArrayView(const std::vec size_t size = sizes[i]; std::vector> newViews; for (size_t j = 0; j < views.size(); j += size) { - std::vector> curViews = - std::vector(views.begin() + j, views.begin() + j + size); + auto chunkBeginIt = views.begin(); + std::advance(chunkBeginIt, j); + auto chunkEndIt = views.begin(); + std::advance(chunkEndIt, j + size); + std::vector> curViews = std::vector(chunkBeginIt, chunkEndIt); newViews.push_back(std::make_shared(curViews)); } views = newViews; @@ -214,7 +217,7 @@ std::shared_ptr KTestObjectParser::multiArrayView(const std::vec return std::make_shared(views); } -std::shared_ptr KTestObjectParser::functionPointerView(const std::optional& scopeName, +std::shared_ptr KTestObjectParser::functionPointerView(const std::optional &scopeName, const std::string &methodName, const std::string ¶mName) { std::string value = PrinterUtils::getFunctionPointerStubName(scopeName, methodName, paramName).substr(1); @@ -229,35 +232,35 @@ std::shared_ptr KTestObjectParser::functionPointerView(cons std::shared_ptr KTestObjectParser::arrayView(const std::vector &byteArray, const types::Type &type, - size_t arraySize, - size_t offset, + size_t arraySizeInBits, + size_t offsetInBits, PointerUsage usage) { types::StructInfo structInfo; types::EnumInfo enumInfo; types::UnionInfo unionInfo; std::vector> subViews; - size_t len; + size_t elementLenInBits; if (types::TypesHandler::isVoid(type)) { - len = typesHandler.typeSize(Type::minimalScalarType()); + elementLenInBits = typesHandler.typeSize(Type::minimalScalarType()); } else { - len = typesHandler.typeSize(type); + elementLenInBits = typesHandler.typeSize(type); } - for (size_t curPos = 0; curPos < arraySize; curPos += len) { - switch (typesHandler.getTypeKind(type)) { + for (size_t curPos = 0; curPos < arraySizeInBits; curPos += elementLenInBits) { + switch (typesHandler.getTypeKind(type)) { // tdm_todo copypaste case TypeKind::STRUCT: structInfo = typesHandler.getStructInfo(type); - subViews.push_back(structView(byteArray, structInfo, curPos + offset, usage)); + subViews.push_back(structView(byteArray, structInfo, curPos + offsetInBits, usage)); break; case TypeKind::PRIMITIVE: - subViews.push_back(primitiveView(byteArray, type.baseTypeObj(), curPos + offset, len)); + subViews.push_back(primitiveView(byteArray, type.baseTypeObj(), curPos + offsetInBits, elementLenInBits)); break; case TypeKind::ENUM: enumInfo = typesHandler.getEnumInfo(type); - subViews.push_back(enumView(byteArray, enumInfo, curPos + offset, len)); + subViews.push_back(enumView(byteArray, enumInfo, curPos + offsetInBits, elementLenInBits)); break; case TypeKind::UNION: unionInfo = typesHandler.getUnionInfo(type); - subViews.push_back(unionView(byteArray, unionInfo, curPos + offset, usage)); + subViews.push_back(unionView(byteArray, unionInfo, curPos + offsetInBits, usage)); break; case TypeKind::OBJECT_POINTER: case TypeKind::ARRAY: @@ -276,15 +279,15 @@ std::shared_ptr KTestObjectParser::arrayView(const std::vector KTestObjectParser::structView(const std::vector &byteArray, types::StructInfo &curStruct, - size_t offset, + size_t offsetInBits, types::PointerUsage usage) { std::vector tmpInitReferences; - return structView(byteArray, curStruct, offset, usage, {}, "", {}, tmpInitReferences); + return structView(byteArray, curStruct, offsetInBits, usage, {}, "", {}, tmpInitReferences); } std::shared_ptr KTestObjectParser::structView(const std::vector &byteArray, StructInfo &curStruct, - size_t offset, + size_t offsetInBits, PointerUsage usage, const std::optional &testingMethod, const std::string &name, @@ -292,7 +295,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector std::vector &initReferences) { std::vector> subViews; std::vector fields; - size_t structOffset = offset; + size_t structOffset = offsetInBits; for (const auto &field: curStruct.fields) { fields.push_back(field.name); @@ -327,9 +330,9 @@ std::shared_ptr KTestObjectParser::structView(const std::vector if (pointerArrayKinds.size() > 1) { size_t size = 1; bool onlyArrays = true; - for (size_t i = 0; i < pointerArrayKinds.size(); i++) { - if (pointerArrayKinds[i]->getKind() == AbstractType::ARRAY) { - size *= pointerArrayKinds[i]->getSize(); + for (const auto &pointerArrayKind : pointerArrayKinds) { + if (pointerArrayKind->getKind() == AbstractType::ARRAY) { + size *= pointerArrayKind->getSize(); } else { onlyArrays = false; break; @@ -352,7 +355,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector break; case TypeKind::OBJECT_POINTER: res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset, - PointerWidthSize); + PointerWidthSizeInBits); subViews.push_back(getLazyPointerView(fromAddressToName, initReferences, PrinterUtils::getFieldAccess(name, field.name), res, field.type)); break; @@ -375,7 +378,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector std::optional entryValue; if (curStruct.hasAnonymousStructOrUnion) { auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte"); - const std::shared_ptr rawDataView = arrayView(byteArray, bytesType, curStruct.size, offset, usage); + const std::shared_ptr rawDataView = arrayView(byteArray, bytesType, curStruct.size, offsetInBits, usage); entryValue = PrinterUtils::convertBytesToUnion(curStruct.name, rawDataView->getEntryValue(nullptr)); } return std::make_shared(curStruct.isCLike, fields, subViews, entryValue); @@ -397,59 +400,59 @@ std::string KTestObjectParser::primitiveBoolView(const std::string &value) { std::string readBytesAsValueForType(const std::vector &byteArray, const std::string &typeName, - size_t offset, - size_t len) { + size_t offsetInBits, + size_t lenInBits) { if (typeName == "utbot_byte") { //we use different name to not trigger char processing - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "short") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "int") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "long") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "long long") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "unsigned short") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "unsigned int") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "unsigned long") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "unsigned long long") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "char") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "signed char") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "unsigned char") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "bool" || typeName == "_Bool") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "float") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "double") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } if (typeName == "long double") { - return readBytesAsValue(byteArray, offset, len); + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } - if (typeName == "int") { - return readBytesAsValue(byteArray, offset, len); + if (typeName == "std::uintptr_t" || typeName == "uintptr_t") { + return readBytesAsValue(byteArray, offsetInBits, lenInBits); } return ""; } @@ -509,9 +512,9 @@ namespace { //Predicate utilities. void KTestObjectParser::parseKTest(const MethodKtests &batch, tests::Tests &tests, - const std::unordered_map& methodNameToReturnTypeMap, + const std::unordered_map &methodNameToReturnTypeMap, bool filterByLineFlag, - std::shared_ptr lineInfo) { + const std::shared_ptr &lineInfo) { LOG_SCOPE_FUNCTION(DEBUG); sourceFilePath = tests.sourceFilePath; for (auto &[testMethod, testCases] : batch) { @@ -523,7 +526,7 @@ void KTestObjectParser::parseKTest(const MethodKtests &batch, } static std::string getSuiteName(const UTBotKTest::Status &status, - const std::shared_ptr lineInfo) { + const std::shared_ptr &lineInfo) { bool forAssert = lineInfo != nullptr && lineInfo->forAssert; if (status == UTBotKTest::Status::FAILED || forAssert) { return Tests::ERROR_SUITE_NAME; @@ -531,8 +534,8 @@ static std::string getSuiteName(const UTBotKTest::Status &status, return Tests::DEFAULT_SUITE_NAME; } -size_t KTestObjectParser::findFieldIndex(const StructInfo &structInfo, size_t offset) { - size_t indField = std::upper_bound(structInfo.fields.begin(), structInfo.fields.end(), offset, [] (int offset, const Field &field) { +size_t KTestObjectParser::findFieldIndex(const StructInfo &structInfo, size_t offsetInBits) { + size_t indField = std::upper_bound(structInfo.fields.begin(), structInfo.fields.end(), offsetInBits, [] (int offset, const Field &field) { return offset < field.offset; }) - structInfo.fields.begin(); indField--; @@ -603,10 +606,10 @@ void KTestObjectParser::assignTypeUnnamedVar(Tests::MethodTestCase &testCase, curType.paramValue.lazyValues.emplace_back(name, std::nullopt, testParamView); } - for (auto const &[offset, indObj] : testCase.objects[curType.jsonInd].offsets) { + for (auto const &[offset, indObj] : testCase.objects[curType.jsonInd].offsetsInBytes) { if (!visited[indObj]) { types::Type fieldType = - traverseLazyInStruct(visited, paramType, offset, testCase, methodDescription); + traverseLazyInStruct(visited, paramType, SizeUtils::bytesToBits(offset), testCase, methodDescription); Tests::MethodParam param = { fieldType, "", std::nullopt }; order.emplace(indObj, param, curType.paramValue); visited[indObj] = true; @@ -617,20 +620,20 @@ void KTestObjectParser::assignTypeUnnamedVar(Tests::MethodTestCase &testCase, types::Type KTestObjectParser::traverseLazyInStruct(std::vector &visited, const types::Type &curVarType, - size_t offset, + size_t offsetInBits, const Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription) { std::string message; switch (typesHandler.getTypeKind(curVarType)) { case TypeKind::STRUCT: { types::StructInfo structInfo = typesHandler.getStructInfo(curVarType); - size_t indField = findFieldIndex(structInfo, offset); + size_t indField = findFieldIndex(structInfo, offsetInBits); const types::Field &next = structInfo.fields[indField]; - return traverseLazyInStruct(visited, next.type, offset - next.offset, testCase, + return traverseLazyInStruct(visited, next.type, offsetInBits - next.offset, testCase, methodDescription); } case TypeKind::OBJECT_POINTER: { - LOG_IF_S(ERROR, offset != 0) << "Offset not zero" << offset; + LOG_IF_S(ERROR, offsetInBits != 0) << "Offset not zero" << offsetInBits; return curVarType.baseTypeObj(1); } case TypeKind::PRIMITIVE: { @@ -672,7 +675,7 @@ void KTestObjectParser::parseTestCases(const UTBotKTestList &cases, bool filterByLineFlag, Tests::MethodDescription &methodDescription, const std::unordered_map& methodNameToReturnTypeMap, - std::shared_ptr lineInfo) { + const std::shared_ptr &lineInfo) { /* Replace the return type for predicate scenario * to treat strings in specific way. This is done to retrieve * correct value from KTests and print the test. @@ -788,7 +791,7 @@ KTestObjectParser::parseTestCaseParameters(const UTBotKTest &testCases, Tests::TestCaseDescription KTestObjectParser::parseTestCaseParams(const UTBotKTest &ktest, const Tests::MethodDescription &methodDescription, - const std::unordered_map& methodNameToReturnTypeMap, + const std::unordered_map &methodNameToReturnTypeMap, const std::stringstream &traceStream) { std::vector rawKleeParams; for (auto const ¶m : ktest.objects) { @@ -807,11 +810,13 @@ KTestObjectParser::parseTestCaseParams(const UTBotKTest &ktest, int cnt = 0; std::vector visited(testCaseDescription.objects.size(), false); for (const auto &obj : testCaseDescription.objects) { - for (const auto &off : obj.offsets) { + for (const auto &off : obj.offsetsInBytes) { if (visited[off.index]) continue; visited[off.index] = true; testCaseDescription.objects[off.index].name = PrinterUtils::generateNewVar(++cnt); - uint64_t address = std::stoull(readBytesAsValueForType(obj.bytes, PointerWidthType, off.offset, PointerWidthSize)); + size_t address = std::stoull( + readBytesAsValueForType(obj.bytes, PointerWidthType, SizeUtils::bytesToBits(off.offset), + PointerWidthSizeInBits)); testCaseDescription.lazyAddressToName.emplace(address, testCaseDescription.objects[off.index].name); } } @@ -974,7 +979,7 @@ void KTestObjectParser::processParamPostValue(Tests::TestCaseDescription &testCa void KTestObjectParser::processStubParamValue(Tests::TestCaseDescription &testCaseDescription, const std::unordered_map& methodNameToReturnTypeMap, std::vector &rawKleeParams) { - for (const auto& kleeParam: rawKleeParams) { + for (const auto &kleeParam: rawKleeParams) { if (StringUtils::endsWith(kleeParam.paramName, PrinterUtils::KLEE_SYMBOLIC_SUFFIX)) { std::string methodName = kleeParam.paramName.substr(0, kleeParam.paramName.size() - PrinterUtils::KLEE_SYMBOLIC_SUFFIX.size()); if (!CollectionUtils::containsKey(methodNameToReturnTypeMap, methodName)) { @@ -992,13 +997,13 @@ void KTestObjectParser::processStubParamValue(Tests::TestCaseDescription &testCa } std::shared_ptr KTestObjectParser::testParameterView( - const KTestObjectParser::RawKleeParam &kleeParam, - const Tests::TypeAndVarName ¶m, - PointerUsage usage, - const MapAddressName &fromAddressToName, - std::vector &initReferences, - const std::optional &testingMethod) { - EnumInfo enumInfo; + const KTestObjectParser::RawKleeParam &kleeParam, + const Tests::TypeAndVarName ¶m, + PointerUsage usage, + const MapAddressName &fromAddressToName, + std::vector &initReferences, + const std::optional &testingMethod) { + EnumInfo enumInfo; // tdm_todo refactor so don't create useless variable. create it in switch StructInfo structInfo; UnionInfo unionInfo; std::string message, name; @@ -1006,22 +1011,22 @@ std::shared_ptr KTestObjectParser::testParameterView( const auto ¶mType = param.type; switch (typesHandler.getTypeKind(paramType)) { case TypeKind::PRIMITIVE: - return primitiveView(rawData, paramType.baseTypeObj(), 0, rawData.size() * CHAR_BIT); + return primitiveView(rawData, paramType.baseTypeObj(), 0, SizeUtils::bytesToBits(rawData.size())); case TypeKind::STRUCT: structInfo = typesHandler.getStructInfo(paramType); name = param.varName; - return structView(rawData, structInfo, 0, usage, testingMethod, name, fromAddressToName, initReferences); + return structView(rawData, structInfo, 0, usage, testingMethod, name, fromAddressToName, + initReferences); case TypeKind::OBJECT_POINTER: if (usage == types::PointerUsage::LAZY) { - std::string res = readBytesAsValueForType(rawData, PointerWidthType, 0, PointerWidthSize); - return getLazyPointerView(fromAddressToName, initReferences, param.varName, res, - paramType); + std::string res = readBytesAsValueForType(rawData, PointerWidthType, 0, PointerWidthSizeInBits); + return getLazyPointerView(fromAddressToName, initReferences, param.varName, res, paramType); } else if (types::TypesHandler::isCStringType(paramType)) { return stringLiteralView(rawData); } else if (paramType.kinds().size() > 2) { - return multiArrayView(rawData, paramType, rawData.size(), 0, usage); + return multiArrayView(rawData, paramType, SizeUtils::bytesToBits(rawData.size()), 0, usage); } else { - return arrayView(rawData, paramType.baseTypeObj(), rawData.size(), 0, usage); + return arrayView(rawData, paramType.baseTypeObj(), SizeUtils::bytesToBits(rawData.size()), 0, usage); } case TypeKind::FUNCTION_POINTER: if (!testingMethod.has_value()) { @@ -1030,15 +1035,15 @@ std::shared_ptr KTestObjectParser::testParameterView( return functionPointerView(testingMethod->getClassTypeName(), testingMethod->name, param.varName); case TypeKind::ENUM: enumInfo = typesHandler.getEnumInfo(paramType); - return enumView(rawData, enumInfo, 0, rawData.size()); + return enumView(rawData, enumInfo, 0, SizeUtils::bytesToBits(rawData.size())); case TypeKind::UNION: unionInfo = typesHandler.getUnionInfo(paramType); return unionView(rawData, unionInfo, 0, usage); case TypeKind::ARRAY: if (paramType.kinds().size() > 2) { - return multiArrayView(rawData, paramType, rawData.size() * CHAR_BIT, 0, usage); + return multiArrayView(rawData, paramType, SizeUtils::bytesToBits(rawData.size()), 0, usage); } else { - return arrayView(rawData, paramType.baseTypeObj(), rawData.size() * CHAR_BIT, 0, usage); + return arrayView(rawData, paramType.baseTypeObj(), SizeUtils::bytesToBits(rawData.size()), 0, usage); } case TypeKind::UNKNOWN: throw UnImplementedException("No such type"); @@ -1068,7 +1073,7 @@ KTestObjectParser::getLazyPointerView(const MapAddressName &fromAddressToName, std::vector> KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, const types::UnionInfo &info, - size_t offset, + size_t offsetInBits, types::PointerUsage usage) { std::vector> subViews; for (const auto &field : info.fields) { @@ -1078,23 +1083,23 @@ KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, types::StructInfo innerStruct; switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offset, + subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offsetInBits, std::min(field.size, fieldLen))); break; case TypeKind::STRUCT: innerStruct = typesHandler.getStructInfo(field.type); - subViews.push_back(structView(byteArray, innerStruct, offset, usage)); + subViews.push_back(structView(byteArray, innerStruct, offsetInBits, usage)); break; case TypeKind::ENUM: innerEnum = typesHandler.getEnumInfo(field.type); - subViews.push_back(enumView(byteArray, innerEnum, offset, fieldLen)); + subViews.push_back(enumView(byteArray, innerEnum, offsetInBits, fieldLen)); break; case TypeKind::UNION: innerUnion = typesHandler.getUnionInfo(field.type); - subViews.push_back(unionView(byteArray, innerUnion, offset, usage)); + subViews.push_back(unionView(byteArray, innerUnion, offsetInBits, usage)); break; case TypeKind::ARRAY: - subViews.push_back(arrayView(byteArray, field.type.baseTypeObj(), fieldLen, offset, usage)); + subViews.push_back(arrayView(byteArray, field.type.baseTypeObj(), fieldLen, offsetInBits, usage)); break; case TypeKind::OBJECT_POINTER: subViews.push_back(std::make_shared(PrinterUtils::C_NULL)); @@ -1137,22 +1142,18 @@ Tests::MethodDescriptionHash::operator()(const Tests::MethodDescription &methodD } UnionValueView::UnionValueView( - const std::string &typeName, - const std::shared_ptr &rawDataView, - std::vector, std::allocator>> subViews) - : AbstractValueView(std::move(subViews)), - entryValue(PrinterUtils::convertBytesToUnion(typeName, rawDataView->getEntryValue(nullptr))) { -} + const std::string &typeName, + const std::shared_ptr &rawDataView, + std::vector, std::allocator>> subViews) + : AbstractValueView(std::move(subViews)), + entryValue(PrinterUtils::convertBytesToUnion(typeName, rawDataView->getEntryValue(nullptr))) {} TestMethod::TestMethod(std::string methodName, fs::path bitcodeFile, fs::path sourceFilename, bool is32) - : methodName(std::move(methodName)) - , bitcodeFilePath(std::move(bitcodeFile)) - , sourceFilePath(std::move(sourceFilename)) - , is32bits(is32) -{} + : methodName(std::move(methodName)), bitcodeFilePath(std::move(bitcodeFile)), + sourceFilePath(std::move(sourceFilename)), is32bits(is32) {} bool TestMethod::operator==(const TestMethod &rhs) const { - return std::tie( methodName, bitcodeFilePath, sourceFilePath, is32bits) + return std::tie( methodName, bitcodeFilePath, sourceFilePath, is32bits) == std::tie(rhs.methodName, rhs.bitcodeFilePath, rhs.sourceFilePath, rhs.is32bits); } bool TestMethod::operator!=(const TestMethod &rhs) const { @@ -1161,7 +1162,7 @@ bool TestMethod::operator!=(const TestMethod &rhs) const { UTBotKTestObject::UTBotKTestObject(std::string name, std::vector bytes, std::vector offsets, uint64_t address, bool is_lazy) : name(std::move(name)), bytes(std::move(bytes)), - offsets(std::move(offsets)), address(address), is_lazy(is_lazy) { + offsetsInBytes(std::move(offsets)), address(address), is_lazy(is_lazy) { } UTBotKTestObject::UTBotKTestObject(const ConcretizedObject &kTestObject) diff --git a/server/src/Tests.h b/server/src/Tests.h index a1230d293..4db971109 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -6,6 +6,7 @@ #include "types/Types.h" #include "utils/CollectionUtils.h" #include "utils/PrinterUtils.h" +#include "utils/SizeUtils.h" #include "json.hpp" #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -48,10 +50,18 @@ namespace tests { struct UTBotKTestObject { std::string name; std::vector bytes; - std::vector offsets; + std::vector offsetsInBytes; size_t address; bool is_lazy = false; + /** + * Constructs UTBotKTestObject + * @param name object's name + * @param bytes byte array associated with object + * @param offsets in bytes + * @param address object's address + * @param is_lazy whether object is lazy + */ UTBotKTestObject(std::string name, std::vector bytes, std::vector offsets, @@ -69,15 +79,14 @@ namespace tests { Status status; std::vector errorDescriptors; - UTBotKTest(const std::vector &objects, + UTBotKTest(std::vector objects, const Status &status, std::vector &errorDescriptors) : - objects(objects), - status(status), - errorDescriptors(errorDescriptors) - {} + objects(std::move(objects)), + status(status), + errorDescriptors(errorDescriptors) {} - bool isError() { + [[nodiscard]] bool isError() const { return !errorDescriptors.empty(); } @@ -333,16 +342,16 @@ namespace tests { std::string name, std::optional alignment, bool hasIncompleteType = false) - : type(std::move(type)), name(std::move(name)), alignment(std::move(alignment)), + : type(std::move(type)), name(std::move(name)), alignment(alignment), hasIncompleteType(hasIncompleteType) { } - std::string underscoredName() const { + [[nodiscard]] std::string underscoredName() const { return "_" + name; } - bool isChangeable() const { + [[nodiscard]] bool isChangeable() const { if((type.isObjectPointer() || type.isLValueReference()) && !type.isTypeContainsFunctionPointer() && !type.isConstQualifiedValue() && !types::TypesHandler::baseTypeIsVoid(type)) { @@ -351,7 +360,7 @@ namespace tests { return false; } - std::string dataVariableName() const { + [[nodiscard]] std::string dataVariableName() const { return this->type.isTwoDimensionalPointer() ? this->underscoredName() : this->name; @@ -366,12 +375,12 @@ namespace tests { std::vector lazyValues; TestCaseParamValue() = default; - TestCaseParamValue(const std::string &_name, + TestCaseParamValue(std::string _name, const std::optional &_alignment, - const std::shared_ptr &_view) - : name(_name), + std::shared_ptr _view) + : name(std::move(_name)), alignment(_alignment), - view(_view) {} + view(std::move(_view)) {} }; struct TestCaseDescription { @@ -492,7 +501,7 @@ namespace tests { } [[nodiscard]] bool hasChangeable() const { - for(const auto& i : params) { + for (const auto& i : params) { if (i.isChangeable()) { return true; } @@ -590,9 +599,9 @@ namespace tests { */ void parseKTest(const MethodKtests &batch, tests::Tests &tests, - const std::unordered_map& methodNameToReturnTypeMap, + const std::unordered_map &methodNameToReturnTypeMap, bool filterByLineFlag, - std::shared_ptr lineInfo); + const std::shared_ptr &lineInfo); private: fs::path sourceFilePath; @@ -631,8 +640,8 @@ namespace tests { void parseTestCases(const UTBotKTestList &cases, bool filterByLineFlag, Tests::MethodDescription &methodDescription, - const std::unordered_map& methodNameToReturnTypeMap, - std::shared_ptr lineInfo); + const std::unordered_map &methodNameToReturnTypeMap, + const std::shared_ptr &lineInfo); /** * Parses parameters that are stored in given objects. Then parameters * are written into paramValues. @@ -658,20 +667,20 @@ namespace tests { std::shared_ptr multiArrayView(const std::vector &byteArray, const types::Type &type, - size_t arraySize, - size_t offset, + size_t arraySizeInBits, + size_t offsetInBits, types::PointerUsage usage); std::shared_ptr arrayView(const std::vector &byteArray, const types::Type &type, - size_t arraySize, - size_t offset, + size_t arraySizeInBits, + size_t offsetInBits, types::PointerUsage usage); static std::shared_ptr stringLiteralView(const std::vector &byteArray, size_t length = 0); - std::shared_ptr functionPointerView(const std::optional& scopeName, + std::shared_ptr functionPointerView(const std::optional &scopeName, const std::string &methodName, const std::string ¶mName); @@ -680,17 +689,17 @@ namespace tests { std::shared_ptr unionView(const std::vector &byteArray, types::UnionInfo &unionInfo, - size_t offset, + size_t offsetInBits, types::PointerUsage usage); std::shared_ptr structView(const std::vector &byteArray, types::StructInfo &curStruct, - size_t offset, + size_t offsetInBits, types::PointerUsage usage); std::shared_ptr structView(const std::vector &byteArray, types::StructInfo &curStruct, - size_t offset, + size_t offsetInBits, types::PointerUsage usage, const std::optional &testingMethod, const std::string &name, @@ -699,20 +708,20 @@ namespace tests { std::shared_ptr primitiveView(const std::vector &byteArray, const types::Type &type, - size_t offset, - size_t len); + size_t offsetInBits, + size_t lenInBits); static std::shared_ptr enumView(const std::vector &byteArray, types::EnumInfo &enumInfo, - size_t offset, - size_t len); + size_t offsetInBits, + size_t lenInBits); std::string primitiveCharView(const types::Type &type, std::string value); static std::string primitiveBoolView(const std::string &value); constexpr static const char *const KLEE_PATH_FLAG = "kleePathFlag"; - const std::string PointerWidthType = "unsigned long long"; - const size_t PointerWidthSize = 8 * CHAR_BIT; //tdm_todo remove hardcoded constant + const std::string PointerWidthType = "std::uintptr_t"; + const size_t PointerWidthSizeInBits = SizeUtils::bytesToBits(sizeof(std::uintptr_t)); constexpr static const char *const KLEE_PATH_FLAG_SYMBOLIC = "kleePathFlagSymbolic"; static std::vector::const_iterator @@ -722,11 +731,11 @@ namespace tests { Tests::TestCaseDescription parseTestCaseParams(const UTBotKTest &ktest, const Tests::MethodDescription &methodDescription, - const std::unordered_map& methodNameToReturnTypeMap, + const std::unordered_map &methodNameToReturnTypeMap, const std::stringstream &traceStream); std::vector> collectUnionSubViews(const std::vector &byteArray, const types::UnionInfo &info, - size_t offset, + size_t offsetInBits, types::PointerUsage usage); void processGlobalParamPreValue(Tests::TestCaseDescription &testCaseDescription, const Tests::MethodParam &globalParam, @@ -756,7 +765,7 @@ namespace tests { const types::Type ¶mType, Tests::TestCaseParamValue ¶mValue, std::vector &visited, - std::queue& order); + std::queue &order); void assignTypeUnnamedVar(Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription); @@ -764,11 +773,11 @@ namespace tests { void assignTypeStubVar(Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription); - size_t findFieldIndex(const types::StructInfo &structInfo, size_t offset); + size_t findFieldIndex(const types::StructInfo &structInfo, size_t offsetInBits); types::Type traverseLazyInStruct(std::vector &visited, const types::Type &curVarType, - size_t offset, + size_t offsetInBits, const Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription); @@ -779,7 +788,7 @@ namespace tests { const types::Type ¶mType) const; }; /** - * @brief This function is used for converting primiive value of a specific type + * @brief This function is used for converting primitive value of a specific type * To a string value which we can print to .cpp file. */ template @@ -797,6 +806,13 @@ namespace tests { return ss.str(); } + /** + * Sign extension of number stored in @b bytes with sign bit at @b signPos + * @tparam T type: @a char or @a unsigned @a char + * @param bytes byte array + * @param len length of bytes + * @param signPos 0-based index of sign bit in two's complement + */ template void sext(T* bytes, size_t len, size_t signPos) { int bit = (bytes[signPos / CHAR_BIT] >> (signPos % CHAR_BIT)) & 1; @@ -808,13 +824,14 @@ namespace tests { } } } + /** * This function is used for converting sequence of bytes to specific type. - * Returns string representation of a value recorded in byteArray. + * Returns string representation of a value recorded in @b byteArray. * @tparam T - type of value * @param byteArray - * @param offset - initial position of a value in byteArray - * @param len - size of T + * @param offset - initial position in bits of a value in byteArray + * @param len - number of bits to read * @return string representation of value */ template @@ -827,7 +844,7 @@ namespace tests { size_t hi = CHAR_BIT - lo; size_t stop = (offset + len + CHAR_BIT - 1) / CHAR_BIT; for (size_t i = offset / CHAR_BIT, j = 0; i < stop; ++i, ++j) { - std::cout << "i = " << i << ", j = " << j << ", byte = " << (unsigned)byteArray[i] << std::endl; +// std::cout << "i = " << i << ", j = " << j << ", byte = " << (unsigned)byteArray[i] << std::endl; auto byte = static_cast(byteArray[i]); if (i + 1 == stop) { size_t top = (offset + len) % CHAR_BIT; @@ -844,7 +861,7 @@ namespace tests { if (j < sizeof(T)) { bytes[j] = high; } - std::cout << "low = " << (unsigned)low << ", high = " << (unsigned)high << std::endl; +// std::cout << "low = " << (unsigned)low << ", high = " << (unsigned)high << std::endl; } } else { for (size_t j = 0; j < len / CHAR_BIT; j++) { @@ -858,18 +875,19 @@ namespace tests { T pValue = *pTypeValue; return primitiveValueToString(pValue); } + /** * Same as readBytesAsValue, but returns result of readBytesAsValue that is already * parametrized by type that corresponds to given typeName. * @param typeName - string name of type * @param byteArray - array of bytes - * @param offset - initial position - * @param len - size of type that corresponds to typeName + * @param offsetInBits - initial position in bits + * @param lenInBits - number of bits to read * @return string representation of value. */ std::string readBytesAsValueForType(const std::vector &byteArray, const std::string &typeName, - size_t offset, - size_t len); + size_t offsetInBits, + size_t lenInBits); } #endif // UNITTESTBOT_TESTS_H diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 57d1912d4..428b24b56 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -6,6 +6,7 @@ #include "TypeVisitor.h" #include "exceptions/UnImplementedException.h" #include "utils/PrinterUtils.h" +#include "utils/SizeUtils.h" #include "loguru.h" @@ -150,8 +151,8 @@ bool types::Type::maybeReturnArray() const { size_t types::Type::countReturnPointers(bool decrementIfArray) const { size_t returnPointer = 0; const std::vector> pointerArrayKinds = this->pointerArrayKinds(); - for (size_t i = 0; i < pointerArrayKinds.size(); ++i) { - returnPointer += pointerArrayKinds[i]->getKind() == AbstractType::OBJECT_POINTER; + for (const auto &pointerArrayKind: pointerArrayKinds) { + returnPointer += pointerArrayKind->getKind() == AbstractType::OBJECT_POINTER; } if (decrementIfArray && maybeReturnArray()) { returnPointer--; @@ -168,7 +169,7 @@ std::vector types::Type::arraysSizes(PointerUsage usage) const { return {}; } std::vector sizes; - for (const auto& kind: pointerArrayKinds()) { + for (const auto &kind: pointerArrayKinds()) { switch (kind->getKind()) { case AbstractType::ARRAY: sizes.push_back(kind->getSize()); @@ -299,7 +300,7 @@ bool types::Type::isPointerToArray() const { } bool types::Type::isConstQualifiedValue() const { - for(const auto& kind : mKinds) { + for (const auto &kind: mKinds) { if(kind->getKind() == AbstractType::SIMPLE) { if(dynamic_cast(kind.get())->isConstQualified()) { return true; @@ -383,6 +384,19 @@ void types::Type::replaceUsedType(const types::TypeName &newUsedType) { /* * Integer types */ +static const std::unordered_map integerTypesToSizes = { + {"utbot_byte", SizeUtils::bytesToBits(sizeof(char))}, // we use different name to not trigger char processing + {"short", SizeUtils::bytesToBits(sizeof(short))}, + {"int", SizeUtils::bytesToBits(sizeof(int))}, + {"long", SizeUtils::bytesToBits(sizeof(long))}, + {"long long", SizeUtils::bytesToBits(sizeof(long long))}, + {"unsigned short", SizeUtils::bytesToBits(sizeof(unsigned short))}, + {"unsigned int", SizeUtils::bytesToBits(sizeof(unsigned int))}, + {"unsigned long", SizeUtils::bytesToBits(sizeof(unsigned long))}, + {"unsigned long long", SizeUtils::bytesToBits(sizeof(unsigned long long))}, + {"unsigned char", SizeUtils::bytesToBits(sizeof(unsigned char))} // we do not want to treat an unsigned char as character literal +}; + bool types::TypesHandler::isIntegerType(const Type &type) { return type.isSimple() && isIntegerType(type.baseType()); } @@ -392,40 +406,56 @@ bool types::TypesHandler::isUnsignedType(const Type &type) { } bool types::TypesHandler::isIntegerType(const TypeName &typeName) { - return CollectionUtils::containsKey(integerTypesToSizes(), typeName); + return CollectionUtils::containsKey(integerTypesToSizes, typeName); } /* * Floating point types */ +static const std::unordered_map floatingPointTypesToSizes = { + {"float", SizeUtils::bytesToBits(sizeof(float))}, + {"double", SizeUtils::bytesToBits(sizeof(double))}, + {"long double", SizeUtils::bytesToBits(sizeof(long double))} +}; + bool types::TypesHandler::isFloatingPointType(const Type &type) { return type.isSimple() && isFloatingPointType(type.baseType()); } bool types::TypesHandler::isFloatingPointType(const TypeName &type) { - return CollectionUtils::containsKey(floatingPointTypesToSizes(), type); + return CollectionUtils::containsKey(floatingPointTypesToSizes, type); } /* * Character types */ +static const std::unordered_map characterTypesToSizes = { + {"char", SizeUtils::bytesToBits(sizeof(char))}, + {"signed char", SizeUtils::bytesToBits(sizeof(signed char))}, +}; + bool types::TypesHandler::isCharacterType(const Type &type) { return type.isSimple() && isCharacterType(type.baseType()); } bool types::TypesHandler::isCharacterType(const TypeName &type) { - return CollectionUtils::containsKey(characterTypesToSizes(), type); + return CollectionUtils::containsKey(characterTypesToSizes, type); } /* * Boolean types */ +static const std::unordered_map boolTypesToSizes = { + {"bool", SizeUtils::bytesToBits(sizeof(bool))}, + {"_Bool", SizeUtils::bytesToBits(sizeof(bool))} +}; + bool types::TypesHandler::isBoolType(const Type &type) { return type.isSimple() && isBoolType(type.baseType()); } bool types::TypesHandler::isBoolType(const TypeName &type) { - return CollectionUtils::containsKey(boolTypesToSizes(), type); + return CollectionUtils::containsKey(boolTypesToSizes, type); } /* @@ -545,15 +575,15 @@ types::UnionInfo types::TypesHandler::getUnionInfo(uint64_t id) const { return typeFromMap(id, typeMaps.unions); } -/* - * Check if type is a pointer +/** + * Checks whether type is a pointer */ bool types::TypesHandler::isObjectPointerType(const Type &type) { return type.isObjectPointer(); } -/* - * Check if type is an array +/** + * Checks whether type is an array */ bool types::TypesHandler::isArrayType(const Type &type) { return type.isArray(); @@ -569,19 +599,19 @@ bool types::TypesHandler::isOneDimensionPointer(const types::Type &type) { size_t types::TypesHandler::typeSize(const types::Type &type) const { if (isIntegerType(type)) { - return integerTypesToSizes().at(type.baseType()); + return integerTypesToSizes.at(type.baseType()); } if (isBoolType(type)) { - return boolTypesToSizes().at(type.baseType()); + return boolTypesToSizes.at(type.baseType()); } if (isFloatingPointType(type)) { - return floatingPointTypesToSizes().at(type.baseType()); + return floatingPointTypesToSizes.at(type.baseType()); } if (isCharacterType(type)) { - return characterTypesToSizes().at(type.baseType()); + return characterTypesToSizes.at(type.baseType()); } if (isStruct(type)) { @@ -595,7 +625,7 @@ size_t types::TypesHandler::typeSize(const types::Type &type) const { } if (isObjectPointerType(type)) { - return getPointerSize() * CHAR_BIT; + return SizeUtils::bytesToBits(getPointerSize()); } if (isEnum(type)) { @@ -607,7 +637,7 @@ size_t types::TypesHandler::typeSize(const types::Type &type) const { } if (isPointerToFunction(type)) { - return sizeof(char *) * CHAR_BIT; + return SizeUtils::bytesToBits(sizeof(char *)); } throw UnImplementedException("Type is unknown for: " + type.typeName()); @@ -661,71 +691,27 @@ std::string types::TypesHandler::removeArrayBrackets(TypeName type) { return type; } -const std::unordered_map &types::TypesHandler::integerTypesToSizes() noexcept { - static const std::unordered_map integerTypes = { - {"utbot_byte", sizeof(char) * CHAR_BIT}, // we use different name to not trigger char processing - {"short", sizeof(short) * CHAR_BIT}, - {"int", sizeof(int) * CHAR_BIT}, - {"long", sizeof(long) * CHAR_BIT}, - {"long long", sizeof(long long) * CHAR_BIT}, - {"unsigned short", sizeof(unsigned short) * CHAR_BIT}, - {"unsigned int", sizeof(unsigned int) * CHAR_BIT}, - {"unsigned long", sizeof(unsigned long) * CHAR_BIT}, - {"unsigned long long", sizeof(unsigned long long) * CHAR_BIT}, - {"unsigned char", sizeof(unsigned char) * CHAR_BIT} // we do not want to treat an unsigned char as character literal - }; - return integerTypes; -} - testsgen::ValidationType types::TypesHandler::getIntegerValidationType(const Type &type) { size_t size; if (isIntegerType(type)) { - size = integerTypesToSizes().at(type.baseType()); + size = integerTypesToSizes.at(type.baseType()); } else { ABORT_F("type is not an integerType: %s", type.baseType().c_str()); } bool isUnsigned = isUnsignedType(type); - if (size == 1) { + if (size == 8) { return (isUnsigned) ? testsgen::UINT8_T : testsgen::INT8_T; - } else if (size == 2) { + } else if (size == 16) { return (isUnsigned) ? testsgen::UINT16_T : testsgen::INT16_T; - } else if (size == 4) { + } else if (size == 32) { return (isUnsigned) ? testsgen::UINT32_T : testsgen::INT32_T; - } else if (size == 8) { + } else if (size == 64) { return (isUnsigned) ? testsgen::UINT64_T : testsgen::INT64_T; } else { ABORT_F("Unknown integer size: %zu", size); } } -const std::unordered_map &types::TypesHandler::floatingPointTypesToSizes() noexcept { - static const std::unordered_map floatingPointTypes = { - {"float", sizeof(float) * CHAR_BIT}, - {"double", sizeof(double) * CHAR_BIT}, - {"long double", sizeof(long double) * CHAR_BIT} - }; - - return floatingPointTypes; -} - -const std::unordered_map &types::TypesHandler::characterTypesToSizes() noexcept { - static const std::unordered_map characterTypes = { - {"char", sizeof(char) * CHAR_BIT}, - {"signed char", sizeof(signed char) * CHAR_BIT}, - }; - - return characterTypes; -} - -const std::unordered_map &types::TypesHandler::boolTypesToSizes() noexcept { - static const std::unordered_map boolTypes = { - {"bool", sizeof(bool) * CHAR_BIT}, - {"_Bool", sizeof(bool) * CHAR_BIT} - }; - - return boolTypes; -} - const std::unordered_map> &types::TypesHandler::preferredConstraints() noexcept { static const std::unordered_map> constraints = { {"char", {">= 'a'", "<= 'z'", "!= '\\0'"}}, diff --git a/server/src/types/Types.h b/server/src/types/Types.h index bf3e0dbc1..b1583d56e 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -259,9 +259,10 @@ namespace types { struct Field { types::Type type; std::string name; - size_t size; // size in bits - // reassigned in structFields - size_t offset = 0; // offset in bits + /// size in @b bits + size_t size; + /// offset in @b bits, reassigned in structFields + size_t offset = 0; enum AccessSpecifier { AS_pubic, AS_protected, @@ -275,8 +276,10 @@ namespace types { fs::path filePath; std::string name; std::string definition; - size_t size; // size in bits - size_t alignment; // alignment in **bytes** + /// size in @b bits + size_t size; + /// alignment in @b bytes + size_t alignment; }; typedef std::unordered_map> FPointerMap; @@ -330,8 +333,8 @@ namespace types { class TypesHandler { public: struct SizeContext { - size_t pointerSize = 8; - size_t maximumAlignment = 16; + size_t pointerSize = 8; /// pointerSize in @b bytes + size_t maximumAlignment = 16; /// maximumAlignment in @b bytes }; explicit TypesHandler(TypeMaps &types, SizeContext sizeContext) @@ -340,7 +343,7 @@ namespace types { /** * This functions calculates size of a given type. For structs in it calculates sum of sizes of its fields, * ignoring alignment. - * @return size of given type in bits. + * @return size of given type in @b bits. */ size_t typeSize(const types::Type &type) const; @@ -637,11 +640,6 @@ namespace types { IsSupportedTypeArgumentsHash> isSupportedTypeHash{}; - static const std::unordered_map &integerTypesToSizes() noexcept; - static const std::unordered_map &floatingPointTypesToSizes() noexcept; - static const std::unordered_map &characterTypesToSizes() noexcept; - static const std::unordered_map &boolTypesToSizes() noexcept; - template bool typeIsInMap(uint64_t id, const std::unordered_map& someMap) const { if (CollectionUtils::containsKey(someMap, id)) { diff --git a/server/src/utils/SizeUtils.cpp b/server/src/utils/SizeUtils.cpp new file mode 100644 index 000000000..85d7e516f --- /dev/null +++ b/server/src/utils/SizeUtils.cpp @@ -0,0 +1,9 @@ +#include "SizeUtils.h" + +size_t SizeUtils::bytesToBits(size_t bytes) { + return bytes * CHAR_BIT; +} + +size_t SizeUtils::bitsToBytes(size_t bits) { + return bits / CHAR_BIT; +} diff --git a/server/src/utils/SizeUtils.h b/server/src/utils/SizeUtils.h new file mode 100644 index 000000000..8f14ca9cd --- /dev/null +++ b/server/src/utils/SizeUtils.h @@ -0,0 +1,14 @@ +#ifndef UTBOTCPP_SIZEUTILS_H +#define UTBOTCPP_SIZEUTILS_H + +#include +#include + +namespace SizeUtils { + size_t bytesToBits(size_t bytes); + + size_t bitsToBytes(size_t bits); +} + + +#endif //UTBOTCPP_SIZEUTILS_H From 5abf8011ec79a157578d6214424a7554e9424c28 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Thu, 18 Aug 2022 14:52:40 +0300 Subject: [PATCH 09/19] remove invalid test --- server/test/framework/Utils_Tests.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/server/test/framework/Utils_Tests.cpp b/server/test/framework/Utils_Tests.cpp index 38856027d..4d8bef0ff 100644 --- a/server/test/framework/Utils_Tests.cpp +++ b/server/test/framework/Utils_Tests.cpp @@ -305,16 +305,6 @@ namespace { EXPECT_EQ(tests::readBytesAsValue(bytes, offset, len), "131071"); } - TEST(ReadBytesAsValueTestDeathTest, InvalidLen) { - size_t const LEN = 4; - std::vector bytes(LEN); - bytes[3] = 1; - bytes[2] = bytes[1] = bytes[0] = -1; - size_t offset = 0; - size_t len = CHAR_BIT * 2; - EXPECT_DEATH(tests::readBytesAsValue(bytes, offset, len), ""); - } - template void readBytesAsValueTestTemplate(T val) { srand(42); From 612f1a8acd17f1de14e5ba967d49651859deaaec Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Fri, 19 Aug 2022 17:00:17 +0300 Subject: [PATCH 10/19] fix link to utbot --- server/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 69d3b30bd..a2bbd6de1 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -17,7 +17,7 @@ if (NOT DEFINED RUN_INFO) endif () project(UTBotCpp DESCRIPTION "Tool that generates unit test by C/C++ source code, trying to reach all branches and maximize code coverage" - HOMEPAGE_URL "https://unittestbot.org" VERSION ${VERSION}) + HOMEPAGE_URL "https://www.utbot.org/" VERSION ${VERSION}) set(PROJECT_ORG "UnitTestBot") message("Project: ${PROJECT_NAME}") From bde9979785d49012abd8e2f2f9744e25dbe44aeb Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Fri, 19 Aug 2022 17:04:33 +0300 Subject: [PATCH 11/19] bit fields examples added --- integration-tests/c-example/lib/bitfields.c | 118 ++++++++++++++++++++ integration-tests/c-example/lib/bitfields.h | 103 +++++++++++++++++ server/src/types/TypesResolver.cpp | 6 + 3 files changed, 227 insertions(+) create mode 100644 integration-tests/c-example/lib/bitfields.c create mode 100644 integration-tests/c-example/lib/bitfields.h diff --git a/integration-tests/c-example/lib/bitfields.c b/integration-tests/c-example/lib/bitfields.c new file mode 100644 index 000000000..e14eaa2e9 --- /dev/null +++ b/integration-tests/c-example/lib/bitfields.c @@ -0,0 +1,118 @@ +#include "bitfields.h" +#include +#include +#include +#include + +static int ALIGN = -30; + +#define print_sizeof(S) printf("size of %*s : %zu bytes\n", ALIGN, #S, sizeof((S){})) + +//int main() { +// print_sizeof(SimpleSignedStr); +// print_sizeof(SimpleUnsignedStr); +// print_sizeof(ImplementationDefinedStr); +// print_sizeof(PossiblySmallStr); +// print_sizeof(SimpleUnsignedUnion); +// print_sizeof(ComplexStr); +// print_sizeof(StrWithBool); +// print_sizeof(StrWithUnnamedBitfields); +// print_sizeof(StrWithUnnamedZeroBitfield); +// print_sizeof(StrWithBreak); +//} + +int check_simple_signed_str(SimpleSignedStr s) { + if (s.a == 1024 && s.b == -1 && s.d == -16) { + return 1; + } else if (s.b == 0) { + return -1; + } + return 0; +} + +int is_sum_greater_10(SimpleUnsignedStr s) { + return (s.a + s.b + s.c + s.d > 10 && s.d > 5); +} + +int is_all_greater_2(PossiblySmallStr s) { + if (s.a > 2 && s.b > 2) { + return 1; + } + return 0; +} + +int union_greater_2(SimpleUnsignedUnion u) { + if (u.c > 2) { + return 1; + } + return 0; +} + +int union_greater_2_short(SimpleUnsignedUnion u) { + return u.c > 2; +} + +int sum_of_fields(ComplexStr s) { + if (!s.a) { + return -1; + } + return s.a + s.b + s.c + s.d + s.e; +} + +int decode_from_bool(StrWithBool s) { + if (!s.a && !s.b && !s.c) { + return 0; + } else if (s.a && !s.b && !s.c) { + return 1; + } else if (!s.a && s.b && !s.c) { + return 2; + } else if (s.a && s.b && !s.c) { + return 3; + } else if (!s.a && !s.b && s.c) { + return 4; + } else if (s.a && !s.b && s.c) { + return 5; + } else if (!s.a && s.b && s.c) { + return 6; + } + return 7; +} + +int decode_from_bool_simple(StrWithBool s) { + if (!s.a && !s.b && !s.c) { + return 0; + } else if (s.a && !s.b && s.c) { + return 5; + } + return 7; +} + + StrWithUnnamedBitfields mult_by_two(StrWithUnnamedBitfields s) { + s.b1 *= 2; + s.b2 *= 2; + s.b3 *= 2; + return s; + } + + int is_nice(StrWithUnnamedZeroBitfield s) { + if (s.b1 == 69 && s.b2 == 42 && s.b3 == 1488) { + return 13; + } + return 0; + } + +int checkFieldsBounds(StrWithBreak s) { + assert(s.b1 >= 0 && s.b1 <= 127); + assert(s.breaking >= LLONG_MIN && s.breaking <= LLONG_MAX); + assert(s.b2 >= -65536 && s.b2 <= 65535); + assert(s.b3 == true || s.b3 == false); + assert(s.b4 >= -2097152 && s.b4 <= 2097151); + if (s.b1 >= 123 && s.b3) { + return 1; + } else if (s.b1 >= 123 && s.b4 < 0) { + return 2; + } else if (s.breaking > 42) { + return 3; + } + return 4; +} diff --git a/integration-tests/c-example/lib/bitfields.h b/integration-tests/c-example/lib/bitfields.h new file mode 100644 index 000000000..07e873429 --- /dev/null +++ b/integration-tests/c-example/lib/bitfields.h @@ -0,0 +1,103 @@ +#ifndef C_EXAMPLE_BITFIELDS_H +#define C_EXAMPLE_BITFIELDS_H + +#include + +typedef struct { + signed a : 24; + signed b : 1; + signed int c : 2; + signed int d : 5; +} SimpleSignedStr; + +typedef struct { + unsigned a : 2; + unsigned b : 5; + unsigned int c : 1; + unsigned int d : 24; +} SimpleUnsignedStr; + +typedef struct { + int a : 24; + int b : 2; + int c : 1; + int d : 5; +} ImplementationDefinedStr; + +typedef struct { + signed a : 3; + signed b : 3; +} PossiblySmallStr; + +typedef union { + unsigned a : 2; + unsigned b : 1; + unsigned c : 6; + unsigned d : 23; +} SimpleUnsignedUnion; + +typedef struct { + // will **usually** occupy 8 bytes: + bool a : 1; + unsigned b : 2; + signed c : 1; + unsigned d : 3; + // 25 bits: unused + int e; +} ComplexStr; + +typedef struct { + // will **usually** occupy 1 byte + bool a : 1, b : 1, c : 1; +} StrWithBool; + +// struct InvalidNumberOfBits { // should produce an error in C +// bool a : 2; +// unsigned b : 50; +// signed c : 1; +// unsigned d : 3; +// }; + +typedef struct { + // will **usually** occupy 4 bytes: + // 5 bits: value of b1 + // 11 bits: unused -- explicitly specified padding + // 6 bits: value of b2 + // 2 bits: value of b3 + // 3 bits: unused -- explicitly specified padding + // 5 bits: unused -- implicitly + unsigned b1 : 5, : 11, b2 : 6, b3 : 2; + signed : 3; +} StrWithUnnamedBitfields; + +typedef struct { + // will **usually** occupy 8 bytes: + // 7 bits: value of b1 + // 25 bits: unused + // 6 bits: value of b2 + // 15 bits: value of b3 + // 11 bits: unused + unsigned b1 : 7; + unsigned : 0; // start a new allocation unit + unsigned b2 : 6; + unsigned b3 : 15; +} StrWithUnnamedZeroBitfield; + +typedef struct { + // will **usually** occupy 24 bytes: + // 7 bits: value of b1 + // 57 bits: unused + // 64 bits: value of breaking + // 17 bits: value of b2 + // 1 bit: value of b3 + // 22 bits: value of b4 + // 24 bits: unused + unsigned b1 : 7; // from 0 to 127 + long long breaking; // from LLONG_MIN to LLONG_MAX + signed b2 : 17; // from -65536 to 65535 + bool b3 : 1; // from 0 to 1 + int b4 : 22; // usually from -2097152 to 2097151 +} StrWithBreak; + + +#endif //C_EXAMPLE_BITFIELDS_H diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index 64ffdead9..2af040ea1 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -112,6 +112,9 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string << "\tFile path: " << structInfo.filePath.string() << ""; std::vector fields; for (const clang::FieldDecl *F : D->fields()) { + if (F->isUnnamedBitfield()) { + continue; + } structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; field.name = F->getNameAsString(); @@ -266,6 +269,9 @@ void TypesResolver::resolveUnion(const clang::RecordDecl *D, const std::string & std::vector fields; unionInfo.hasAnonymousStructOrUnion = false; for (const clang::FieldDecl *F : D->fields()) { + if (F->isUnnamedBitfield()) { + continue; + } unionInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; field.name = F->getNameAsString(); From 77f9f2bf5ba9937e7c1652830ea36f1044f9e15d Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Tue, 23 Aug 2022 16:16:08 +0300 Subject: [PATCH 12/19] unnamed bit fields klee wrapper generation --- server/src/Tests.cpp | 46 +++++++++---------- server/src/Tests.h | 5 ++ .../src/printers/KleeConstraintsPrinter.cpp | 13 +++++- server/src/printers/KleePrinter.cpp | 28 +++++------ .../src/visitors/AbstractValueViewVisitor.cpp | 6 +++ .../FunctionPointerForStubsVisitor.cpp | 3 +- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index a25a2ce6d..8f9eee5bf 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -826,35 +826,16 @@ KTestObjectParser::parseTestCaseParams(const UTBotKTest &ktest, if (methodDescription.isClassMethod()) { auto methodParam = methodDescription.classObj.value(); std::shared_ptr testParamView; - auto paramType = methodParam.type.maybeJustPointer() ? methodParam.type.baseTypeObj() : methodParam.type; - if (CollectionUtils::containsKey(methodDescription.functionPointers, methodParam.name)) { - testParamView = testParameterView( - emptyKleeParam, { paramType, methodParam.name }, PointerUsage::PARAMETER, testCaseDescription.lazyAddressToName, - testCaseDescription.lazyReferences, methodDescription); - } else { - const auto kleeParam = getKleeParamOrThrow(rawKleeParams, methodParam.name); - testParamView = testParameterView(kleeParam, {paramType, methodParam.name }, PointerUsage::PARAMETER, - testCaseDescription.lazyAddressToName, testCaseDescription.lazyReferences, - methodDescription); - } + getTestParamView(methodDescription, rawKleeParams, emptyKleeParam, testCaseDescription, methodParam, + testParamView); testCaseDescription.classPreValues = { methodParam.name, methodParam.alignment, testParamView }; processClassPostValue(testCaseDescription, methodParam, rawKleeParams); } for (auto &methodParam : methodDescription.params) { std::shared_ptr testParamView; - auto paramType = methodParam.type.maybeJustPointer() ? methodParam.type.baseTypeObj() : methodParam.type; - if (CollectionUtils::containsKey(methodDescription.functionPointers, methodParam.name)) { - testParamView = testParameterView( - emptyKleeParam, { paramType, methodParam.name }, PointerUsage::PARAMETER, testCaseDescription.lazyAddressToName, - testCaseDescription.lazyReferences, methodDescription); - } else { - const auto kleeParam = getKleeParamOrThrow(rawKleeParams, methodParam.name); - - testParamView = testParameterView(kleeParam, {paramType, methodParam.name }, PointerUsage::PARAMETER, - testCaseDescription.lazyAddressToName, testCaseDescription.lazyReferences, - methodDescription); - } + getTestParamView(methodDescription, rawKleeParams, emptyKleeParam, testCaseDescription, methodParam, + testParamView); testCaseDescription.funcParamValues.emplace_back(methodParam.name, methodParam.alignment, testParamView); @@ -906,6 +887,25 @@ KTestObjectParser::parseTestCaseParams(const UTBotKTest &ktest, return testCaseDescription; } +void KTestObjectParser::getTestParamView(const Tests::MethodDescription &methodDescription, + const std::vector &rawKleeParams, + const KTestObjectParser::RawKleeParam &emptyKleeParam, + Tests::TestCaseDescription &testCaseDescription, + const Tests::MethodParam& methodParam, + std::shared_ptr &testParamView) { + auto paramType = methodParam.type.maybeJustPointer() ? methodParam.type.baseTypeObj() : methodParam.type; + if (CollectionUtils::containsKey(methodDescription.functionPointers, methodParam.name)) { + testParamView = testParameterView( + emptyKleeParam, { paramType, methodParam.name }, PointerUsage::PARAMETER, testCaseDescription.lazyAddressToName, + testCaseDescription.lazyReferences, methodDescription); + } else { + const auto kleeParam = getKleeParamOrThrow(rawKleeParams, methodParam.name); + testParamView = testParameterView(kleeParam, { paramType, methodParam.name }, PointerUsage::PARAMETER, + testCaseDescription.lazyAddressToName, testCaseDescription.lazyReferences, + methodDescription); + } +} + void KTestObjectParser::processGlobalParamPreValue(Tests::TestCaseDescription &testCaseDescription, const Tests::MethodParam &globalParam, std::vector &rawKleeParams) { diff --git a/server/src/Tests.h b/server/src/Tests.h index 4db971109..181a9eacd 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -786,6 +786,11 @@ namespace tests { const std::string &name, std::string res, const types::Type ¶mType) const; + + void + getTestParamView(const Tests::MethodDescription &methodDescription, const std::vector &rawKleeParams, + const RawKleeParam &emptyKleeParam, Tests::TestCaseDescription &testCaseDescription, + const Tests::MethodParam& methodParam, std::shared_ptr &testParamView); }; /** * @brief This function is used for converting primitive value of a specific type diff --git a/server/src/printers/KleeConstraintsPrinter.cpp b/server/src/printers/KleeConstraintsPrinter.cpp index a71c249e8..89cd5f4c9 100644 --- a/server/src/printers/KleeConstraintsPrinter.cpp +++ b/server/src/printers/KleeConstraintsPrinter.cpp @@ -79,7 +79,12 @@ void KleeConstraintsPrinter::genConstraintsForUnion(const ConstraintsState &stat ConstraintsState newState = { state.paramName, access, field.type, false, state.depth + 1 }; switch (typesHandler->getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - return genConstraintsForPrimitive(newState); + if (!field.name.empty()) { + genConstraintsForPrimitive(newState); + } else { + ss << TAB_N() << "// No constraints for unnamed bit fields" << NL; + } + break; case TypeKind::STRUCT: return genConstraintsForStruct(newState); case TypeKind::ARRAY: @@ -160,7 +165,11 @@ void KleeConstraintsPrinter::genConstraintsForStruct(const ConstraintsState &sta std::string stubFunctionName = PrinterUtils::getFunctionPointerAsStructFieldStubName(curStruct.name, field.name); switch (kind) { case TypeKind::PRIMITIVE: - genConstraintsForPrimitive(newState); + if (!field.name.empty()) { + genConstraintsForPrimitive(newState); + } else { + ss << TAB_N() << "// No constraints for unnamed bit fields" << NL; + } break; case TypeKind::STRUCT: genConstraintsForStruct(newState); diff --git a/server/src/printers/KleePrinter.cpp b/server/src/printers/KleePrinter.cpp index 51267c65f..a75c7d167 100644 --- a/server/src/printers/KleePrinter.cpp +++ b/server/src/printers/KleePrinter.cpp @@ -72,15 +72,15 @@ void KleePrinter::writeTestedFunction(const Tests &tests, } fs::path KleePrinter::writeTmpKleeFile( - const Tests &tests, - const std::string &buildDir, - const PathSubstitution &pathSubstitution, - const std::optional &predicateInfo, - const std::string &testedMethod, - const std::optional &testedClass, - bool onlyForOneFunction, - bool onlyForOneClass, - const std::function &methodFilter) { + const Tests &tests, + const std::string &buildDir, + const PathSubstitution &pathSubstitution, + const std::optional &predicateInfo, + const std::string &testedMethod, + const std::optional &testedClass, + bool onlyForOneFunction, + bool onlyForOneClass, + const std::function &methodFilter) { resetStream(); writeCopyrightHeader(); @@ -401,7 +401,7 @@ void KleePrinter::makeBracketsForStrPredicate(const std::optional &inf void KleePrinter::genReturnDeclaration(const Tests::MethodDescription &testMethod, const std::optional &predicateInfo) { - // If return type is a pointer, we compare values that are stored at this pointers, + // If return type is a pointer, we compare values that are stored at these pointers, // not the pointers themselves Type returnType = types::TypesHandler::isVoid(testMethod.returnType.baseTypeObj()) ? Type::minimalScalarType() @@ -424,10 +424,10 @@ void KleePrinter::genReturnDeclaration(const Tests::MethodDescription &testMetho } void KleePrinter::genParamsKleeAssumes( - const Tests::MethodDescription &testMethod, - const std::optional &predicateInfo, - const std::string &testedMethod, - bool onlyForOneEntity) { + const Tests::MethodDescription &testMethod, + const std::optional &predicateInfo, + const std::string &testedMethod, + bool onlyForOneEntity) { visitor::KleeAssumeReturnValueVisitor(typesHandler, this).visit(testMethod, predicateInfo); if (!onlyForOneEntity && !testedMethod.empty() && !predicateInfo.has_value()) { std::string assumption = concat("(", PrinterUtils::KLEE_PATH_FLAG, PrinterUtils::EQ_OPERATOR, PrinterUtils::KLEE_PATH_FLAG_SYMBOLIC, ") & (", diff --git a/server/src/visitors/AbstractValueViewVisitor.cpp b/server/src/visitors/AbstractValueViewVisitor.cpp index 1f5b75420..1cc7cd059 100644 --- a/server/src/visitors/AbstractValueViewVisitor.cpp +++ b/server/src/visitors/AbstractValueViewVisitor.cpp @@ -95,6 +95,9 @@ namespace visitor { auto subViews = view ? &view->getSubViews() : nullptr; for (int i = 0; i < structInfo.fields.size(); i++) { auto const &field = structInfo.fields[i]; + if (field.name.empty() && types::TypesHandler::isPrimitiveType(field.type)) { // unnamed bit field + continue; + } auto newName = PrinterUtils::getFieldAccess(name, field); auto const *newView = (subViews && i < subViews->size()) ? (*subViews)[i].get() : nullptr; auto newAccess = PrinterUtils::getFieldAccess(access, field); @@ -113,6 +116,9 @@ namespace visitor { inUnion = true; for (int i = 0; i < unionInfo.fields.size(); i++) { auto const &field = unionInfo.fields[i]; + if (field.name.empty() && types::TypesHandler::isPrimitiveType(field.type)) { // unnamed bit field + continue; + } auto newName = PrinterUtils::getFieldAccess(name, field.name); auto const *newView = subViews ? (*subViews)[i].get() : nullptr; auto newAccess = PrinterUtils::getFieldAccess(access, field.name); diff --git a/server/src/visitors/FunctionPointerForStubsVisitor.cpp b/server/src/visitors/FunctionPointerForStubsVisitor.cpp index bae82b5a6..1e0e2300b 100644 --- a/server/src/visitors/FunctionPointerForStubsVisitor.cpp +++ b/server/src/visitors/FunctionPointerForStubsVisitor.cpp @@ -44,7 +44,8 @@ namespace visitor { } for (auto &field : structInfo.fields) { if (!types::TypesHandler::isPointerToFunction(field.type) && - !types::TypesHandler::isArrayOfPointersToFunction(field.type)) { + !types::TypesHandler::isArrayOfPointersToFunction(field.type) && + !(types::TypesHandler::isPrimitiveType(field.type) && name.empty())) { visitAny(field.type, name, nullptr, access, depth + 1); } } From 5d52fc8d050f6f7b8df05773beba2ca22524b55c Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Tue, 23 Aug 2022 17:05:48 +0300 Subject: [PATCH 13/19] unnamed bit fields tests generation --- server/src/Tests.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 8f9eee5bf..5c0df5faa 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -298,6 +298,9 @@ std::shared_ptr KTestObjectParser::structView(const std::vector size_t structOffset = offsetInBits; for (const auto &field: curStruct.fields) { + if (field.name.empty() && typesHandler.getTypeKind(field.type) == TypeKind::PRIMITIVE) { //tdm_todo вынести в функцию isUnnamedBitfield + continue; + } fields.push_back(field.name); size_t fieldLen = typesHandler.typeSize(field.type); size_t fieldOffset = structOffset + field.offset; @@ -308,7 +311,6 @@ std::shared_ptr KTestObjectParser::structView(const std::vector switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - // tdm_todo unnamed bitfield subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), fieldOffset, std::min(field.size, fieldLen))); break; @@ -1083,8 +1085,10 @@ KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, types::StructInfo innerStruct; switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offsetInBits, - std::min(field.size, fieldLen))); + if (!field.name.empty()) { + subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offsetInBits, + std::min(field.size, fieldLen))); + } break; case TypeKind::STRUCT: innerStruct = typesHandler.getStructInfo(field.type); From 922bab5a4aec34d8758749c8428b0956ae4ab4ef Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Wed, 24 Aug 2022 13:34:07 +0300 Subject: [PATCH 14/19] minor refactoring --- .github/workflows/publish-utbot.yml | 1 + server/CMakeLists.txt | 2 +- server/src/Tests.cpp | 17 ++++---- server/src/Tests.h | 3 -- .../src/printers/KleeConstraintsPrinter.cpp | 43 +++++++++++-------- server/src/printers/KleeConstraintsPrinter.h | 2 + server/src/types/Types.cpp | 4 ++ server/src/types/Types.h | 1 + server/src/types/TypesResolver.cpp | 9 +--- .../src/visitors/AbstractValueViewVisitor.cpp | 4 +- .../FunctionPointerForStubsVisitor.cpp | 2 +- server/test/framework/Utils_Tests.cpp | 10 +---- 12 files changed, 47 insertions(+), 51 deletions(-) diff --git a/.github/workflows/publish-utbot.yml b/.github/workflows/publish-utbot.yml index 54be04a68..75afac098 100644 --- a/.github/workflows/publish-utbot.yml +++ b/.github/workflows/publish-utbot.yml @@ -6,6 +6,7 @@ on: - '[1-9][0-9][0-9][0-9].[1]?[0-9].[0-9]+' branches: - main + - belous-dp/bitfields jobs: matrix-prep: diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index a2bbd6de1..26ce0ad19 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -17,7 +17,7 @@ if (NOT DEFINED RUN_INFO) endif () project(UTBotCpp DESCRIPTION "Tool that generates unit test by C/C++ source code, trying to reach all branches and maximize code coverage" - HOMEPAGE_URL "https://www.utbot.org/" VERSION ${VERSION}) + HOMEPAGE_URL "https://www.utbot.org" VERSION ${VERSION}) set(PROJECT_ORG "UnitTestBot") message("Project: ${PROJECT_NAME}") diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 5c0df5faa..a42b31044 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -298,7 +298,7 @@ std::shared_ptr KTestObjectParser::structView(const std::vector size_t structOffset = offsetInBits; for (const auto &field: curStruct.fields) { - if (field.name.empty() && typesHandler.getTypeKind(field.type) == TypeKind::PRIMITIVE) { //tdm_todo вынести в функцию isUnnamedBitfield + if (field.isUnnamedBitfield()) { continue; } fields.push_back(field.name); @@ -307,7 +307,6 @@ std::shared_ptr KTestObjectParser::structView(const std::vector types::EnumInfo innerEnum; types::UnionInfo innerUnion; types::StructInfo innerStruct; - std::string res; switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: @@ -355,11 +354,12 @@ std::shared_ptr KTestObjectParser::structView(const std::vector } } break; - case TypeKind::OBJECT_POINTER: - res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset, + case TypeKind::OBJECT_POINTER: { + std::string res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset, PointerWidthSizeInBits); subViews.push_back(getLazyPointerView(fromAddressToName, initReferences, PrinterUtils::getFieldAccess(name, field.name), res, field.type)); + } break; case TypeKind::FUNCTION_POINTER: subViews.push_back(functionPointerView(curStruct.name, field.name)); @@ -1079,16 +1079,17 @@ KTestObjectParser::collectUnionSubViews(const std::vector &byteArray, types::PointerUsage usage) { std::vector> subViews; for (const auto &field : info.fields) { + if (field.isUnnamedBitfield()) { + continue; + } size_t fieldLen = typesHandler.typeSize(field.type); types::EnumInfo innerEnum; types::UnionInfo innerUnion; types::StructInfo innerStruct; switch (typesHandler.getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - if (!field.name.empty()) { - subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offsetInBits, - std::min(field.size, fieldLen))); - } + subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), offsetInBits, + std::min(field.size, fieldLen))); break; case TypeKind::STRUCT: innerStruct = typesHandler.getStructInfo(field.type); diff --git a/server/src/Tests.h b/server/src/Tests.h index 181a9eacd..2c67e5e59 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -843,13 +843,11 @@ namespace tests { std::string readBytesAsValue(const std::vector &byteArray, size_t offset, size_t len) { char bytes[sizeof(T)] = {}; if (offset % CHAR_BIT != 0 || len % CHAR_BIT != 0) { - // tdm_todo unnamed bitfield // if (len == 0) // WARNING: assuming little endian and two's complement size_t lo = offset % CHAR_BIT; size_t hi = CHAR_BIT - lo; size_t stop = (offset + len + CHAR_BIT - 1) / CHAR_BIT; for (size_t i = offset / CHAR_BIT, j = 0; i < stop; ++i, ++j) { -// std::cout << "i = " << i << ", j = " << j << ", byte = " << (unsigned)byteArray[i] << std::endl; auto byte = static_cast(byteArray[i]); if (i + 1 == stop) { size_t top = (offset + len) % CHAR_BIT; @@ -866,7 +864,6 @@ namespace tests { if (j < sizeof(T)) { bytes[j] = high; } -// std::cout << "low = " << (unsigned)low << ", high = " << (unsigned)high << std::endl; } } else { for (size_t j = 0; j < len / CHAR_BIT; j++) { diff --git a/server/src/printers/KleeConstraintsPrinter.cpp b/server/src/printers/KleeConstraintsPrinter.cpp index 89cd5f4c9..16b8d6bca 100644 --- a/server/src/printers/KleeConstraintsPrinter.cpp +++ b/server/src/printers/KleeConstraintsPrinter.cpp @@ -52,10 +52,14 @@ void KleeConstraintsPrinter::genConstraintsForPrimitive(const ConstraintsState & if (!cons.empty()) { strFunctionCall(PrinterUtils::KLEE_PREFER_CEX, { state.paramName, cons }); } else { - ss << TAB_N() << "// No constraints for " << state.curElement << NL; + noConstraints(state.curElement); } } +void KleeConstraintsPrinter::noConstraints(const std::string &cause) { + ss << TAB_N() << "// No constraints for " << cause << NL; +} + void KleeConstraintsPrinter::genConstraintsForEnum(const ConstraintsState &state) { types::EnumInfo enumInfo = typesHandler->getEnumInfo(state.curType); @@ -73,17 +77,15 @@ void KleeConstraintsPrinter::genConstraintsForUnion(const ConstraintsState &stat UnionInfo curUnion = typesHandler->getUnionInfo(state.curType); for (const auto &field : curUnion.fields) { - std::string errorMessage = "Unrecognized field of type '" + field.type.typeName() + - "' in union '" + curUnion.name + "'."; + if (field.isUnnamedBitfield()) { + noConstraints("unnamed bit fields"); + continue; + } auto access = PrinterUtils::getFieldAccess(state.curElement, field); ConstraintsState newState = { state.paramName, access, field.type, false, state.depth + 1 }; switch (typesHandler->getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - if (!field.name.empty()) { - genConstraintsForPrimitive(newState); - } else { - ss << TAB_N() << "// No constraints for unnamed bit fields" << NL; - } + genConstraintsForPrimitive(newState); break; case TypeKind::STRUCT: return genConstraintsForStruct(newState); @@ -95,9 +97,12 @@ void KleeConstraintsPrinter::genConstraintsForUnion(const ConstraintsState &stat return genConstraintsForUnion(newState); case TypeKind::OBJECT_POINTER: return genConstraintsForPointerInUnion(newState); - case TypeKind::UNKNOWN: + case TypeKind::UNKNOWN: { + std::string errorMessage = "Unrecognized field of type '" + field.type.typeName() + + "' in union '" + curUnion.name + "'."; LOG_S(ERROR) << errorMessage; throw UnImplementedException(errorMessage); + } default: std::string message = "Missing case for this TypeKind in switch"; LOG_S(ERROR) << message; @@ -157,19 +162,16 @@ void KleeConstraintsPrinter::genConstraintsForStruct(const ConstraintsState &sta StructInfo curStruct = typesHandler->getStructInfo(state.curType); for (const auto &field : curStruct.fields) { - std::string errorMessage = "Unrecognized field of type '" + field.type.typeName() + - "' in struct '" + curStruct.name + "'."; + if (field.isUnnamedBitfield()) { + noConstraints("unnamed bit fields"); + continue; + } auto access = PrinterUtils::getFieldAccess(state.curElement, field); ConstraintsState newState = { state.paramName, access, field.type, state.endString, state.depth + 1 }; - TypeKind kind = typesHandler->getTypeKind(field.type); std::string stubFunctionName = PrinterUtils::getFunctionPointerAsStructFieldStubName(curStruct.name, field.name); - switch (kind) { + switch (typesHandler->getTypeKind(field.type)) { case TypeKind::PRIMITIVE: - if (!field.name.empty()) { - genConstraintsForPrimitive(newState); - } else { - ss << TAB_N() << "// No constraints for unnamed bit fields" << NL; - } + genConstraintsForPrimitive(newState); break; case TypeKind::STRUCT: genConstraintsForStruct(newState); @@ -191,8 +193,11 @@ void KleeConstraintsPrinter::genConstraintsForStruct(const ConstraintsState &sta case TypeKind::FUNCTION_POINTER: genStubForStructFunctionPointer(state.curElement, field.name, stubFunctionName); break; - case TypeKind::UNKNOWN: + case TypeKind::UNKNOWN: { + std::string errorMessage = "Unrecognized field of type '" + field.type.typeName() + + "' in struct '" + curStruct.name + "'."; throw UnImplementedException(errorMessage); + } default: std::string message = "Missing case for this TypeKind in switch"; LOG_S(ERROR) << message; diff --git a/server/src/printers/KleeConstraintsPrinter.h b/server/src/printers/KleeConstraintsPrinter.h index 14b7a5d6e..9924c786c 100644 --- a/server/src/printers/KleeConstraintsPrinter.h +++ b/server/src/printers/KleeConstraintsPrinter.h @@ -49,6 +49,8 @@ namespace printer { void genConstraintsForPointerInUnion(const ConstraintsState &state); static std::string cexConstraints(const std::string &name, const types::Type &type); + + void noConstraints(const std::string &cause); }; } diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 428b24b56..0129f1e6f 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -488,6 +488,10 @@ bool types::TypesHandler::isStruct(uint64_t id) const { return typeIsInMap(id, typeMaps.structs); } +bool types::Field::isUnnamedBitfield() const { + return name.empty() && TypesHandler::isPrimitiveType(type); +} + /* * Enum types */ diff --git a/server/src/types/Types.h b/server/src/types/Types.h index b1583d56e..7c9c21654 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -270,6 +270,7 @@ namespace types { AS_none }; AccessSpecifier accessSpecifier = AS_pubic; + [[nodiscard]] bool isUnnamedBitfield() const; }; struct TypeInfo { diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index 2af040ea1..c81fd454c 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -95,7 +95,7 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string structInfo.name = getFullname(D, canonicalType, id, sourceFilePath); structInfo.hasAnonymousStructOrUnion = false; if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::CXX) { - const auto *cppD = dynamic_cast(D); + const auto *cppD = llvm::dyn_cast(D); structInfo.isCLike = cppD != nullptr && cppD->isCLike(); } else { @@ -112,9 +112,6 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string << "\tFile path: " << structInfo.filePath.string() << ""; std::vector fields; for (const clang::FieldDecl *F : D->fields()) { - if (F->isUnnamedBitfield()) { - continue; - } structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; field.name = F->getNameAsString(); @@ -166,7 +163,6 @@ void TypesResolver::resolveStruct(const clang::RecordDecl *D, const std::string structInfo.fields = fields; structInfo.size = getRecordSize(D); structInfo.alignment = getDeclAlignment(D); - addInfo(id, parent->projectTypes->structs, structInfo); ss << "\nName: " << structInfo.name << ", id: " << id << " , size: " << structInfo.size << "\n"; @@ -269,9 +265,6 @@ void TypesResolver::resolveUnion(const clang::RecordDecl *D, const std::string & std::vector fields; unionInfo.hasAnonymousStructOrUnion = false; for (const clang::FieldDecl *F : D->fields()) { - if (F->isUnnamedBitfield()) { - continue; - } unionInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; field.name = F->getNameAsString(); diff --git a/server/src/visitors/AbstractValueViewVisitor.cpp b/server/src/visitors/AbstractValueViewVisitor.cpp index 1cc7cd059..621e9c26b 100644 --- a/server/src/visitors/AbstractValueViewVisitor.cpp +++ b/server/src/visitors/AbstractValueViewVisitor.cpp @@ -95,7 +95,7 @@ namespace visitor { auto subViews = view ? &view->getSubViews() : nullptr; for (int i = 0; i < structInfo.fields.size(); i++) { auto const &field = structInfo.fields[i]; - if (field.name.empty() && types::TypesHandler::isPrimitiveType(field.type)) { // unnamed bit field + if (field.isUnnamedBitfield()) { continue; } auto newName = PrinterUtils::getFieldAccess(name, field); @@ -116,7 +116,7 @@ namespace visitor { inUnion = true; for (int i = 0; i < unionInfo.fields.size(); i++) { auto const &field = unionInfo.fields[i]; - if (field.name.empty() && types::TypesHandler::isPrimitiveType(field.type)) { // unnamed bit field + if (field.isUnnamedBitfield()) { continue; } auto newName = PrinterUtils::getFieldAccess(name, field.name); diff --git a/server/src/visitors/FunctionPointerForStubsVisitor.cpp b/server/src/visitors/FunctionPointerForStubsVisitor.cpp index 1e0e2300b..2d311f741 100644 --- a/server/src/visitors/FunctionPointerForStubsVisitor.cpp +++ b/server/src/visitors/FunctionPointerForStubsVisitor.cpp @@ -45,7 +45,7 @@ namespace visitor { for (auto &field : structInfo.fields) { if (!types::TypesHandler::isPointerToFunction(field.type) && !types::TypesHandler::isArrayOfPointersToFunction(field.type) && - !(types::TypesHandler::isPrimitiveType(field.type) && name.empty())) { + !field.isUnnamedBitfield()) { visitAny(field.type, name, nullptr, access, depth + 1); } } diff --git a/server/test/framework/Utils_Tests.cpp b/server/test/framework/Utils_Tests.cpp index 4d8bef0ff..68a1a6628 100644 --- a/server/test/framework/Utils_Tests.cpp +++ b/server/test/framework/Utils_Tests.cpp @@ -309,21 +309,13 @@ namespace { void readBytesAsValueTestTemplate(T val) { srand(42); for (size_t tcount = 0; tcount < 5; ++tcount) { -// std::cout << "\ttest #" << tcount << ", val = " << val; auto add = static_cast(rand() % 10); auto start = static_cast(rand() % add); -// std::cout << "additional bytes: " << add << ", start position: " << start << std::endl; size_t const len = sizeof(T); std::vector bytes(len + add); for (size_t i = 1; i <= len; ++i) { - bytes[start + i - 1] = (val & ((1LL << (CHAR_BIT * i)) - 1)) >> (CHAR_BIT * (i - 1)); -// std::cout << (unsigned)bytes[start + i - 1] << " "; + bytes[start + i - 1] = (val >> (CHAR_BIT * (i - 1))) & ((1 << CHAR_BIT) - 1); } -// std::cout << std::endl; -// for (char c : bytes) { -// std::cout << (unsigned)c << " "; -// } -// std::cout << std::endl; EXPECT_EQ(tests::readBytesAsValue(bytes, start * CHAR_BIT, len * CHAR_BIT), std::to_string(val)); } } From 446318ea65ce5a3d626b0415b3ad91592c29565e Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Wed, 31 Aug 2022 16:43:54 +0300 Subject: [PATCH 15/19] requested changes fix --- .../lib/{ => structures}/bitfields.c | 24 +++++++++---------- .../lib/{ => structures}/bitfields.h | 0 server/src/Tests.cpp | 9 +++---- 3 files changed, 17 insertions(+), 16 deletions(-) rename integration-tests/c-example/lib/{ => structures}/bitfields.c (84%) rename integration-tests/c-example/lib/{ => structures}/bitfields.h (100%) diff --git a/integration-tests/c-example/lib/bitfields.c b/integration-tests/c-example/lib/structures/bitfields.c similarity index 84% rename from integration-tests/c-example/lib/bitfields.c rename to integration-tests/c-example/lib/structures/bitfields.c index e14eaa2e9..8eb806cee 100644 --- a/integration-tests/c-example/lib/bitfields.c +++ b/integration-tests/c-example/lib/structures/bitfields.c @@ -8,18 +8,18 @@ static int ALIGN = -30; #define print_sizeof(S) printf("size of %*s : %zu bytes\n", ALIGN, #S, sizeof((S){})) -//int main() { -// print_sizeof(SimpleSignedStr); -// print_sizeof(SimpleUnsignedStr); -// print_sizeof(ImplementationDefinedStr); -// print_sizeof(PossiblySmallStr); -// print_sizeof(SimpleUnsignedUnion); -// print_sizeof(ComplexStr); -// print_sizeof(StrWithBool); -// print_sizeof(StrWithUnnamedBitfields); -// print_sizeof(StrWithUnnamedZeroBitfield); -// print_sizeof(StrWithBreak); -//} +void print_sizeof_structs() { + print_sizeof(SimpleSignedStr); + print_sizeof(SimpleUnsignedStr); + print_sizeof(ImplementationDefinedStr); + print_sizeof(PossiblySmallStr); + print_sizeof(SimpleUnsignedUnion); + print_sizeof(ComplexStr); + print_sizeof(StrWithBool); + print_sizeof(StrWithUnnamedBitfields); + print_sizeof(StrWithUnnamedZeroBitfield); + print_sizeof(StrWithBreak); +} int check_simple_signed_str(SimpleSignedStr s) { if (s.a == 1024 && s.b == -1 && s.d == -16) { diff --git a/integration-tests/c-example/lib/bitfields.h b/integration-tests/c-example/lib/structures/bitfields.h similarity index 100% rename from integration-tests/c-example/lib/bitfields.h rename to integration-tests/c-example/lib/structures/bitfields.h diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index a42b31044..95c3a073e 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -29,7 +29,8 @@ Tests::MethodDescription::MethodDescription() : suiteTestCases{{ Tests::DEFAULT_SUITE_NAME, std::vector() }, { Tests::ERROR_SUITE_NAME, std::vector() }}, codeText{{ Tests::DEFAULT_SUITE_NAME, std::string() }, - { Tests::ERROR_SUITE_NAME, std::string() }} { } + { Tests::ERROR_SUITE_NAME, std::string() }}, + modifiers{} { } static std::string makeDecimalConstant(std::string value, const std::string &typeName) { if (typeName == "long") { @@ -65,7 +66,7 @@ static const std::unordered_map FPSpecialValuesMapping namespace tests { /** - * The function checks for precense of argument in values as it is + * The function checks for presence of argument in values as it is * called by the time processFPSpecialValue is already applied */ bool isFPSpecialValue(const std::string& value) { @@ -246,7 +247,7 @@ std::shared_ptr KTestObjectParser::arrayView(const std::vector KTestObjectParser::testParameterView( const MapAddressName &fromAddressToName, std::vector &initReferences, const std::optional &testingMethod) { - EnumInfo enumInfo; // tdm_todo refactor so don't create useless variable. create it in switch + EnumInfo enumInfo; // TODO refactor so don't create useless variable. create it in switch StructInfo structInfo; UnionInfo unionInfo; std::string message, name; From 57f94d58f020b928d33cc6f24b155298ccc1c6a6 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Thu, 1 Sep 2022 15:15:04 +0300 Subject: [PATCH 16/19] tests added --- .../c-example/lib/structures/bitfields.c | 32 ++++++++++++++++- .../cpp-example/cxx_lib/structs/bitfields.cpp | 36 +++++++++++++++++++ .../cpp-example/cxx_lib/structs/bitfields.h | 29 +++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp create mode 100644 integration-tests/cpp-example/cxx_lib/structs/bitfields.h diff --git a/integration-tests/c-example/lib/structures/bitfields.c b/integration-tests/c-example/lib/structures/bitfields.c index 8eb806cee..30da29faf 100644 --- a/integration-tests/c-example/lib/structures/bitfields.c +++ b/integration-tests/c-example/lib/structures/bitfields.c @@ -3,6 +3,7 @@ #include #include #include +#include static int ALIGN = -30; @@ -101,7 +102,7 @@ int decode_from_bool_simple(StrWithBool s) { return 0; } -int checkFieldsBounds(StrWithBreak s) { +int check_fields_bounds(StrWithBreak s) { assert(s.b1 >= 0 && s.b1 <= 127); assert(s.breaking >= LLONG_MIN && s.breaking <= LLONG_MAX); assert(s.b2 >= -65536 && s.b2 <= 65535); @@ -116,3 +117,32 @@ int checkFieldsBounds(StrWithBreak s) { } return 4; } + +void simple_modify(SimpleSignedStr* s) { + s->a++; + s->b = ~s->b; + if (s->c >= 0) { + s->c *= 2; + } + s->d /= 2; +} + +SimpleSignedStr* create_on_heap(int a, int b, int c, int d) { + SimpleSignedStr* s = malloc(sizeof(SimpleSignedStr)); + if (s) { + s->a = s->b = s->c = s->d = -1; + if (a >= -8388608 && a <= 8388607) { + s->a = a; + } + if (b >= -1 && b <= 0) { + s->b = b; + } + if (c >= -2 && c <= 1) { + s->c = c; + } + if (d >= -16 && d <= 15) { + s->d = d; + } + } + return s; +} diff --git a/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp b/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp new file mode 100644 index 000000000..1b7c1aa72 --- /dev/null +++ b/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp @@ -0,0 +1,36 @@ +#include "bitfields.h" +#include +#include +#include + +static int ALIGN = -30; + +#define print_sizeof(S) printf("size of %*s : %zu bytes\n", ALIGN, #S, sizeof((S){})) + +void print_sizeof_structs() { + print_sizeof(CppSpecificStruct); +} + +CppSpecificStruct modify_and_return_copy(CppSpecificStruct& s) { + assert(s.b1 >= 0 && s.b1 <= 127); + assert(s.breaking >= std::numeric_limits::min()); + assert(s.breaking <= std::numeric_limits::max()); + assert(s.b2 >= -65536 && s.b2 <= 65535); + assert(s.b4 >= -2097152 && s.b4 <= 2097151); + assert(s.b5 >= std::numeric_limits::min()); + assert(s.b5 <= std::numeric_limits::max()); + assert(s.b6 >= -8 && s.b6 <= 7); + s.b1 = s.b1 * 2 + 1; + s.breaking = std::numeric_limits::max(); + if (s.b2 != -65536) { + s.b2 = -s.b2; + } + s.b3 ^= 1; + if (s.b5 > 0) { + s.b5 = 10; + } else { + s.b5 = -10; + } + s.b6 = (s.b1 > 0) + (s.b2 > 0) + (s.b3 > 0) + (s.b4 > 0) + (s.b5 > 0) + (s.b6 > 0); + return CppSpecificStruct(s); +} diff --git a/integration-tests/cpp-example/cxx_lib/structs/bitfields.h b/integration-tests/cpp-example/cxx_lib/structs/bitfields.h new file mode 100644 index 000000000..268c6f09a --- /dev/null +++ b/integration-tests/cpp-example/cxx_lib/structs/bitfields.h @@ -0,0 +1,29 @@ +#ifndef CPP_EXAMPLE_BITFIELDS_H +#define CPP_EXAMPLE_BITFIELDS_H + +struct CppSpecificStruct { + // will **usually** occupy 40 bytes: + // 7 bits: value of b1 + // 57 bits: unused + // 64 bits: value of breaking + // 17 bits: value of b2 + // 1 bit: value of b3 + // 22 bits: value of b4 + // 24 bits: unused + // 32 bits: value of b5 + // 34 bits: unused + // 4 bits: value of b6 + // 58 bits: unused + unsigned b1 : 7; // from 0 to 127 + long long breaking; // from LLONG_MIN to LLONG_MAX + signed b2 : 17; // from -65536 to 65535 + bool b3 : 1; // from 0 to 1 + int b4 : 22; // from -2097152 to 2097151 + unsigned : 0; // starts a new allocation unit + signed b5 : 66; // from INT_MIN to INT_MAX + signed b6 : 4; // from -8 to 7 +}; + +CppSpecificStruct modify_and_return_copy(CppSpecificStruct& s); + +#endif //CPP_EXAMPLE_BITFIELDS_H From 836ded82f63a71f1cc1c49bac6492c286433d637 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Thu, 1 Sep 2022 20:17:30 +0300 Subject: [PATCH 17/19] unit tests added --- .../cpp-example/cxx_lib/structs/bitfields.cpp | 2 +- .../structs/{bitfields.h => bitfields.hpp} | 0 server/src/Tests.cpp | 21 ++- server/src/utils/StringUtils.cpp | 46 ++++++ server/src/utils/StringUtils.h | 5 + server/test/framework/Syntax_Tests.cpp | 115 ++++++++++++++ server/test/suites/syntax/CMakeLists.txt | 3 +- server/test/suites/syntax/bitfields.c | 148 ++++++++++++++++++ server/test/suites/syntax/bitfields.h | 103 ++++++++++++ 9 files changed, 430 insertions(+), 13 deletions(-) rename integration-tests/cpp-example/cxx_lib/structs/{bitfields.h => bitfields.hpp} (100%) create mode 100644 server/test/suites/syntax/bitfields.c create mode 100644 server/test/suites/syntax/bitfields.h diff --git a/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp b/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp index 1b7c1aa72..8b5a60794 100644 --- a/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp +++ b/integration-tests/cpp-example/cxx_lib/structs/bitfields.cpp @@ -1,4 +1,4 @@ -#include "bitfields.h" +#include "bitfields.hpp" #include #include #include diff --git a/integration-tests/cpp-example/cxx_lib/structs/bitfields.h b/integration-tests/cpp-example/cxx_lib/structs/bitfields.hpp similarity index 100% rename from integration-tests/cpp-example/cxx_lib/structs/bitfields.h rename to integration-tests/cpp-example/cxx_lib/structs/bitfields.hpp diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 95c3a073e..bfa8a0f64 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -4,6 +4,7 @@ #include "exceptions/UnImplementedException.h" #include "printers/TestsPrinter.h" #include "utils/KleeUtils.h" +#include "utils/StringUtils.h" #include "loguru.h" @@ -481,14 +482,8 @@ namespace { //Predicate utilities. } } - bool stobool(const std::string& s) { - if (s == "false") return false; - if (s == "true") return true; - ABORT_F("Wrong bool value: %s", s.c_str()); - } - bool predicateMatch(const std::string &value, const LineInfo::PredicateInfo &info) { - switch(info.type) { + switch (info.type) { case testsgen::CHAR: return compareSimpleValues(info.predicate, value, "\'" + info.returnValue + "\'"); case testsgen::STRING: @@ -497,16 +492,20 @@ namespace { //Predicate utilities. case testsgen::INT16_T: case testsgen::INT32_T: case testsgen::INT64_T: - return compareSimpleValues(info.predicate, std::stoll(value), std::stoll(info.returnValue)); + return compareSimpleValues(info.predicate, StringUtils::stot(value), + StringUtils::stot(info.returnValue)); case testsgen::UINT8_T: case testsgen::UINT16_T: case testsgen::UINT32_T: case testsgen::UINT64_T: - return compareSimpleValues(info.predicate, std::stoull(value), std::stoull(info.returnValue)); + return compareSimpleValues(info.predicate, StringUtils::stot(value), + StringUtils::stot(info.returnValue)); case testsgen::BOOL: - return compareSimpleValues(info.predicate, stobool(value), stobool(info.returnValue)); + return compareSimpleValues(info.predicate, StringUtils::stot(value), + StringUtils::stot(info.returnValue)); case testsgen::FLOAT: - return compareSimpleValues(info.predicate, std::stof(value), std::stof(info.returnValue)); + return compareSimpleValues(info.predicate, StringUtils::stot(value), + StringUtils::stot(info.returnValue)); default: ABORT_F("Unsupported ValidationType: %s", ValidationType_Name(info.type).c_str()); } diff --git a/server/src/utils/StringUtils.cpp b/server/src/utils/StringUtils.cpp index f9bb3ceaf..01e893307 100644 --- a/server/src/utils/StringUtils.cpp +++ b/server/src/utils/StringUtils.cpp @@ -150,4 +150,50 @@ namespace StringUtils { s.erase(std::remove(s.begin(), s.end(), '\n'), s.end()); } + template<> + int stot<>(const std::string& s) { + return std::stoi(s); + } + template<> + long stot(const std::string& s) { + return std::stol(s); + } + template<> + long long stot(const std::string& s) { + return std::stoll(s); + } + template<> + unsigned int stot(const std::string& s) { + return std::stoul(s); + } + template<> + unsigned long stot(const std::string& s) { + return std::stoul(s); + } + template<> + unsigned long long stot(const std::string& s) { + return std::stoull(s); + } + template<> + float stot(const std::string& s) { + return std::stof(s); + } + template<> + double stot(const std::string& s) { + return std::stod(s); + } + template<> + long double stot(const std::string& s) { + return std::stold(s); + } + template<> + bool stot(const std::string& s) { + if (s == "false") { + return false; + } else if (s == "true") { + return true; + } + throw std::invalid_argument("Wrong bool value: " + s); + } + } diff --git a/server/src/utils/StringUtils.h b/server/src/utils/StringUtils.h index 3e24a7587..58439274d 100644 --- a/server/src/utils/StringUtils.h +++ b/server/src/utils/StringUtils.h @@ -80,6 +80,11 @@ namespace StringUtils { std::string repeat(const std::string &s, int n); bool contains(std::string_view s, std::string_view t); + + template + T stot(const std::string&) { + return T(); + } } #endif //UNITTESTBOT_STRINGUTIL_H diff --git a/server/test/framework/Syntax_Tests.cpp b/server/test/framework/Syntax_Tests.cpp index 71b220b43..fb1023a8f 100644 --- a/server/test/framework/Syntax_Tests.cpp +++ b/server/test/framework/Syntax_Tests.cpp @@ -8,6 +8,9 @@ #include "coverage/CoverageAndResultsGenerator.h" #include "utils/path/FileSystemPath.h" +#include "utils/StringUtils.h" +#include "utils/SizeUtils.h" +#include "Tests.h" #include namespace { @@ -52,6 +55,7 @@ namespace { fs::path stubs_c = getTestFilePath("stubs.c"); fs::path namespace_cpp = getTestFilePath("namespace.cpp"); fs::path input_output_c = getTestFilePath("input_output.c"); + fs::path bitfields_c = getTestFilePath("bitfields.c"); void SetUp() override { clearEnv(CompilationUtils::CompilerName::CLANG); @@ -3012,4 +3016,115 @@ namespace { }) ); } + template + bool checkBitfieldFit(const std::shared_ptr &fieldView, size_t size) { + T val = StringUtils::stot(fieldView->getEntryValue(nullptr)); + T minVal, maxVal, one = 1; + if constexpr (std::is_signed_v) { + minVal = -(one << (size - 1)); + maxVal = -(minVal + 1); + } else { + minVal = 0; + maxVal = (one << size) - 1; + } + return val >= minVal && val <= maxVal; + } + + TEST_F(Syntax_Test, bitfields_check_simple_signed_str) { + auto [testGen, status] = createTestForFunction(bitfields_c, 26); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + checkTestCasePredicates( + testGen.tests.at(bitfields_c).methods.begin().value().testCases, + std::vector( + { + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 24) && + checkBitfieldFit(subViews[1], 1) && + checkBitfieldFit(subViews[2], 2) && + checkBitfieldFit(subViews[3], 5) && + testCase.returnValue.view->getEntryValue(nullptr) == "1"; + }, + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 24) && + checkBitfieldFit(subViews[1], 1) && + checkBitfieldFit(subViews[2], 2) && + checkBitfieldFit(subViews[3], 5) && + testCase.returnValue.view->getEntryValue(nullptr) == "-1"; + }, + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 24) && + checkBitfieldFit(subViews[1], 1) && + checkBitfieldFit(subViews[2], 2) && + checkBitfieldFit(subViews[3], 5) && + testCase.returnValue.view->getEntryValue(nullptr) == "0"; + } + }) + ); + } + + TEST_F(Syntax_Test, bitfields_check_fields_bounds) { + auto [testGen, status] = createTestForFunction(bitfields_c, 106); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + for (const auto &testCase: testGen.tests.at(bitfields_c).methods.begin().value().testCases) { + ASSERT_EQ(testCase.suiteName, tests::Tests::DEFAULT_SUITE_NAME); + } + + checkTestCasePredicates( + testGen.tests.at(bitfields_c).methods.begin().value().testCases, + std::vector( + { + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 7) && + checkBitfieldFit(subViews[1], + SizeUtils::bytesToBits( + sizeof(long long))) && + checkBitfieldFit(subViews[2], 17) && + checkBitfieldFit(subViews[3], 1) && + checkBitfieldFit(subViews[4], 22) && + testCase.returnValue.view->getEntryValue(nullptr) == "1"; + }, + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 7) && + checkBitfieldFit(subViews[1], + SizeUtils::bytesToBits( + sizeof(long long))) && + checkBitfieldFit(subViews[2], 17) && + checkBitfieldFit(subViews[3], 1) && + checkBitfieldFit(subViews[4], 22) && + testCase.returnValue.view->getEntryValue(nullptr) == "2"; + }, + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 7) && + checkBitfieldFit(subViews[1], + SizeUtils::bytesToBits( + sizeof(long long))) && + checkBitfieldFit(subViews[2], 17) && + checkBitfieldFit(subViews[3], 1) && + checkBitfieldFit(subViews[4], 22) && + testCase.returnValue.view->getEntryValue(nullptr) == "3"; + }, + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return checkBitfieldFit(subViews[0], 7) && + checkBitfieldFit(subViews[1], + SizeUtils::bytesToBits( + sizeof(long long))) && + checkBitfieldFit(subViews[2], 17) && + checkBitfieldFit(subViews[3], 1) && + checkBitfieldFit(subViews[4], 22) && + testCase.returnValue.view->getEntryValue(nullptr) == "4"; + } + }) + ); + } } diff --git a/server/test/suites/syntax/CMakeLists.txt b/server/test/suites/syntax/CMakeLists.txt index 885a3e828..d9f03df76 100644 --- a/server/test/suites/syntax/CMakeLists.txt +++ b/server/test/suites/syntax/CMakeLists.txt @@ -38,4 +38,5 @@ add_executable(syntax1 array_sort.c stubs.c namespace.cpp - input_output.c) + input_output.c + bitfields.c) diff --git a/server/test/suites/syntax/bitfields.c b/server/test/suites/syntax/bitfields.c new file mode 100644 index 000000000..30da29faf --- /dev/null +++ b/server/test/suites/syntax/bitfields.c @@ -0,0 +1,148 @@ +#include "bitfields.h" +#include +#include +#include +#include +#include + +static int ALIGN = -30; + +#define print_sizeof(S) printf("size of %*s : %zu bytes\n", ALIGN, #S, sizeof((S){})) + +void print_sizeof_structs() { + print_sizeof(SimpleSignedStr); + print_sizeof(SimpleUnsignedStr); + print_sizeof(ImplementationDefinedStr); + print_sizeof(PossiblySmallStr); + print_sizeof(SimpleUnsignedUnion); + print_sizeof(ComplexStr); + print_sizeof(StrWithBool); + print_sizeof(StrWithUnnamedBitfields); + print_sizeof(StrWithUnnamedZeroBitfield); + print_sizeof(StrWithBreak); +} + +int check_simple_signed_str(SimpleSignedStr s) { + if (s.a == 1024 && s.b == -1 && s.d == -16) { + return 1; + } else if (s.b == 0) { + return -1; + } + return 0; +} + +int is_sum_greater_10(SimpleUnsignedStr s) { + return (s.a + s.b + s.c + s.d > 10 && s.d > 5); +} + +int is_all_greater_2(PossiblySmallStr s) { + if (s.a > 2 && s.b > 2) { + return 1; + } + return 0; +} + +int union_greater_2(SimpleUnsignedUnion u) { + if (u.c > 2) { + return 1; + } + return 0; +} + +int union_greater_2_short(SimpleUnsignedUnion u) { + return u.c > 2; +} + +int sum_of_fields(ComplexStr s) { + if (!s.a) { + return -1; + } + return s.a + s.b + s.c + s.d + s.e; +} + +int decode_from_bool(StrWithBool s) { + if (!s.a && !s.b && !s.c) { + return 0; + } else if (s.a && !s.b && !s.c) { + return 1; + } else if (!s.a && s.b && !s.c) { + return 2; + } else if (s.a && s.b && !s.c) { + return 3; + } else if (!s.a && !s.b && s.c) { + return 4; + } else if (s.a && !s.b && s.c) { + return 5; + } else if (!s.a && s.b && s.c) { + return 6; + } + return 7; +} + +int decode_from_bool_simple(StrWithBool s) { + if (!s.a && !s.b && !s.c) { + return 0; + } else if (s.a && !s.b && s.c) { + return 5; + } + return 7; +} + + StrWithUnnamedBitfields mult_by_two(StrWithUnnamedBitfields s) { + s.b1 *= 2; + s.b2 *= 2; + s.b3 *= 2; + return s; + } + + int is_nice(StrWithUnnamedZeroBitfield s) { + if (s.b1 == 69 && s.b2 == 42 && s.b3 == 1488) { + return 13; + } + return 0; + } + +int check_fields_bounds(StrWithBreak s) { + assert(s.b1 >= 0 && s.b1 <= 127); + assert(s.breaking >= LLONG_MIN && s.breaking <= LLONG_MAX); + assert(s.b2 >= -65536 && s.b2 <= 65535); + assert(s.b3 == true || s.b3 == false); + assert(s.b4 >= -2097152 && s.b4 <= 2097151); + if (s.b1 >= 123 && s.b3) { + return 1; + } else if (s.b1 >= 123 && s.b4 < 0) { + return 2; + } else if (s.breaking > 42) { + return 3; + } + return 4; +} + +void simple_modify(SimpleSignedStr* s) { + s->a++; + s->b = ~s->b; + if (s->c >= 0) { + s->c *= 2; + } + s->d /= 2; +} + +SimpleSignedStr* create_on_heap(int a, int b, int c, int d) { + SimpleSignedStr* s = malloc(sizeof(SimpleSignedStr)); + if (s) { + s->a = s->b = s->c = s->d = -1; + if (a >= -8388608 && a <= 8388607) { + s->a = a; + } + if (b >= -1 && b <= 0) { + s->b = b; + } + if (c >= -2 && c <= 1) { + s->c = c; + } + if (d >= -16 && d <= 15) { + s->d = d; + } + } + return s; +} diff --git a/server/test/suites/syntax/bitfields.h b/server/test/suites/syntax/bitfields.h new file mode 100644 index 000000000..07e873429 --- /dev/null +++ b/server/test/suites/syntax/bitfields.h @@ -0,0 +1,103 @@ +#ifndef C_EXAMPLE_BITFIELDS_H +#define C_EXAMPLE_BITFIELDS_H + +#include + +typedef struct { + signed a : 24; + signed b : 1; + signed int c : 2; + signed int d : 5; +} SimpleSignedStr; + +typedef struct { + unsigned a : 2; + unsigned b : 5; + unsigned int c : 1; + unsigned int d : 24; +} SimpleUnsignedStr; + +typedef struct { + int a : 24; + int b : 2; + int c : 1; + int d : 5; +} ImplementationDefinedStr; + +typedef struct { + signed a : 3; + signed b : 3; +} PossiblySmallStr; + +typedef union { + unsigned a : 2; + unsigned b : 1; + unsigned c : 6; + unsigned d : 23; +} SimpleUnsignedUnion; + +typedef struct { + // will **usually** occupy 8 bytes: + bool a : 1; + unsigned b : 2; + signed c : 1; + unsigned d : 3; + // 25 bits: unused + int e; +} ComplexStr; + +typedef struct { + // will **usually** occupy 1 byte + bool a : 1, b : 1, c : 1; +} StrWithBool; + +// struct InvalidNumberOfBits { // should produce an error in C +// bool a : 2; +// unsigned b : 50; +// signed c : 1; +// unsigned d : 3; +// }; + +typedef struct { + // will **usually** occupy 4 bytes: + // 5 bits: value of b1 + // 11 bits: unused -- explicitly specified padding + // 6 bits: value of b2 + // 2 bits: value of b3 + // 3 bits: unused -- explicitly specified padding + // 5 bits: unused -- implicitly + unsigned b1 : 5, : 11, b2 : 6, b3 : 2; + signed : 3; +} StrWithUnnamedBitfields; + +typedef struct { + // will **usually** occupy 8 bytes: + // 7 bits: value of b1 + // 25 bits: unused + // 6 bits: value of b2 + // 15 bits: value of b3 + // 11 bits: unused + unsigned b1 : 7; + unsigned : 0; // start a new allocation unit + unsigned b2 : 6; + unsigned b3 : 15; +} StrWithUnnamedZeroBitfield; + +typedef struct { + // will **usually** occupy 24 bytes: + // 7 bits: value of b1 + // 57 bits: unused + // 64 bits: value of breaking + // 17 bits: value of b2 + // 1 bit: value of b3 + // 22 bits: value of b4 + // 24 bits: unused + unsigned b1 : 7; // from 0 to 127 + long long breaking; // from LLONG_MIN to LLONG_MAX + signed b2 : 17; // from -65536 to 65535 + bool b3 : 1; // from 0 to 1 + int b4 : 22; // usually from -2097152 to 2097151 +} StrWithBreak; + + +#endif //C_EXAMPLE_BITFIELDS_H From a311eba5f94eeef084d3ea2dc290b86503d6c341 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Mon, 5 Sep 2022 13:38:54 +0300 Subject: [PATCH 18/19] unnamed bit fields fix --- server/src/Tests.cpp | 3 --- server/src/printers/KleeConstraintsPrinter.cpp | 4 ---- server/src/printers/TestsPrinter.cpp | 1 - server/src/types/Types.cpp | 4 ---- server/src/types/Types.h | 1 - server/src/types/TypesResolver.cpp | 3 +++ server/src/visitors/AbstractValueViewVisitor.cpp | 3 --- server/src/visitors/FunctionPointerForStubsVisitor.cpp | 3 +-- 8 files changed, 4 insertions(+), 18 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 355fdced1..e6acf5458 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -259,9 +259,6 @@ std::shared_ptr KTestObjectParser::structView(const std::vector std::vector> subViews; for (const auto &field: curStruct.fields) { - if (field.isUnnamedBitfield()) { - continue; - } size_t fieldLen = typesHandler.typeSize(field.type); size_t fieldOffset = offsetInBits + field.offset; diff --git a/server/src/printers/KleeConstraintsPrinter.cpp b/server/src/printers/KleeConstraintsPrinter.cpp index 93059121d..cc3d31430 100644 --- a/server/src/printers/KleeConstraintsPrinter.cpp +++ b/server/src/printers/KleeConstraintsPrinter.cpp @@ -119,10 +119,6 @@ void KleeConstraintsPrinter::genConstraintsForStruct(const ConstraintsState &sta StructInfo curStruct = typesHandler->getStructInfo(state.curType); bool isStruct = curStruct.subType == SubType::Struct; for (const auto &field : curStruct.fields) { - if (field.isUnnamedBitfield()) { - noConstraints("unnamed bit fields"); - continue; - } auto access = PrinterUtils::getFieldAccess(state.curElement, field); ConstraintsState newState = { state.paramName, access, diff --git a/server/src/printers/TestsPrinter.cpp b/server/src/printers/TestsPrinter.cpp index 47545e9f7..757757647 100644 --- a/server/src/printers/TestsPrinter.cpp +++ b/server/src/printers/TestsPrinter.cpp @@ -710,7 +710,6 @@ std::string printer::MultiLinePrinter::print(TestsPrinter *printer, const size_t longestFieldIndexForUnionInit = structInfo.longestFieldIndexForUnionInit; const bool isStruct = structInfo.subType == types::SubType::Struct; - bool firstField = true; size_t i = 0; for (const auto &sview : subViews) { if (i != 0) { diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index faa0806df..56890ccfe 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -488,10 +488,6 @@ bool types::TypesHandler::isStructLike(uint64_t id) const { return typeIsInMap(id, typeMaps.structs); } -bool types::Field::isUnnamedBitfield() const { - return name.empty() && TypesHandler::isPrimitiveType(type); -} - /* * Enum types */ diff --git a/server/src/types/Types.h b/server/src/types/Types.h index fbd4027e5..a73fd579e 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -270,7 +270,6 @@ namespace types { AS_none }; AccessSpecifier accessSpecifier = AS_pubic; - [[nodiscard]] bool isUnnamedBitfield() const; }; struct TypeInfo { diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index 4f7f0fe5f..549f516a9 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -125,6 +125,9 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin size_t i = 0; size_t maxFieldSize = 0; for (const clang::FieldDecl *F : D->fields()) { + if (F->isUnnamedBitfield()) { + continue; + } structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion(); types::Field field; field.name = F->getNameAsString(); diff --git a/server/src/visitors/AbstractValueViewVisitor.cpp b/server/src/visitors/AbstractValueViewVisitor.cpp index 151831990..b91b7a6b4 100644 --- a/server/src/visitors/AbstractValueViewVisitor.cpp +++ b/server/src/visitors/AbstractValueViewVisitor.cpp @@ -96,9 +96,6 @@ namespace visitor { inUnion = structInfo.subType == types::SubType::Union; for (int i = 0; i < structInfo.fields.size(); ++i) { auto const &field = structInfo.fields[i]; - if (field.isUnnamedBitfield()) { - continue; - } auto newName = PrinterUtils::getFieldAccess(name, field); auto const *newView = (subViews && i < subViews->size()) ? (*subViews)[i].get() : nullptr; auto newAccess = PrinterUtils::getFieldAccess(access, field); diff --git a/server/src/visitors/FunctionPointerForStubsVisitor.cpp b/server/src/visitors/FunctionPointerForStubsVisitor.cpp index 2d311f741..bae82b5a6 100644 --- a/server/src/visitors/FunctionPointerForStubsVisitor.cpp +++ b/server/src/visitors/FunctionPointerForStubsVisitor.cpp @@ -44,8 +44,7 @@ namespace visitor { } for (auto &field : structInfo.fields) { if (!types::TypesHandler::isPointerToFunction(field.type) && - !types::TypesHandler::isArrayOfPointersToFunction(field.type) && - !field.isUnnamedBitfield()) { + !types::TypesHandler::isArrayOfPointersToFunction(field.type)) { visitAny(field.type, name, nullptr, access, depth + 1); } } From 476a666c310b6cbacbcb4265277d8633210a5fd3 Mon Sep 17 00:00:00 2001 From: Danila Belous Date: Mon, 5 Sep 2022 13:56:58 +0300 Subject: [PATCH 19/19] unnamed bit fields unit tests added --- .../c-example/lib/structures/bitfields.h | 4 ++- server/test/framework/Syntax_Tests.cpp | 29 +++++++++++++++++++ server/test/suites/syntax/bitfields.h | 4 ++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/integration-tests/c-example/lib/structures/bitfields.h b/integration-tests/c-example/lib/structures/bitfields.h index 07e873429..6f57306d6 100644 --- a/integration-tests/c-example/lib/structures/bitfields.h +++ b/integration-tests/c-example/lib/structures/bitfields.h @@ -75,11 +75,13 @@ typedef struct { // 7 bits: value of b1 // 25 bits: unused // 6 bits: value of b2 + // 3 bits: unused // 15 bits: value of b3 - // 11 bits: unused + // 8 bits: unused unsigned b1 : 7; unsigned : 0; // start a new allocation unit unsigned b2 : 6; + unsigned : 3; unsigned b3 : 15; } StrWithUnnamedZeroBitfield; diff --git a/server/test/framework/Syntax_Tests.cpp b/server/test/framework/Syntax_Tests.cpp index dab0f5cd1..689415ec0 100644 --- a/server/test/framework/Syntax_Tests.cpp +++ b/server/test/framework/Syntax_Tests.cpp @@ -3166,4 +3166,33 @@ namespace { }) ); } + + TEST_F(Syntax_Test, bitfields_check_unnamed) { + auto [testGen, status] = createTestForFunction(bitfields_c, 99); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + checkTestCasePredicates( + testGen.tests.at(bitfields_c).methods.begin().value().testCases, + std::vector( + { + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return subViews.size() == 3 && + checkBitfieldFit(subViews[0], 7) && + checkBitfieldFit(subViews[1], 6) && + checkBitfieldFit(subViews[2], 15) && + testCase.returnValue.view->getEntryValue(nullptr) == "0"; + }, + [](const tests::Tests::MethodTestCase &testCase) { + auto &subViews = testCase.paramValues.front().view->getSubViews(); + return subViews.size() == 3 && + checkBitfieldFit(subViews[0], 7) && + checkBitfieldFit(subViews[1], 6) && + checkBitfieldFit(subViews[2], 15) && + testCase.returnValue.view->getEntryValue(nullptr) == "13"; + } + }) + ); + } } diff --git a/server/test/suites/syntax/bitfields.h b/server/test/suites/syntax/bitfields.h index 07e873429..6f57306d6 100644 --- a/server/test/suites/syntax/bitfields.h +++ b/server/test/suites/syntax/bitfields.h @@ -75,11 +75,13 @@ typedef struct { // 7 bits: value of b1 // 25 bits: unused // 6 bits: value of b2 + // 3 bits: unused // 15 bits: value of b3 - // 11 bits: unused + // 8 bits: unused unsigned b1 : 7; unsigned : 0; // start a new allocation unit unsigned b2 : 6; + unsigned : 3; unsigned b3 : 15; } StrWithUnnamedZeroBitfield;