From 334ad6083b1500c55688034d306778120dc1a38c Mon Sep 17 00:00:00 2001 From: Peter Matula Date: Tue, 12 Nov 2019 12:54:19 +0100 Subject: [PATCH] progress --- .../optimizations/class_hierarchy/hierarchy.h | 2 +- .../{config/classes.h => common/class.h} | 39 +++++---- include/retdec/config/config.h | 4 +- include/retdec/serdes/class.h | 26 ++++++ .../class_hierarchy/hierarchy.cpp | 4 +- src/common/CMakeLists.txt | 1 + src/{config/classes.cpp => common/class.cpp} | 73 +++-------------- src/config/CMakeLists.txt | 1 - src/config/config.cpp | 5 +- src/llvmir2hll/config/configs/json_config.cpp | 13 +-- src/serdes/CMakeLists.txt | 1 + src/serdes/class.cpp | 80 ++++++++++++++++++ tests/common/CMakeLists.txt | 1 + .../class_tests.cpp} | 82 +------------------ tests/config/CMakeLists.txt | 1 - tests/config/config_tests.cpp | 2 +- tests/serdes/CMakeLists.txt | 1 + tests/serdes/class_tests.cpp | 77 +++++++++++++++++ 18 files changed, 243 insertions(+), 170 deletions(-) rename include/retdec/{config/classes.h => common/class.h} (65%) create mode 100644 include/retdec/serdes/class.h rename src/{config/classes.cpp => common/class.cpp} (53%) create mode 100644 src/serdes/class.cpp rename tests/{config/classes_tests.cpp => common/class_tests.cpp} (65%) create mode 100644 tests/serdes/class_tests.cpp diff --git a/include/retdec/bin2llvmir/optimizations/class_hierarchy/hierarchy.h b/include/retdec/bin2llvmir/optimizations/class_hierarchy/hierarchy.h index ba73de76..9b1b7863 100644 --- a/include/retdec/bin2llvmir/optimizations/class_hierarchy/hierarchy.h +++ b/include/retdec/bin2llvmir/optimizations/class_hierarchy/hierarchy.h @@ -23,7 +23,7 @@ class Class { public: std::string dump() const; - retdec::config::Class getConfigClass( + retdec::common::Class getConfigClass( llvm::Module* m, Config* config) const; diff --git a/include/retdec/config/classes.h b/include/retdec/common/class.h similarity index 65% rename from include/retdec/config/classes.h rename to include/retdec/common/class.h index 608da871..1a79910f 100644 --- a/include/retdec/config/classes.h +++ b/include/retdec/common/class.h @@ -1,19 +1,18 @@ /** - * @file include/retdec/config/classes.h - * @brief Decompilation configuration manipulation: classes. - * @copyright (c) 2017 Avast Software, licensed under the MIT license + * @file include/retdec/common/class.h + * @brief Common class representation. + * @copyright (c) 2019 Avast Software, licensed under the MIT license */ -#ifndef RETDEC_CONFIG_CLASSES_H -#define RETDEC_CONFIG_CLASSES_H +#ifndef RETDEC_COMMON_CLASS_H +#define RETDEC_COMMON_CLASS_H +#include #include #include -#include "retdec/config/base.h" - namespace retdec { -namespace config { +namespace common { /** * Represents C++ class. @@ -22,10 +21,7 @@ namespace config { class Class { public: - explicit Class(const std::string& className); - static Class fromJsonValue(const Json::Value& val); - - Json::Value getJsonValue() const; + Class(const std::string& className = std::string()); /// @name Class get methods. /// @{ @@ -42,6 +38,7 @@ class Class /// @name Class set methods. /// @{ + void setName(const std::string& name); void setDemangledName(const std::string& demangledName); /// @} @@ -70,12 +67,26 @@ class Class /** * A set container with classes. */ -class ClassContainer : public BaseSetContainer +struct ClassCompare { + using is_transparent = void; + bool operator()(const Class& c1, const Class& c2) const + { + return c1 < c2; + } + bool operator()(const std::string& id, Class const& c) const + { + return id < c.getName(); + } + bool operator()(const Class& c, const std::string& id) const + { + return c.getName() < id; + } }; +using ClassContainer = std::set; -} // namespace config +} // namespace common } // namespace retdec #endif diff --git a/include/retdec/config/config.h b/include/retdec/config/config.h index e5d5b7a1..9f11321e 100644 --- a/include/retdec/config/config.h +++ b/include/retdec/config/config.h @@ -8,13 +8,13 @@ #define RETDEC_CONFIG_CONFIG_H #include "retdec/common/architecture.h" +#include "retdec/common/class.h" #include "retdec/common/file_format.h" #include "retdec/common/file_type.h" #include "retdec/common/language.h" #include "retdec/common/tool_info.h" #include "retdec/common/vtable.h" #include "retdec/config/base.h" -#include "retdec/config/classes.h" #include "retdec/config/functions.h" #include "retdec/config/parameters.h" #include "retdec/config/patterns.h" @@ -86,7 +86,7 @@ class Config RegisterContainer registers; TypeContainer structures; common::VtableContainer vtables; - ClassContainer classes; + common::ClassContainer classes; PatternContainer patterns; private: diff --git a/include/retdec/serdes/class.h b/include/retdec/serdes/class.h new file mode 100644 index 00000000..e52086b8 --- /dev/null +++ b/include/retdec/serdes/class.h @@ -0,0 +1,26 @@ +/** + * @file include/retdec/serdes/class.h + * @brief Class (de)serialization. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_SERDES_CLASS_H +#define RETDEC_SERDES_CLASS_H + +#include + +namespace retdec { + +namespace common { +class Class; +} // namespace common + +namespace serdes { + +Json::Value serialize(const common::Class& c); +void deserialize(const Json::Value& val, common::Class& c); + +} // namespace serdes +} // namespace retdec + +#endif \ No newline at end of file diff --git a/src/bin2llvmir/optimizations/class_hierarchy/hierarchy.cpp b/src/bin2llvmir/optimizations/class_hierarchy/hierarchy.cpp index 4820cf98..44bc12f9 100644 --- a/src/bin2llvmir/optimizations/class_hierarchy/hierarchy.cpp +++ b/src/bin2llvmir/optimizations/class_hierarchy/hierarchy.cpp @@ -52,11 +52,11 @@ std::string Class::dump() const return out.str(); } -retdec::config::Class Class::getConfigClass( +retdec::common::Class Class::getConfigClass( llvm::Module* m, Config* config) const { - retdec::config::Class c(name); + retdec::common::Class c(name); auto* demangler = DemanglerProvider::getDemangler(m); if (demangler) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8301aa58..2c7cc03e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(retdec-common STATIC address.cpp architecture.cpp calling_convention.cpp + class.cpp file_format.cpp file_type.cpp language.cpp diff --git a/src/config/classes.cpp b/src/common/class.cpp similarity index 53% rename from src/config/classes.cpp rename to src/common/class.cpp index a4d7ed38..008dab44 100644 --- a/src/config/classes.cpp +++ b/src/common/class.cpp @@ -1,30 +1,16 @@ /** - * @file src/config/classes.cpp - * @brief Decompilation configuration manipulation: classes. - * @copyright (c) 2017 Avast Software, licensed under the MIT license + * @file src/common/class.cpp + * @brief Common class representation. + * @copyright (c) 2019 Avast Software, licensed under the MIT license */ -#include "retdec/config/classes.h" -#include "retdec/serdes/std.h" +#include "retdec/common/class.h" #include "retdec/utils/container.h" using retdec::utils::hasItem; -namespace { - -const std::string JSON_name = "name"; -const std::string JSON_demangledName = "demangledName"; -const std::string JSON_superClasses = "superClasses"; -const std::string JSON_virtualMethods = "virtualMethods"; -const std::string JSON_constructors = "constructors"; -const std::string JSON_destructors = "destructors"; -const std::string JSON_methods = "methods"; -const std::string JSON_vtables = "virtualTables"; - -} // anonymous namespace - namespace retdec { -namespace config { +namespace common { // //============================================================================= @@ -38,48 +24,6 @@ Class::Class(const std::string& className) : } -/** - * Reads JSON object (associative array) holding class information. - * @param val JSON object. - */ -Class Class::fromJsonValue(const Json::Value& val) -{ - checkJsonValueIsObject(val, "Class"); - - Class ret(safeGetString(val, JSON_name)); - - ret.setDemangledName(safeGetString(val, JSON_demangledName)); - serdes::deserialize(val[JSON_superClasses], ret._superClasses); - serdes::deserialize(val[JSON_virtualMethods], ret.virtualMethods); - serdes::deserialize(val[JSON_constructors], ret.constructors); - serdes::deserialize(val[JSON_destructors], ret.destructors); - serdes::deserialize(val[JSON_methods], ret.methods); - serdes::deserialize(val[JSON_vtables], ret.virtualTables); - - return ret; -} - -/** - * Returns JSON object (associative array) holding class information. - * @return JSON object. - */ -Json::Value Class::getJsonValue() const -{ - Json::Value val; - - if (!getName().empty()) val[JSON_name] = getName(); - if (!getDemangledName().empty()) val[JSON_demangledName] = getDemangledName(); - - val[JSON_superClasses] = serdes::serialize(_superClasses); - val[JSON_virtualMethods] = serdes::serialize(virtualMethods); - val[JSON_constructors] = serdes::serialize(constructors); - val[JSON_destructors] = serdes::serialize(destructors); - val[JSON_methods] = serdes::serialize(methods); - val[JSON_vtables] = serdes::serialize(virtualTables); - - return val; -} - /** * @return Class's ID is its name. */ @@ -103,6 +47,11 @@ const std::vector& Class::getSuperClasses() const return _superClasses; } +void Class::setName(const std::string& name) +{ + _name = name; +} + void Class::setDemangledName(const std::string& demangledName) { _demangledName = demangledName; @@ -187,5 +136,5 @@ bool Class::operator==(const Class& o) const return getName() == o.getName(); } -} // namespace config +} // namespace common } // namespace retdec diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index 4aa4c592..b92f93d3 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -1,7 +1,6 @@ add_library(retdec-config STATIC base.cpp - classes.cpp config.cpp functions.cpp objects.cpp diff --git a/src/config/config.cpp b/src/config/config.cpp index 7e6faaab..f8a93a85 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -9,6 +9,7 @@ #include "retdec/config/config.h" #include "retdec/serdes/address.h" #include "retdec/serdes/architecture.h" +#include "retdec/serdes/class.h" #include "retdec/serdes/file_format.h" #include "retdec/serdes/file_type.h" #include "retdec/serdes/language.h" @@ -183,7 +184,7 @@ std::string Config::generateJsonString() const root[JSON_registers] = registers.getJsonValue(); root[JSON_structures] = structures.getJsonValue(); root[JSON_vtables] = serdes::serialize(vtables); - root[JSON_classes] = classes.getJsonValue(); + root[JSON_classes] = serdes::serialize(classes); root[JSON_patterns] = patterns.getJsonValue(); StreamWriterBuilder builder; @@ -257,7 +258,7 @@ void Config::readJsonString(const std::string& json) registers.readJsonValue( root[JSON_registers] ); structures.readJsonValue( root[JSON_structures] ); serdes::deserialize(root[JSON_vtables], vtables); - classes.readJsonValue( root[JSON_classes] ); + serdes::deserialize(root[JSON_classes], classes); patterns.readJsonValue( root[JSON_patterns] ); } catch (const InternalException& e) diff --git a/src/llvmir2hll/config/configs/json_config.cpp b/src/llvmir2hll/config/configs/json_config.cpp index 89c2e31a..df3402b8 100644 --- a/src/llvmir2hll/config/configs/json_config.cpp +++ b/src/llvmir2hll/config/configs/json_config.cpp @@ -35,8 +35,8 @@ struct JSONConfig::Impl { const retdec::config::Function *getConfigFunctionByName(const std::string &name) const; const retdec::config::Function &getConfigFunctionByNameOrEmptyFunction( const std::string &name) const; - const retdec::config::Class *getConfigClassByName(const std::string &name) const; - const retdec::config::Class &getConfigClassByNameOrEmptyClass( + const retdec::common::Class *getConfigClassByName(const std::string &name) const; + const retdec::common::Class &getConfigClassByNameOrEmptyClass( const std::string &name) const; std::string getNameOfRegister(const retdec::config::Object ®) const; @@ -85,9 +85,10 @@ const retdec::config::Function &JSONConfig::Impl::getConfigFunctionByNameOrEmpty return f ? *f : emptyFunction; } -const retdec::config::Class *JSONConfig::Impl::getConfigClassByName( +const retdec::common::Class *JSONConfig::Impl::getConfigClassByName( const std::string &name) const { - return config.classes.getElementById(name); + auto it = config.classes.find(name); + return it != config.classes.end() ? &(*it) : nullptr; } std::string JSONConfig::Impl::getNameOfRegister(const retdec::config::Object ®) const { @@ -105,9 +106,9 @@ std::string JSONConfig::Impl::getNameOfRegister(const retdec::config::Object &re return !realName.empty() ? realName : name; } -const retdec::config::Class &JSONConfig::Impl::getConfigClassByNameOrEmptyClass( +const retdec::common::Class &JSONConfig::Impl::getConfigClassByNameOrEmptyClass( const std::string &name) const { - static const retdec::config::Class emptyClass(""s); + static const retdec::common::Class emptyClass(""s); auto c = getConfigClassByName(name); return c ? *c : emptyClass; } diff --git a/src/serdes/CMakeLists.txt b/src/serdes/CMakeLists.txt index 7a9d4770..5d58623b 100644 --- a/src/serdes/CMakeLists.txt +++ b/src/serdes/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(retdec-serdes STATIC address.cpp architecture.cpp calling_convention.cpp + class.cpp file_format.cpp file_type.cpp language.cpp diff --git a/src/serdes/class.cpp b/src/serdes/class.cpp new file mode 100644 index 00000000..73d62096 --- /dev/null +++ b/src/serdes/class.cpp @@ -0,0 +1,80 @@ +/** + * @file src/serdes/class.h + * @brief Class (de)serialization. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include +#include + +#include "retdec/common/class.h" +#include "retdec/serdes/class.h" +#include "retdec/serdes/std.h" + +#include "serdes/utils.h" + +namespace { + +const std::string JSON_name = "name"; +const std::string JSON_demangledName = "demangledName"; +const std::string JSON_superClasses = "superClasses"; +const std::string JSON_virtualMethods = "virtualMethods"; +const std::string JSON_constructors = "constructors"; +const std::string JSON_destructors = "destructors"; +const std::string JSON_methods = "methods"; +const std::string JSON_vtables = "virtualTables"; + +} // anonymous namespace + +namespace retdec { +namespace serdes { + +Json::Value serialize(const common::Class& c) +{ + Json::Value val; + + if (!c.getName().empty()) + { + val[JSON_name] = c.getName(); + } + if (!c.getDemangledName().empty()) + { + val[JSON_demangledName] = c.getDemangledName(); + } + + val[JSON_superClasses] = serdes::serialize(c.getSuperClasses()); + val[JSON_virtualMethods] = serdes::serialize(c.virtualMethods); + val[JSON_constructors] = serdes::serialize(c.constructors); + val[JSON_destructors] = serdes::serialize(c.destructors); + val[JSON_methods] = serdes::serialize(c.methods); + val[JSON_vtables] = serdes::serialize(c.virtualTables); + + return val; +} + +void deserialize(const Json::Value& val, common::Class& c) +{ + if (val.isNull() || !val.isObject()) + { + return; + } + + c.setName(safeGetString(val, JSON_name)); + c.setDemangledName(safeGetString(val, JSON_demangledName)); + + serdes::deserialize(val[JSON_virtualMethods], c.virtualMethods); + serdes::deserialize(val[JSON_constructors], c.constructors); + serdes::deserialize(val[JSON_destructors], c.destructors); + serdes::deserialize(val[JSON_methods], c.methods); + serdes::deserialize(val[JSON_vtables], c.virtualTables); + + std::vector superClasses; + serdes::deserialize(val[JSON_superClasses], superClasses); + for (auto& s : superClasses) + { + c.addSuperClass(s); + } +} + +} // namespace serdes +} // namespace retdec diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt index e62affc9..b38ad0e2 100644 --- a/tests/common/CMakeLists.txt +++ b/tests/common/CMakeLists.txt @@ -2,6 +2,7 @@ add_executable(retdec-tests-common address_tests.cpp architecture_tests.cpp + class_tests.cpp file_format_tests.cpp file_type_tests.cpp language_tests.cpp diff --git a/tests/config/classes_tests.cpp b/tests/common/class_tests.cpp similarity index 65% rename from tests/config/classes_tests.cpp rename to tests/common/class_tests.cpp index 62af4e42..a19fdb7e 100644 --- a/tests/config/classes_tests.cpp +++ b/tests/common/class_tests.cpp @@ -1,96 +1,22 @@ /** * @file tests/config/classes_tests.cpp * @brief Tests for the @c classes module. - * @copyright (c) 2017 Avast Software, licensed under the MIT license + * @copyright (c) 2019 Avast Software, licensed under the MIT license */ #include -#include "retdec/config/classes.h" -#include "retdec/config/config.h" +#include "retdec/common/class.h" using namespace ::testing; using namespace std::string_literals; namespace retdec { -namespace config { +namespace common { namespace tests { class ClassesTests: public Test {}; -// -// Parsing -// - -TEST_F(ClassesTests, -ClassesAreEmptyWhenConfigIsCreated) { - Config config; - ASSERT_TRUE(config.classes.empty()); -} - -TEST_F(ClassesTests, -NoClassesAreParsedCorrectly) { - Config config; - - config.readJsonString(R"({ - "classes" : [] - })"); - - ASSERT_TRUE(config.classes.empty()); -} - -TEST_F(ClassesTests, -ClassIsParsedCorrectlyFromJSONWhenClassIsFullySpecified) { - Config config; - - config.readJsonString(R"({ - "classes" : [ - { - "name" : "A", - "constructors" : [ "Actor" ], - "destructors" : [ "Adtor" ], - "methods" : [ "Amethod" ], - "superClasses" : [ "Asuper" ], - "virtualMethods" : [ "Avirtual" ], - "virtualTables" : [ "Avtable" ] - } - ] - })"); - - ASSERT_EQ(1, config.classes.size()); - auto cl = *config.classes.begin(); - EXPECT_EQ("A", cl.getName()); - EXPECT_EQ(std::set({"Actor"}), cl.constructors); - EXPECT_EQ(std::set({"Adtor"}), cl.destructors); - EXPECT_EQ(std::set({"Amethod"}), cl.methods); - EXPECT_EQ(std::vector({"Asuper"}), cl.getSuperClasses()); - EXPECT_EQ(std::set({"Avirtual"}), cl.virtualMethods); - EXPECT_EQ(std::set({"Avtable"}), cl.virtualTables); -} - -TEST_F(ClassesTests, -ClassIsParsedCorrectlyFromJSONWhenClassHasOnlyNameSpecified) { - Config config; - - config.readJsonString(R"({ - "classes" : [ - { - "name" : "A" - } - ] - })"); - - ASSERT_EQ(1, config.classes.size()); - auto cl = *config.classes.begin(); - EXPECT_EQ("A", cl.getName()); - EXPECT_TRUE(cl.constructors.empty()); - EXPECT_TRUE(cl.destructors.empty()); - EXPECT_TRUE(cl.methods.empty()); - EXPECT_TRUE(cl.getSuperClasses().empty()); - EXPECT_TRUE(cl.virtualMethods.empty()); - EXPECT_TRUE(cl.virtualTables.empty()); -} - // // hasConstructor() // @@ -267,5 +193,5 @@ AddSuperClassDoesNotAddSameSuperclassTwice) { } } // namespace tests -} // namespace config +} // namespace common } // namespace retdec diff --git a/tests/config/CMakeLists.txt b/tests/config/CMakeLists.txt index 84262450..56c7dcbd 100644 --- a/tests/config/CMakeLists.txt +++ b/tests/config/CMakeLists.txt @@ -1,7 +1,6 @@ add_executable(retdec-tests-config base_tests.cpp - classes_tests.cpp documentation_tests.cpp functions_tests.cpp objects_tests.cpp diff --git a/tests/config/config_tests.cpp b/tests/config/config_tests.cpp index 48273075..ae8ff08b 100644 --- a/tests/config/config_tests.cpp +++ b/tests/config/config_tests.cpp @@ -77,7 +77,7 @@ TEST_F(ConfigTests, SuccessfulReadJsonStringResetsAllConfigData) TEST_F(ConfigTests, ClassesGetElementByIdReturnsNullPointerWhenThereIsNoSuchClass) { - ASSERT_EQ(nullptr, config.classes.getElementById("ClassName")); + ASSERT_EQ(config.classes.end(), config.classes.find("ClassName")); } } // namespace tests diff --git a/tests/serdes/CMakeLists.txt b/tests/serdes/CMakeLists.txt index 8cba8c62..57e490c8 100644 --- a/tests/serdes/CMakeLists.txt +++ b/tests/serdes/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable(retdec-tests-serdes calling_convention_tests.cpp + class_tests.cpp ) target_link_libraries(retdec-tests-serdes retdec-serdes diff --git a/tests/serdes/class_tests.cpp b/tests/serdes/class_tests.cpp new file mode 100644 index 00000000..269a52f6 --- /dev/null +++ b/tests/serdes/class_tests.cpp @@ -0,0 +1,77 @@ +/** + * @file tests/serdes/classe_tests.cpp + * @brief Tests for the @c classes module. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include +#include + +#include "retdec/common/class.h" +#include "retdec/serdes/class.h" + +using namespace ::testing; + +namespace retdec { +namespace serdes { +namespace tests { + +class ClassTests : public Test +{ + protected: + common::Class cl; +}; + +TEST_F(ClassTests, ClassIsParsedCorrectlyFromJSONWhenClassIsFullySpecified) +{ + std::istringstream input(R"({ + "name" : "A", + "constructors" : [ "Actor" ], + "destructors" : [ "Adtor" ], + "methods" : [ "Amethod" ], + "superClasses" : [ "Asuper" ], + "virtualMethods" : [ "Avirtual" ], + "virtualTables" : [ "Avtable" ] + })"); + Json::Value val; + std::string errs; + Json::CharReaderBuilder rbuilder; + bool success = Json::parseFromStream(rbuilder, input, &val, &errs); + ASSERT_TRUE(success); + + deserialize(val, cl); + + EXPECT_EQ("A", cl.getName()); + EXPECT_EQ(std::set({"Actor"}), cl.constructors); + EXPECT_EQ(std::set({"Adtor"}), cl.destructors); + EXPECT_EQ(std::set({"Amethod"}), cl.methods); + EXPECT_EQ(std::vector({"Asuper"}), cl.getSuperClasses()); + EXPECT_EQ(std::set({"Avirtual"}), cl.virtualMethods); + EXPECT_EQ(std::set({"Avtable"}), cl.virtualTables); +} + +TEST_F(ClassTests, ClassIsParsedCorrectlyFromJSONWhenClassHasOnlyNameSpecified) +{ + std::istringstream input(R"({ + "name" : "A" + })"); + Json::Value val; + std::string errs; + Json::CharReaderBuilder rbuilder; + bool success = Json::parseFromStream(rbuilder, input, &val, &errs); + ASSERT_TRUE(success); + + deserialize(val, cl); + + EXPECT_EQ("A", cl.getName()); + EXPECT_TRUE(cl.constructors.empty()); + EXPECT_TRUE(cl.destructors.empty()); + EXPECT_TRUE(cl.methods.empty()); + EXPECT_TRUE(cl.getSuperClasses().empty()); + EXPECT_TRUE(cl.virtualMethods.empty()); + EXPECT_TRUE(cl.virtualTables.empty()); +} + +} // namespace tests +} // namespace serdes +} // namespace retdec