mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-27 23:37:05 -05:00
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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 ¶ms) {
|
||||
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 ¶m = 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)
|
||||
|
||||
Reference in New Issue
Block a user