patterns: Huge refactor of Pattern Language runtime to use smart pointers (#458)

* patterns: Initial work to refactor pattern language to use smart pointers

* patterns: Fixed remaining issues, moved patterns to unique files

* sys: Added missing includes for macOS
This commit is contained in:
WerWolv
2022-02-27 23:25:39 +01:00
committed by GitHub
parent b28eaf2dbf
commit 66d1b3fd2f
85 changed files with 4990 additions and 5146 deletions

View File

@@ -1,9 +1,8 @@
project(unit_tests)
enable_testing()
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
add_custom_target(unit_tests)
add_custom_target(unit_tests DEPENDS helpers algorithms pattern_language)
add_subdirectory(common)
add_subdirectory(helpers)

View File

@@ -3,7 +3,7 @@
#include <string>
#include <vector>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#define TEST(name) (hex::test::TestPattern *)new hex::test::TestPattern##name()
@@ -11,7 +11,8 @@ namespace hex::test {
using namespace pl;
enum class Mode {
enum class Mode
{
Succeeding,
Failing
};
@@ -22,25 +23,22 @@ namespace hex::test {
TestPattern::s_tests.insert({ name, this });
}
virtual ~TestPattern() {
for (auto &pattern : this->m_patterns)
delete pattern;
}
virtual ~TestPattern() = default;
template<typename T>
static T *create(const std::string &typeName, const std::string &varName, auto... args) {
auto pattern = new T(nullptr, args...);
static std::unique_ptr<T> create(const std::string &typeName, const std::string &varName, auto... args) {
auto pattern = std::make_unique<T>(nullptr, args...);
pattern->setTypeName(typeName);
pattern->setVariableName(varName);
return pattern;
return std::move(pattern);
}
[[nodiscard]] virtual std::string getSourceCode() const = 0;
[[nodiscard]] virtual const std::vector<PatternData *> &getPatterns() const final { return this->m_patterns; }
virtual void addPattern(PatternData *pattern) final {
this->m_patterns.push_back(pattern);
[[nodiscard]] virtual const std::vector<std::unique_ptr<Pattern>> &getPatterns() const final { return this->m_patterns; }
virtual void addPattern(std::unique_ptr<Pattern> &&pattern) final {
this->m_patterns.push_back(std::move(pattern));
}
[[nodiscard]] auto failing() {
@@ -58,7 +56,7 @@ namespace hex::test {
}
private:
std::vector<PatternData *> m_patterns;
std::vector<std::unique_ptr<Pattern>> m_patterns;
Mode m_mode;
static inline std::map<std::string, TestPattern *> s_tests;

View File

@@ -2,19 +2,27 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
namespace hex::test {
class TestPatternBitfields : public TestPattern {
public:
TestPatternBitfields() : TestPattern("Bitfields") {
auto testBitfield = create<PatternDataBitfield>("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8);
auto testBitfield = create<PatternBitfield>("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8);
testBitfield->setEndian(std::endian::big);
testBitfield->setFields({ create<PatternDataBitfieldField>("", "a", 0x12, 0, 4, testBitfield),
create<PatternDataBitfieldField>("", "b", 0x12, 4, 4, testBitfield),
create<PatternDataBitfieldField>("", "c", 0x12, 8, 4, testBitfield),
create<PatternDataBitfieldField>("", "d", 0x12, 12, 4, testBitfield) });
addPattern(testBitfield);
std::vector<std::shared_ptr<hex::pl::Pattern>> bitfieldFields;
{
bitfieldFields.push_back(create<PatternBitfieldField>("", "a", 0x12, 0, 4, testBitfield.get()));
bitfieldFields.push_back(create<PatternBitfieldField>("", "b", 0x12, 4, 4, testBitfield.get()));
bitfieldFields.push_back(create<PatternBitfieldField>("", "c", 0x12, 8, 4, testBitfield.get()));
bitfieldFields.push_back(create<PatternBitfieldField>("", "d", 0x12, 12, 4, testBitfield.get()));
}
testBitfield->setFields(std::move(bitfieldFields));
addPattern(std::move(testBitfield));
}
~TestPatternBitfields() override = default;

View File

@@ -2,12 +2,14 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_enum.hpp>
namespace hex::test {
class TestPatternEnums : public TestPattern {
public:
TestPatternEnums() : TestPattern("Enums") {
auto testEnum = create<PatternDataEnum>("TestEnum", "testEnum", 0x08, sizeof(u32));
auto testEnum = create<PatternEnum>("TestEnum", "testEnum", 0x08, sizeof(u32));
testEnum->setEnumValues({
{u128(0x00), "A"},
{ i128(0x0C), "B"},
@@ -16,7 +18,7 @@ namespace hex::test {
});
testEnum->setEndian(std::endian::big);
addPattern(testEnum);
addPattern(std::move(testEnum));
}
~TestPatternEnums() override = default;

View File

@@ -2,21 +2,34 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_signed.hpp>
#include <hex/pattern_language/patterns/pattern_struct.hpp>
#include <hex/pattern_language/patterns/pattern_padding.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
namespace hex::test {
class TestPatternPadding : public TestPattern {
public:
TestPatternPadding() : TestPattern("Padding") {
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + 20 + sizeof(u8[0x10]));
auto testStruct = create<PatternStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + 20 + sizeof(u8[0x10]));
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(i32));
auto padding = create<PatternDataPadding>("padding", "", 0x100 + sizeof(i32), 20);
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(i32) + 20, sizeof(u8[0x10]));
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(i32) + 20, sizeof(u8)), 0x10);
auto variable = create<PatternSigned>("s32", "variable", 0x100, sizeof(i32));
auto padding = create<PatternPadding>("padding", "", 0x100 + sizeof(i32), 20);
auto array = create<PatternArrayStatic>("u8", "array", 0x100 + sizeof(i32) + 20, sizeof(u8[0x10]));
array->setEntries(create<PatternUnsigned>("u8", "", 0x100 + sizeof(i32) + 20, sizeof(u8)), 0x10);
testStruct->setMembers({ variable, padding, array });
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
{
structMembers.push_back(std::move(variable));
structMembers.push_back(std::move(padding));
structMembers.push_back(std::move(array));
}
addPattern(testStruct);
testStruct->setMembers(std::move(structMembers));
addPattern(std::move(testStruct));
}
~TestPatternPadding() override = default;

View File

@@ -2,6 +2,9 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
namespace hex::test {
class TestPatternPlacement : public TestPattern {
@@ -9,14 +12,14 @@ namespace hex::test {
TestPatternPlacement() : TestPattern("Placement") {
// placementVar
{
addPattern(create<PatternDataUnsigned>("u32", "placementVar", 0x00, sizeof(u32)));
addPattern(create<PatternUnsigned>("u32", "placementVar", 0x00, sizeof(u32)));
}
// placementArray
{
auto placementArray = create<PatternDataStaticArray>("u8", "placementArray", 0x10, sizeof(u8) * 10);
placementArray->setEntries(create<PatternDataUnsigned>("u8", "", 0x10, sizeof(u8)), 10);
addPattern(placementArray);
auto placementArray = create<PatternArrayStatic>("u8", "placementArray", 0x10, sizeof(u8) * 10);
placementArray->setEntries(std::move(create<PatternUnsigned>("u8", "", 0x10, sizeof(u8))), 10);
addPattern(std::move(placementArray));
}
}
~TestPatternPlacement() override = default;

View File

@@ -2,6 +2,9 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
namespace hex::test {
class TestPatternPointers : public TestPattern {
@@ -9,12 +12,12 @@ namespace hex::test {
TestPatternPointers() : TestPattern("Pointers") {
// placementPointer
{
auto placementPointer = create<PatternDataPointer>("", "placementPointer", 0x0C, sizeof(u8));
auto placementPointer = create<PatternPointer>("", "placementPointer", 0x0C, sizeof(u8));
placementPointer->setPointedAtAddress(0x49);
auto pointedTo = create<PatternDataUnsigned>("u32", "", 0x49, sizeof(u32));
placementPointer->setPointedAtPattern(pointedTo);
addPattern(placementPointer);
auto pointedTo = create<PatternUnsigned>("u32", "", 0x49, sizeof(u32));
placementPointer->setPointedAtPattern(std::move(pointedTo));
addPattern(std::move(placementPointer));
}
}
~TestPatternPointers() override = default;

View File

@@ -2,20 +2,30 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_signed.hpp>
#include <hex/pattern_language/patterns/pattern_struct.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
namespace hex::test {
class TestPatternStructs : public TestPattern {
public:
TestPatternStructs() : TestPattern("Structs") {
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + sizeof(u8[0x10]));
auto testStruct = create<PatternStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + sizeof(u8[0x10]));
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(i32));
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10]));
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10);
auto variable = create<PatternSigned>("s32", "variable", 0x100, sizeof(i32));
auto array = create<PatternArrayStatic>("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10]));
array->setEntries(create<PatternUnsigned>("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10);
testStruct->setMembers({ variable, array });
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
{
structMembers.push_back(std::move(variable));
structMembers.push_back(std::move(array));
}
testStruct->setMembers(std::move(structMembers));
addPattern(testStruct);
addPattern(std::move(testStruct));
}
~TestPatternStructs() override = default;

View File

@@ -2,20 +2,31 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_signed.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
#include <hex/pattern_language/patterns/pattern_union.hpp>
namespace hex::test {
class TestPatternUnions : public TestPattern {
public:
TestPatternUnions() : TestPattern("Unions") {
auto testUnion = create<PatternDataUnion>("TestUnion", "testUnion", 0x200, sizeof(u128));
auto testUnion = create<PatternUnion>("TestUnion", "testUnion", 0x200, sizeof(u128));
auto array = create<PatternDataStaticArray>("s32", "array", 0x200, sizeof(i32[2]));
array->setEntries(create<PatternDataSigned>("s32", "", 0x200, sizeof(i32)), 2);
auto variable = create<PatternDataUnsigned>("u128", "variable", 0x200, sizeof(u128));
auto array = create<PatternArrayStatic>("s32", "array", 0x200, sizeof(i32[2]));
array->setEntries(create<PatternSigned>("s32", "", 0x200, sizeof(i32)), 2);
auto variable = create<PatternUnsigned>("u128", "variable", 0x200, sizeof(u128));
testUnion->setMembers({ array, variable });
std::vector<std::shared_ptr<hex::pl::Pattern>> unionMembers;
{
unionMembers.push_back(std::move(array));
unionMembers.push_back(std::move(variable));
}
addPattern(testUnion);
testUnion->setMembers(std::move(unionMembers));
addPattern(std::move(testUnion));
}
~TestPatternUnions() override = default;

View File

@@ -7,14 +7,43 @@
#include <hex/helpers/fmt.hpp>
#include <hex/pattern_language/pattern_language.hpp>
#include <hex/pattern_language/evaluator.hpp>
#include <hex/pattern_language/ast_node.hpp>
#include <hex/pattern_language/ast/ast_node.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#include <hex/api/content_registry.hpp>
#include "test_provider.hpp"
#include "test_patterns/test_pattern.hpp"
#include <fmt/args.h>
using namespace hex::test;
static std::string format(hex::pl::Evaluator *ctx, const auto &params) {
auto format = hex::pl::Token::literalToString(params[0], true);
std::string message;
fmt::dynamic_format_arg_store<fmt::format_context> formatArgs;
for (u32 i = 1; i < params.size(); i++) {
auto &param = params[i];
std::visit(hex::overloaded {
[&](const std::shared_ptr<hex::pl::Pattern> &value) {
formatArgs.push_back(value->toString(ctx->getProvider()));
},
[&](auto &&value) {
formatArgs.push_back(value);
} },
param);
}
try {
return fmt::vformat(format, formatArgs);
} catch (fmt::format_error &error) {
hex::pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
}
}
void addFunctions() {
hex::ContentRegistry::PatternLanguage::Namespace nsStd = { "std" };
hex::ContentRegistry::PatternLanguage::addFunction(nsStd, "assert", 2, [](Evaluator *ctx, auto params) -> Token::Literal {
@@ -26,6 +55,12 @@ void addFunctions() {
return {};
});
hex::ContentRegistry::PatternLanguage::addFunction(nsStd, "print", hex::ContentRegistry::PatternLanguage::MoreParametersThan | 0, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
ctx->getConsole().log(LogConsole::Level::Info, format(ctx, params));
return std::nullopt;
});
}
int test(int argc, char **argv) {
@@ -55,7 +90,6 @@ int test(int argc, char **argv) {
}
hex::pl::PatternLanguage language;
addFunctions();
// Check if compilation succeeded
auto result = language.executeString(provider, testPatterns[testName]->getSourceCode());
@@ -75,11 +109,6 @@ int test(int argc, char **argv) {
return EXIT_FAILURE;
}
ON_SCOPE_EXIT {
for (auto &pattern : language.getPatterns())
delete pattern;
};
// Check if the right number of patterns have been produced
if (language.getPatterns().size() != currTest->getPatterns().size() && !currTest->getPatterns().empty()) {
hex::log::fatal("Source didn't produce expected number of patterns");
@@ -103,6 +132,8 @@ int test(int argc, char **argv) {
int main(int argc, char **argv) {
int result = EXIT_SUCCESS;
addFunctions();
for (u32 i = 0; i < 16; i++) {
result = test(argc, argv);
if (result != EXIT_SUCCESS)