This commit is contained in:
Peter Matula 2019-11-12 12:54:19 +01:00
parent 0505be4929
commit 334ad6083b
18 changed files with 243 additions and 170 deletions

View File

@ -23,7 +23,7 @@ class Class
{ {
public: public:
std::string dump() const; std::string dump() const;
retdec::config::Class getConfigClass( retdec::common::Class getConfigClass(
llvm::Module* m, llvm::Module* m,
Config* config) const; Config* config) const;

View File

@ -1,19 +1,18 @@
/** /**
* @file include/retdec/config/classes.h * @file include/retdec/common/class.h
* @brief Decompilation configuration manipulation: classes. * @brief Common class representation.
* @copyright (c) 2017 Avast Software, licensed under the MIT license * @copyright (c) 2019 Avast Software, licensed under the MIT license
*/ */
#ifndef RETDEC_CONFIG_CLASSES_H #ifndef RETDEC_COMMON_CLASS_H
#define RETDEC_CONFIG_CLASSES_H #define RETDEC_COMMON_CLASS_H
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include "retdec/config/base.h"
namespace retdec { namespace retdec {
namespace config { namespace common {
/** /**
* Represents C++ class. * Represents C++ class.
@ -22,10 +21,7 @@ namespace config {
class Class class Class
{ {
public: public:
explicit Class(const std::string& className); Class(const std::string& className = std::string());
static Class fromJsonValue(const Json::Value& val);
Json::Value getJsonValue() const;
/// @name Class get methods. /// @name Class get methods.
/// @{ /// @{
@ -42,6 +38,7 @@ class Class
/// @name Class set methods. /// @name Class set methods.
/// @{ /// @{
void setName(const std::string& name);
void setDemangledName(const std::string& demangledName); void setDemangledName(const std::string& demangledName);
/// @} /// @}
@ -70,12 +67,26 @@ class Class
/** /**
* A set container with classes. * A set container with classes.
*/ */
class ClassContainer : public BaseSetContainer<Class> 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<Class, ClassCompare>;
} // namespace config } // namespace common
} // namespace retdec } // namespace retdec
#endif #endif

View File

@ -8,13 +8,13 @@
#define RETDEC_CONFIG_CONFIG_H #define RETDEC_CONFIG_CONFIG_H
#include "retdec/common/architecture.h" #include "retdec/common/architecture.h"
#include "retdec/common/class.h"
#include "retdec/common/file_format.h" #include "retdec/common/file_format.h"
#include "retdec/common/file_type.h" #include "retdec/common/file_type.h"
#include "retdec/common/language.h" #include "retdec/common/language.h"
#include "retdec/common/tool_info.h" #include "retdec/common/tool_info.h"
#include "retdec/common/vtable.h" #include "retdec/common/vtable.h"
#include "retdec/config/base.h" #include "retdec/config/base.h"
#include "retdec/config/classes.h"
#include "retdec/config/functions.h" #include "retdec/config/functions.h"
#include "retdec/config/parameters.h" #include "retdec/config/parameters.h"
#include "retdec/config/patterns.h" #include "retdec/config/patterns.h"
@ -86,7 +86,7 @@ class Config
RegisterContainer registers; RegisterContainer registers;
TypeContainer structures; TypeContainer structures;
common::VtableContainer vtables; common::VtableContainer vtables;
ClassContainer classes; common::ClassContainer classes;
PatternContainer patterns; PatternContainer patterns;
private: private:

View File

@ -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 <json/json.h>
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

View File

@ -52,11 +52,11 @@ std::string Class::dump() const
return out.str(); return out.str();
} }
retdec::config::Class Class::getConfigClass( retdec::common::Class Class::getConfigClass(
llvm::Module* m, llvm::Module* m,
Config* config) const Config* config) const
{ {
retdec::config::Class c(name); retdec::common::Class c(name);
auto* demangler = DemanglerProvider::getDemangler(m); auto* demangler = DemanglerProvider::getDemangler(m);
if (demangler) if (demangler)

View File

@ -3,6 +3,7 @@ add_library(retdec-common STATIC
address.cpp address.cpp
architecture.cpp architecture.cpp
calling_convention.cpp calling_convention.cpp
class.cpp
file_format.cpp file_format.cpp
file_type.cpp file_type.cpp
language.cpp language.cpp

View File

@ -1,30 +1,16 @@
/** /**
* @file src/config/classes.cpp * @file src/common/class.cpp
* @brief Decompilation configuration manipulation: classes. * @brief Common class representation.
* @copyright (c) 2017 Avast Software, licensed under the MIT license * @copyright (c) 2019 Avast Software, licensed under the MIT license
*/ */
#include "retdec/config/classes.h" #include "retdec/common/class.h"
#include "retdec/serdes/std.h"
#include "retdec/utils/container.h" #include "retdec/utils/container.h"
using retdec::utils::hasItem; 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 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. * @return Class's ID is its name.
*/ */
@ -103,6 +47,11 @@ const std::vector<std::string>& Class::getSuperClasses() const
return _superClasses; return _superClasses;
} }
void Class::setName(const std::string& name)
{
_name = name;
}
void Class::setDemangledName(const std::string& demangledName) void Class::setDemangledName(const std::string& demangledName)
{ {
_demangledName = demangledName; _demangledName = demangledName;
@ -187,5 +136,5 @@ bool Class::operator==(const Class& o) const
return getName() == o.getName(); return getName() == o.getName();
} }
} // namespace config } // namespace common
} // namespace retdec } // namespace retdec

View File

@ -1,7 +1,6 @@
add_library(retdec-config STATIC add_library(retdec-config STATIC
base.cpp base.cpp
classes.cpp
config.cpp config.cpp
functions.cpp functions.cpp
objects.cpp objects.cpp

View File

@ -9,6 +9,7 @@
#include "retdec/config/config.h" #include "retdec/config/config.h"
#include "retdec/serdes/address.h" #include "retdec/serdes/address.h"
#include "retdec/serdes/architecture.h" #include "retdec/serdes/architecture.h"
#include "retdec/serdes/class.h"
#include "retdec/serdes/file_format.h" #include "retdec/serdes/file_format.h"
#include "retdec/serdes/file_type.h" #include "retdec/serdes/file_type.h"
#include "retdec/serdes/language.h" #include "retdec/serdes/language.h"
@ -183,7 +184,7 @@ std::string Config::generateJsonString() const
root[JSON_registers] = registers.getJsonValue(); root[JSON_registers] = registers.getJsonValue();
root[JSON_structures] = structures.getJsonValue(); root[JSON_structures] = structures.getJsonValue();
root[JSON_vtables] = serdes::serialize(vtables); root[JSON_vtables] = serdes::serialize(vtables);
root[JSON_classes] = classes.getJsonValue(); root[JSON_classes] = serdes::serialize(classes);
root[JSON_patterns] = patterns.getJsonValue(); root[JSON_patterns] = patterns.getJsonValue();
StreamWriterBuilder builder; StreamWriterBuilder builder;
@ -257,7 +258,7 @@ void Config::readJsonString(const std::string& json)
registers.readJsonValue( root[JSON_registers] ); registers.readJsonValue( root[JSON_registers] );
structures.readJsonValue( root[JSON_structures] ); structures.readJsonValue( root[JSON_structures] );
serdes::deserialize(root[JSON_vtables], vtables); serdes::deserialize(root[JSON_vtables], vtables);
classes.readJsonValue( root[JSON_classes] ); serdes::deserialize(root[JSON_classes], classes);
patterns.readJsonValue( root[JSON_patterns] ); patterns.readJsonValue( root[JSON_patterns] );
} }
catch (const InternalException& e) catch (const InternalException& e)

View File

@ -35,8 +35,8 @@ struct JSONConfig::Impl {
const retdec::config::Function *getConfigFunctionByName(const std::string &name) const; const retdec::config::Function *getConfigFunctionByName(const std::string &name) const;
const retdec::config::Function &getConfigFunctionByNameOrEmptyFunction( const retdec::config::Function &getConfigFunctionByNameOrEmptyFunction(
const std::string &name) const; const std::string &name) const;
const retdec::config::Class *getConfigClassByName(const std::string &name) const; const retdec::common::Class *getConfigClassByName(const std::string &name) const;
const retdec::config::Class &getConfigClassByNameOrEmptyClass( const retdec::common::Class &getConfigClassByNameOrEmptyClass(
const std::string &name) const; const std::string &name) const;
std::string getNameOfRegister(const retdec::config::Object &reg) const; std::string getNameOfRegister(const retdec::config::Object &reg) const;
@ -85,9 +85,10 @@ const retdec::config::Function &JSONConfig::Impl::getConfigFunctionByNameOrEmpty
return f ? *f : emptyFunction; return f ? *f : emptyFunction;
} }
const retdec::config::Class *JSONConfig::Impl::getConfigClassByName( const retdec::common::Class *JSONConfig::Impl::getConfigClassByName(
const std::string &name) const { 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 &reg) const { std::string JSONConfig::Impl::getNameOfRegister(const retdec::config::Object &reg) const {
@ -105,9 +106,9 @@ std::string JSONConfig::Impl::getNameOfRegister(const retdec::config::Object &re
return !realName.empty() ? realName : name; return !realName.empty() ? realName : name;
} }
const retdec::config::Class &JSONConfig::Impl::getConfigClassByNameOrEmptyClass( const retdec::common::Class &JSONConfig::Impl::getConfigClassByNameOrEmptyClass(
const std::string &name) const { const std::string &name) const {
static const retdec::config::Class emptyClass(""s); static const retdec::common::Class emptyClass(""s);
auto c = getConfigClassByName(name); auto c = getConfigClassByName(name);
return c ? *c : emptyClass; return c ? *c : emptyClass;
} }

View File

@ -3,6 +3,7 @@ add_library(retdec-serdes STATIC
address.cpp address.cpp
architecture.cpp architecture.cpp
calling_convention.cpp calling_convention.cpp
class.cpp
file_format.cpp file_format.cpp
file_type.cpp file_type.cpp
language.cpp language.cpp

80
src/serdes/class.cpp Normal file
View File

@ -0,0 +1,80 @@
/**
* @file src/serdes/class.h
* @brief Class (de)serialization.
* @copyright (c) 2019 Avast Software, licensed under the MIT license
*/
#include <algorithm>
#include <vector>
#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<std::string> superClasses;
serdes::deserialize(val[JSON_superClasses], superClasses);
for (auto& s : superClasses)
{
c.addSuperClass(s);
}
}
} // namespace serdes
} // namespace retdec

View File

@ -2,6 +2,7 @@
add_executable(retdec-tests-common add_executable(retdec-tests-common
address_tests.cpp address_tests.cpp
architecture_tests.cpp architecture_tests.cpp
class_tests.cpp
file_format_tests.cpp file_format_tests.cpp
file_type_tests.cpp file_type_tests.cpp
language_tests.cpp language_tests.cpp

View File

@ -1,96 +1,22 @@
/** /**
* @file tests/config/classes_tests.cpp * @file tests/config/classes_tests.cpp
* @brief Tests for the @c classes module. * @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 <gtest/gtest.h> #include <gtest/gtest.h>
#include "retdec/config/classes.h" #include "retdec/common/class.h"
#include "retdec/config/config.h"
using namespace ::testing; using namespace ::testing;
using namespace std::string_literals; using namespace std::string_literals;
namespace retdec { namespace retdec {
namespace config { namespace common {
namespace tests { namespace tests {
class ClassesTests: public Test {}; 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<std::string>({"Actor"}), cl.constructors);
EXPECT_EQ(std::set<std::string>({"Adtor"}), cl.destructors);
EXPECT_EQ(std::set<std::string>({"Amethod"}), cl.methods);
EXPECT_EQ(std::vector<std::string>({"Asuper"}), cl.getSuperClasses());
EXPECT_EQ(std::set<std::string>({"Avirtual"}), cl.virtualMethods);
EXPECT_EQ(std::set<std::string>({"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() // hasConstructor()
// //
@ -267,5 +193,5 @@ AddSuperClassDoesNotAddSameSuperclassTwice) {
} }
} // namespace tests } // namespace tests
} // namespace config } // namespace common
} // namespace retdec } // namespace retdec

View File

@ -1,7 +1,6 @@
add_executable(retdec-tests-config add_executable(retdec-tests-config
base_tests.cpp base_tests.cpp
classes_tests.cpp
documentation_tests.cpp documentation_tests.cpp
functions_tests.cpp functions_tests.cpp
objects_tests.cpp objects_tests.cpp

View File

@ -77,7 +77,7 @@ TEST_F(ConfigTests, SuccessfulReadJsonStringResetsAllConfigData)
TEST_F(ConfigTests, ClassesGetElementByIdReturnsNullPointerWhenThereIsNoSuchClass) TEST_F(ConfigTests, ClassesGetElementByIdReturnsNullPointerWhenThereIsNoSuchClass)
{ {
ASSERT_EQ(nullptr, config.classes.getElementById("ClassName")); ASSERT_EQ(config.classes.end(), config.classes.find("ClassName"));
} }
} // namespace tests } // namespace tests

View File

@ -1,6 +1,7 @@
add_executable(retdec-tests-serdes add_executable(retdec-tests-serdes
calling_convention_tests.cpp calling_convention_tests.cpp
class_tests.cpp
) )
target_link_libraries(retdec-tests-serdes target_link_libraries(retdec-tests-serdes
retdec-serdes retdec-serdes

View File

@ -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 <gtest/gtest.h>
#include <json/json.h>
#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<std::string>({"Actor"}), cl.constructors);
EXPECT_EQ(std::set<std::string>({"Adtor"}), cl.destructors);
EXPECT_EQ(std::set<std::string>({"Amethod"}), cl.methods);
EXPECT_EQ(std::vector<std::string>({"Asuper"}), cl.getSuperClasses());
EXPECT_EQ(std::set<std::string>({"Avirtual"}), cl.virtualMethods);
EXPECT_EQ(std::set<std::string>({"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