mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-11-12 21:56:54 +00:00
TypeGraph: Introduce TypeGraphParser to simplify unit testing
TypeGraphParser parses a textual type graph, as emitted by Printer. It also doubles as a way of ensuring that Printer displays all information about a type graph, to aid with debugging. Convert Flattener unit tests over to this new framework as a first step.
This commit is contained in:
parent
bd948152b7
commit
30cd23fa53
@ -55,21 +55,27 @@ class TypeGraph {
|
||||
template <typename T>
|
||||
Primitive& makeType(Primitive::Kind kind);
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T& makeType(NodeId id, Args&&... args) {
|
||||
static_assert(std::is_same_v<T, Class> || std::is_same_v<T, Container> ||
|
||||
std::is_same_v<T, Array> || std::is_same_v<T, Typedef> ||
|
||||
std::is_same_v<T, Pointer>,
|
||||
"Unnecessary node ID provided");
|
||||
auto type_unique_ptr = std::make_unique<T>(id, std::forward<Args>(args)...);
|
||||
auto type_raw_ptr = type_unique_ptr.get();
|
||||
types_.push_back(std::move(type_unique_ptr));
|
||||
return *type_raw_ptr;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T& makeType(Args&&... args) {
|
||||
static_assert(!std::is_same<T, Primitive>::value,
|
||||
static_assert(!std::is_same_v<T, Primitive>,
|
||||
"Primitive singleton override should be used");
|
||||
if constexpr (std::is_same<T, Class>::value ||
|
||||
std::is_same<T, Container>::value ||
|
||||
std::is_same<T, Array>::value ||
|
||||
std::is_same<T, Typedef>::value ||
|
||||
std::is_same<T, Pointer>::value) {
|
||||
if constexpr (std::is_same_v<T, Class> || std::is_same_v<T, Container> ||
|
||||
std::is_same_v<T, Array> || std::is_same_v<T, Typedef> ||
|
||||
std::is_same_v<T, Pointer>) {
|
||||
// Node ID required
|
||||
auto type_unique_ptr =
|
||||
std::make_unique<T>(next_id_++, std::forward<Args>(args)...);
|
||||
auto type_raw_ptr = type_unique_ptr.get();
|
||||
types_.push_back(std::move(type_unique_ptr));
|
||||
return *type_raw_ptr;
|
||||
return makeType<T>(next_id_++, std::forward<Args>(args)...);
|
||||
} else {
|
||||
// No Node ID
|
||||
auto type_unique_ptr = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
|
@ -62,6 +62,7 @@ add_executable(test_type_graph
|
||||
test_topo_sorter.cpp
|
||||
test_type_identifier.cpp
|
||||
type_graph_utils.cpp
|
||||
TypeGraphParser.cpp
|
||||
)
|
||||
add_dependencies(test_type_graph integration_test_target)
|
||||
target_compile_definitions(test_type_graph PRIVATE
|
||||
|
422
test/TypeGraphParser.cpp
Normal file
422
test/TypeGraphParser.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
#include "TypeGraphParser.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "oi/type_graph/TypeGraph.h"
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* Sets line to the contents of the current line.
|
||||
* Advances input to the beginning of the next line.
|
||||
*/
|
||||
bool getline(std::string_view& input, std::string_view& line) {
|
||||
auto nextline = input.find('\n');
|
||||
if (nextline == input.npos) {
|
||||
return false;
|
||||
}
|
||||
line = input.substr(0, nextline);
|
||||
input = input.substr(nextline + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
Primitive::Kind getKind(std::string_view kindStr) {
|
||||
if (kindStr == "int8_t")
|
||||
return Primitive::Kind::Int8;
|
||||
if (kindStr == "int16_t")
|
||||
return Primitive::Kind::Int16;
|
||||
if (kindStr == "int32_t")
|
||||
return Primitive::Kind::Int32;
|
||||
if (kindStr == "int64_t")
|
||||
return Primitive::Kind::Int64;
|
||||
if (kindStr == "uint8_t")
|
||||
return Primitive::Kind::UInt8;
|
||||
if (kindStr == "uint16_t")
|
||||
return Primitive::Kind::UInt16;
|
||||
if (kindStr == "uint32_t")
|
||||
return Primitive::Kind::UInt32;
|
||||
if (kindStr == "uint64_t")
|
||||
return Primitive::Kind::UInt64;
|
||||
if (kindStr == "float")
|
||||
return Primitive::Kind::Float32;
|
||||
if (kindStr == "double")
|
||||
return Primitive::Kind::Float64;
|
||||
if (kindStr == "long double")
|
||||
return Primitive::Kind::Float128;
|
||||
if (kindStr == "bool")
|
||||
return Primitive::Kind::Bool;
|
||||
if (kindStr == "uintptr_t")
|
||||
return Primitive::Kind::UIntPtr;
|
||||
if (kindStr == "void")
|
||||
return Primitive::Kind::Void;
|
||||
throw std::runtime_error("Invalid Primitive::Kind: " + std::string{kindStr});
|
||||
}
|
||||
|
||||
ContainerInfo getContainerInfo(std::string_view name) {
|
||||
if (name == "std::vector") {
|
||||
ContainerInfo info{"std::vector", SEQ_TYPE, "vector"};
|
||||
info.stubTemplateParams = {1};
|
||||
return info;
|
||||
}
|
||||
if (name == "std::map") {
|
||||
ContainerInfo info{"std::map", STD_MAP_TYPE, "utility"};
|
||||
info.stubTemplateParams = {2, 3};
|
||||
return info;
|
||||
}
|
||||
if (name == "std::pair") {
|
||||
ContainerInfo info{"std::pair", SEQ_TYPE, "utility"};
|
||||
return info;
|
||||
}
|
||||
if (name == "std::allocator") {
|
||||
ContainerInfo info{"std::allocator", DUMMY_TYPE, "memory"};
|
||||
return info;
|
||||
}
|
||||
throw std::runtime_error("Unsupported container: " + std::string{name});
|
||||
}
|
||||
|
||||
Qualifier getQualifier(std::string_view line) {
|
||||
if (line == "const") {
|
||||
return Qualifier::Const;
|
||||
}
|
||||
throw std::runtime_error("Unsupported qualifier: " + std::string{line});
|
||||
}
|
||||
|
||||
size_t stripIndent(std::string_view& line) {
|
||||
auto indent = line.find_first_not_of(" ");
|
||||
line.remove_prefix(indent);
|
||||
return indent;
|
||||
}
|
||||
|
||||
bool tryRemovePrefix(std::string_view& line, std::string_view prefix) {
|
||||
if (!line.starts_with(prefix))
|
||||
return false;
|
||||
line.remove_prefix(prefix.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void removePrefix(std::string_view& line, std::string_view prefix) {
|
||||
if (!tryRemovePrefix(line, prefix))
|
||||
throw std::runtime_error("Unexpected line prefix. Expected '" +
|
||||
std::string{prefix} + "'. Got '" +
|
||||
std::string{line} + "'.");
|
||||
}
|
||||
|
||||
std::optional<uint64_t> tryParseIntAttribute(std::string_view line,
|
||||
std::string_view marker) {
|
||||
auto attrStartPos = line.find(marker);
|
||||
if (attrStartPos == line.npos)
|
||||
return {};
|
||||
|
||||
auto valStartPos = attrStartPos + marker.size();
|
||||
auto valEndPos = line.find_first_not_of("0123456789", valStartPos);
|
||||
|
||||
auto valStr = line.substr(valStartPos, valEndPos);
|
||||
uint64_t val = std::stoi(std::string{valStr}); // Makes a string copy :'(
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t parseIntAttribute(std::string_view line,
|
||||
std::string_view type,
|
||||
std::string_view marker) {
|
||||
auto val = tryParseIntAttribute(line, marker);
|
||||
if (!val)
|
||||
throw std::runtime_error(std::string{type} + " must have an attribute: '" +
|
||||
std::string{marker} + "'. Got: '" +
|
||||
std::string{line} + "'");
|
||||
return *val;
|
||||
}
|
||||
|
||||
std::optional<std::string_view> tryParseStringValue(std::string_view& input,
|
||||
std::string_view marker,
|
||||
size_t rootIndent) {
|
||||
std::string_view modifiedInput = input;
|
||||
std::string_view line;
|
||||
getline(modifiedInput, line);
|
||||
|
||||
size_t indent = stripIndent(line);
|
||||
if (indent != rootIndent)
|
||||
return {};
|
||||
|
||||
if (!tryRemovePrefix(line, marker))
|
||||
return {};
|
||||
|
||||
auto val = line;
|
||||
|
||||
// Update input to point to after the value we have read
|
||||
input = modifiedInput;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
NodeId getId(std::string_view str, size_t* idLen = nullptr) {
|
||||
if (idLen)
|
||||
*idLen = 0;
|
||||
if (str[0] != '[')
|
||||
return -1;
|
||||
|
||||
auto closeBracket = str.find(']');
|
||||
if (closeBracket == str.npos)
|
||||
return -1;
|
||||
|
||||
auto idStr = str.substr(1, closeBracket);
|
||||
NodeId id = std::stoi(std::string{idStr}); // Makes a string copy :'(
|
||||
|
||||
if (idLen)
|
||||
*idLen = closeBracket + 2; // +2 for the trailing "] "
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void TypeGraphParser::parse(std::string_view input) {
|
||||
size_t rootIndent = input.find_first_not_of("[]0123456789 ");
|
||||
while (!input.empty()) {
|
||||
Type& type = parseType(input, rootIndent);
|
||||
typeGraph_.addRoot(type);
|
||||
}
|
||||
}
|
||||
|
||||
Type& TypeGraphParser::parseType(std::string_view& input, size_t rootIndent) {
|
||||
std::string_view line;
|
||||
getline(input, line);
|
||||
|
||||
size_t idLen = 0;
|
||||
NodeId id = getId(line, &idLen);
|
||||
line.remove_prefix(idLen);
|
||||
|
||||
size_t indent = stripIndent(line) + idLen;
|
||||
if (indent != rootIndent)
|
||||
throw std::runtime_error("Unexpected indent for line: " +
|
||||
std::string{line});
|
||||
|
||||
auto nodeEndPos = line.find_first_of(": \n");
|
||||
auto nodeTypeName = line.substr(0, nodeEndPos);
|
||||
|
||||
Type* type = nullptr;
|
||||
if (NodeId refId = getId(nodeTypeName); refId != -1) {
|
||||
auto it = nodesById_.find(refId);
|
||||
if (it == nodesById_.end())
|
||||
throw std::runtime_error("Node ID referenced before definition: " +
|
||||
std::to_string(refId));
|
||||
|
||||
type = &it->second.get();
|
||||
} else if (nodeTypeName == "Class" || nodeTypeName == "Struct" ||
|
||||
nodeTypeName == "Union") {
|
||||
// Format: "Class: MyClass (size: 12)"
|
||||
Class::Kind kind;
|
||||
if (nodeTypeName == "Class")
|
||||
kind = Class::Kind::Class;
|
||||
else if (nodeTypeName == "Struct")
|
||||
kind = Class::Kind::Struct;
|
||||
else if (nodeTypeName == "Union")
|
||||
kind = Class::Kind::Union;
|
||||
else
|
||||
abort();
|
||||
|
||||
// Extract name
|
||||
auto nameStartPos = line.find(' ') + 1;
|
||||
auto nameEndPos = line.find('(', nameStartPos + 1);
|
||||
auto name = line.substr(nameStartPos, nameEndPos - nameStartPos - 1);
|
||||
|
||||
auto size = parseIntAttribute(line, nodeTypeName, "size: ");
|
||||
auto align = tryParseIntAttribute(line, "align: ");
|
||||
|
||||
Class& c = typeGraph_.makeType<Class>(id, kind, std::string{name}, size);
|
||||
if (align)
|
||||
c.setAlign(*align);
|
||||
nodesById_.insert({id, c});
|
||||
|
||||
parseParams(c, input, indent + 2);
|
||||
parseParents(c, input, indent + 2);
|
||||
parseMembers(c, input, indent + 2);
|
||||
parseFunctions(c, input, indent + 2);
|
||||
parseChildren(c, input, indent + 2);
|
||||
|
||||
type = &c;
|
||||
} else if (nodeTypeName == "Container") {
|
||||
// Format: "Container: std::vector (size: 24)
|
||||
|
||||
// Extract container type name
|
||||
auto nameStartPos = line.find(' ') + 1;
|
||||
auto nameEndPos = line.find('(', nameStartPos + 1);
|
||||
auto name = line.substr(nameStartPos, nameEndPos - nameStartPos - 1);
|
||||
|
||||
auto info = getContainerInfo(name);
|
||||
|
||||
auto size = parseIntAttribute(line, nodeTypeName, "size: ");
|
||||
|
||||
Container& c = typeGraph_.makeType<Container>(id, info, size);
|
||||
nodesById_.insert({id, c});
|
||||
|
||||
parseParams(c, input, indent + 2);
|
||||
|
||||
type = &c;
|
||||
} else if (nodeTypeName == "Primitive") {
|
||||
// Format: "Primitive: int32_t"
|
||||
removePrefix(line, "Primitive: ");
|
||||
auto kind = getKind(line);
|
||||
type = &typeGraph_.makeType<Primitive>(kind);
|
||||
} else if (nodeTypeName == "Enum") {
|
||||
// Format: "Enum: MyEnum (size: 4)"
|
||||
removePrefix(line, "Enum: ");
|
||||
auto nameEndPos = line.find(' ');
|
||||
auto name = line.substr(0, nameEndPos);
|
||||
auto size = parseIntAttribute(line, nodeTypeName, "size: ");
|
||||
type = &typeGraph_.makeType<Enum>(std::string{name}, size);
|
||||
} else if (nodeTypeName == "Array") {
|
||||
// Format: "Array: (length: 5)
|
||||
auto len = parseIntAttribute(line, nodeTypeName, "length: ");
|
||||
auto& elementType = parseType(input, indent + 2);
|
||||
type = &typeGraph_.makeType<Array>(id, elementType, len);
|
||||
} else if (nodeTypeName == "Typedef") {
|
||||
// Format: "Typedef: myTypedef"
|
||||
removePrefix(line, "Typedef: ");
|
||||
auto name = line;
|
||||
auto& underlyingType = parseType(input, indent + 2);
|
||||
type = &typeGraph_.makeType<Typedef>(id, std::string{name}, underlyingType);
|
||||
nodesById_.insert({id, *type});
|
||||
} else if (nodeTypeName == "Pointer") {
|
||||
// Format: "Pointer"
|
||||
auto& pointeeType = parseType(input, indent + 2);
|
||||
type = &typeGraph_.makeType<Pointer>(id, pointeeType);
|
||||
nodesById_.insert({id, *type});
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported node type: " +
|
||||
std::string{nodeTypeName});
|
||||
}
|
||||
|
||||
return *type;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TypeGraphParser::parseParams(T& c,
|
||||
std::string_view& input,
|
||||
size_t rootIndent) {
|
||||
std::string_view origInput = input;
|
||||
for (std::string_view line; getline(input, line); origInput = input) {
|
||||
size_t indent = stripIndent(line);
|
||||
if (indent != rootIndent)
|
||||
break;
|
||||
|
||||
// Format: "Param"
|
||||
if (!tryRemovePrefix(line, "Param"))
|
||||
break;
|
||||
|
||||
if (auto value = tryParseStringValue(input, "Value: ", rootIndent + 2);
|
||||
value) {
|
||||
c.templateParams.emplace_back(std::string{*value});
|
||||
} else {
|
||||
Type& type = parseType(input, rootIndent + 2);
|
||||
TemplateParam param{type};
|
||||
|
||||
if (auto qualStr =
|
||||
tryParseStringValue(input, "Qualifiers: ", rootIndent + 2);
|
||||
qualStr) {
|
||||
Qualifier qual = getQualifier(*qualStr);
|
||||
param.qualifiers[qual] = true;
|
||||
}
|
||||
|
||||
c.templateParams.push_back(param);
|
||||
}
|
||||
}
|
||||
// No more params for us - put back the line we just read
|
||||
input = origInput;
|
||||
}
|
||||
|
||||
void TypeGraphParser::parseParents(Class& c,
|
||||
std::string_view& input,
|
||||
size_t rootIndent) {
|
||||
std::string_view origInput = input;
|
||||
for (std::string_view line; getline(input, line); origInput = input) {
|
||||
size_t indent = stripIndent(line);
|
||||
if (indent != rootIndent)
|
||||
break;
|
||||
|
||||
// Format: "Parent (offset: 0)"
|
||||
if (!tryRemovePrefix(line, "Parent "))
|
||||
break;
|
||||
|
||||
auto offset = parseIntAttribute(line, "Parent", "offset: ");
|
||||
Type& type = parseType(input, rootIndent + 2);
|
||||
|
||||
c.parents.emplace_back(type, offset * 8);
|
||||
}
|
||||
// No more parents for us - put back the line we just read
|
||||
input = origInput;
|
||||
}
|
||||
|
||||
void TypeGraphParser::parseMembers(Class& c,
|
||||
std::string_view& input,
|
||||
size_t rootIndent) {
|
||||
std::string_view origInput = input;
|
||||
for (std::string_view line; getline(input, line); origInput = input) {
|
||||
size_t indent = stripIndent(line);
|
||||
if (indent != rootIndent)
|
||||
break;
|
||||
|
||||
// Format: "Member: memberName (offset: 0)"
|
||||
if (!tryRemovePrefix(line, "Member: "))
|
||||
break;
|
||||
|
||||
auto nameEndPos = line.find(' ');
|
||||
auto name = line.substr(0, nameEndPos);
|
||||
auto offset = parseIntAttribute(line, "Member", "offset: ");
|
||||
auto align = tryParseIntAttribute(line, "align: ");
|
||||
Type& type = parseType(input, rootIndent + 2);
|
||||
|
||||
Member member{type, std::string{name}, offset * 8};
|
||||
if (align)
|
||||
member.align = *align;
|
||||
|
||||
c.members.push_back(member);
|
||||
}
|
||||
// No more members for us - put back the line we just read
|
||||
input = origInput;
|
||||
}
|
||||
|
||||
void TypeGraphParser::parseFunctions(Class& c,
|
||||
std::string_view& input,
|
||||
size_t rootIndent) {
|
||||
std::string_view origInput = input;
|
||||
for (std::string_view line; getline(input, line); origInput = input) {
|
||||
size_t indent = stripIndent(line);
|
||||
if (indent != rootIndent)
|
||||
break;
|
||||
|
||||
// Format: "Function: funcName"
|
||||
if (!tryRemovePrefix(line, "Function: "))
|
||||
break;
|
||||
|
||||
auto name = line;
|
||||
|
||||
Function func{std::string{name}};
|
||||
|
||||
c.functions.push_back(func);
|
||||
}
|
||||
// No more functions for us - put back the line we just read
|
||||
input = origInput;
|
||||
}
|
||||
|
||||
void TypeGraphParser::parseChildren(Class& c,
|
||||
std::string_view& input,
|
||||
size_t rootIndent) {
|
||||
std::string_view origInput = input;
|
||||
for (std::string_view line; getline(input, line); origInput = input) {
|
||||
size_t indent = stripIndent(line);
|
||||
if (indent != rootIndent)
|
||||
break;
|
||||
|
||||
// Format: "Child"
|
||||
if (!tryRemovePrefix(line, "Child"))
|
||||
break;
|
||||
|
||||
Type& type = parseType(input, rootIndent + 2);
|
||||
auto* childClass = dynamic_cast<Class*>(&type);
|
||||
if (!childClass)
|
||||
throw std::runtime_error("Invalid type for child");
|
||||
|
||||
c.children.push_back(*childClass);
|
||||
}
|
||||
// No more children for us - put back the line we just read
|
||||
input = origInput;
|
||||
}
|
37
test/TypeGraphParser.h
Normal file
37
test/TypeGraphParser.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "oi/type_graph/Types.h"
|
||||
|
||||
using namespace type_graph;
|
||||
|
||||
namespace type_graph {
|
||||
class TypeGraph;
|
||||
} // namespace type_graph
|
||||
|
||||
/*
|
||||
* TypeGraphParser
|
||||
*
|
||||
* Parses a textual type graph, as emitted by Printer.
|
||||
*/
|
||||
class TypeGraphParser {
|
||||
public:
|
||||
TypeGraphParser(TypeGraph& typeGraph) : typeGraph_(typeGraph) {
|
||||
}
|
||||
|
||||
void parse(std::string_view input);
|
||||
|
||||
private:
|
||||
TypeGraph& typeGraph_;
|
||||
std::unordered_map<NodeId, std::reference_wrapper<Type>> nodesById_;
|
||||
|
||||
Type& parseType(std::string_view& input, size_t rootIndent);
|
||||
template <typename T>
|
||||
void parseParams(T& c, std::string_view& input, size_t rootIndent);
|
||||
void parseParents(Class& c, std::string_view& input, size_t rootIndent);
|
||||
void parseMembers(Class& c, std::string_view& input, size_t rootIndent);
|
||||
void parseFunctions(Class& c, std::string_view& input, size_t rootIndent);
|
||||
void parseChildren(Class& c, std::string_view& input, size_t rootIndent);
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
using namespace type_graph;
|
||||
|
||||
TEST(FlattenerTest, NoParents) {
|
||||
// No change
|
||||
// Original and flattened:
|
||||
// struct MyStruct { int n0; };
|
||||
// class MyClass {
|
||||
@ -14,18 +15,7 @@ TEST(FlattenerTest, NoParents) {
|
||||
// MyEnum e;
|
||||
// MyStruct mystruct;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto myenum = Enum{"MyEnum", 4};
|
||||
auto mystruct = Class{1, Class::Kind::Struct, "MyStruct", 4};
|
||||
auto myclass = Class{0, Class::Kind::Class, "MyClass", 12};
|
||||
|
||||
mystruct.members.push_back(Member{myint, "n0", 0});
|
||||
|
||||
myclass.members.push_back(Member{myint, "n", 0});
|
||||
myclass.members.push_back(Member{myenum, "e", 4 * 8});
|
||||
myclass.members.push_back(Member{mystruct, "mystruct", 8 * 8});
|
||||
|
||||
test(Flattener::createPass(), {myclass}, R"(
|
||||
testNoChange(Flattener::createPass(), R"(
|
||||
[0] Class: MyClass (size: 12)
|
||||
Member: n (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -49,18 +39,18 @@ TEST(FlattenerTest, OnlyParents) {
|
||||
// int b;
|
||||
// int c;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 8};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 4 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Parent (offset: 4)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 8)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -81,19 +71,21 @@ TEST(FlattenerTest, ParentsFirst) {
|
||||
// int c;
|
||||
// int a;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 12};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 4 * 8});
|
||||
classA.members.push_back(Member{myint, "a", 8 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Parent (offset: 4)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 8)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -116,20 +108,21 @@ TEST(FlattenerTest, MembersFirst) {
|
||||
// int b;
|
||||
// int c;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 12};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
classA.members.push_back(Member{myint, "a", 0});
|
||||
classA.parents.push_back(Parent{classB, 4 * 8});
|
||||
classA.parents.push_back(Parent{classC, 8 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Parent (offset: 4)
|
||||
[1] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Parent (offset: 8)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 0)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Member: a (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -153,21 +146,23 @@ TEST(FlattenerTest, MixedMembersAndParents) {
|
||||
// int a2;
|
||||
// int c;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 16};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.members.push_back(Member{myint, "a1", 4 * 8});
|
||||
classA.members.push_back(Member{myint, "a2", 8 * 8});
|
||||
classA.parents.push_back(Parent{classC, 12 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 16)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Parent (offset: 12)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a1 (offset: 4)
|
||||
Primitive: int32_t
|
||||
Member: a2 (offset: 8)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 16)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -192,19 +187,21 @@ TEST(FlattenerTest, EmptyParent) {
|
||||
// int a1;
|
||||
// int a2;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 12};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 0};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classA.members.push_back(Member{myint, "a1", 4 * 8});
|
||||
classA.members.push_back(Member{myint, "a2", 8 * 8});
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 0});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 0)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a1 (offset: 4)
|
||||
Primitive: int32_t
|
||||
Member: a2 (offset: 8)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -229,24 +226,25 @@ TEST(FlattenerTest, TwoDeep) {
|
||||
// int c;
|
||||
// int a;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 16};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 8};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
auto classD = Class{3, Class::Kind::Class, "ClassD", 4};
|
||||
|
||||
classD.members.push_back(Member{myint, "d", 0});
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.parents.push_back(Parent{classD, 0});
|
||||
classB.members.push_back(Member{myint, "b", 4 * 8});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 8 * 8});
|
||||
classA.members.push_back(Member{myint, "a", 12 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 16)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 8)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassD (size: 4)
|
||||
Member: d (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: b (offset: 4)
|
||||
Primitive: int32_t
|
||||
Parent (offset: 8)
|
||||
[3] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 12)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 16)
|
||||
Member: d (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -272,21 +270,23 @@ TEST(FlattenerTest, DiamondInheritance) {
|
||||
// int c1;
|
||||
// int a;
|
||||
// };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 16};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 8};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.parents.push_back(Parent{classC, 0});
|
||||
classB.members.push_back(Member{myint, "b", 4 * 8});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 8 * 8});
|
||||
classA.members.push_back(Member{myint, "a", 12 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 16)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 8)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: b (offset: 4)
|
||||
Primitive: int32_t
|
||||
Parent (offset: 8)
|
||||
[2]
|
||||
Member: a (offset: 12)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 16)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -308,20 +308,21 @@ TEST(FlattenerTest, Member) {
|
||||
// Flattened:
|
||||
// class B { int c; int b; };
|
||||
// Class A { int a; B b; };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 12};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 8};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.parents.push_back(Parent{classC, 0});
|
||||
classB.members.push_back(Member{myint, "b", 4 * 8});
|
||||
|
||||
classA.members.push_back(Member{myint, "a", 0});
|
||||
classA.members.push_back(Member{classB, "b", 4 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Member: a (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: b (offset: 4)
|
||||
[1] Class: ClassB (size: 8)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: b (offset: 4)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Member: a (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -343,20 +344,21 @@ TEST(FlattenerTest, MemberOfParent) {
|
||||
// Flattened:
|
||||
// class C { int c; };
|
||||
// class A { int b; C c; int a; };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 12};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 8};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
classB.members.push_back(Member{classC, "c", 4 * 8});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.members.push_back(Member{myint, "a", 8 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 8)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: c (offset: 4)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 8)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -378,20 +380,21 @@ TEST(FlattenerTest, ContainerParam) {
|
||||
// Flattened:
|
||||
// class A { int b; int a; };
|
||||
// std::vector<A, int>
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{1, Class::Kind::Class, "ClassA", 8};
|
||||
auto classB = Class{2, Class::Kind::Class, "ClassB", 4};
|
||||
auto container = getVector();
|
||||
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.members.push_back(Member{myint, "a", 4 * 8});
|
||||
|
||||
container.templateParams.push_back(TemplateParam{classA});
|
||||
container.templateParams.push_back(TemplateParam{myint});
|
||||
|
||||
test(Flattener::createPass(), {container}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Container: std::vector (size: 24)
|
||||
Param
|
||||
[1] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 4)
|
||||
Primitive: int32_t
|
||||
Param
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Container: std::vector (size: 24)
|
||||
Param
|
||||
[1] Class: ClassA (size: 8)
|
||||
@ -409,18 +412,18 @@ TEST(FlattenerTest, Array) {
|
||||
// class B { int b; };
|
||||
// class A : B { int a; };
|
||||
// A[5]
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
|
||||
auto classB = Class{2, Class::Kind::Class, "ClassB", 4};
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
auto classA = Class{1, Class::Kind::Class, "ClassA", 8};
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.members.push_back(Member{myint, "a", 4 * 8});
|
||||
|
||||
auto arrayA = Array{0, classA, 5};
|
||||
|
||||
test(Flattener::createPass(), {arrayA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Array: (length: 5)
|
||||
[1] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 4)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Array: (length: 5)
|
||||
[1] Class: ClassA (size: 8)
|
||||
Member: b (offset: 0)
|
||||
@ -435,17 +438,18 @@ TEST(FlattenerTest, Typedef) {
|
||||
// class B { int b; };
|
||||
// class A : B { int a; };
|
||||
// using aliasA = A;
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classB = Class{2, Class::Kind::Class, "ClassB", 4};
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
auto classA = Class{1, Class::Kind::Class, "ClassA", 8};
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.members.push_back(Member{myint, "a", 4 * 8});
|
||||
|
||||
auto aliasA = Typedef{0, "aliasA", classA};
|
||||
|
||||
test(Flattener::createPass(), {aliasA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Typedef: aliasA
|
||||
[1] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 4)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Typedef: aliasA
|
||||
[1] Class: ClassA (size: 8)
|
||||
Member: b (offset: 0)
|
||||
@ -460,17 +464,18 @@ TEST(FlattenerTest, TypedefParent) {
|
||||
// class B { int b; };
|
||||
// using aliasB = B;
|
||||
// class A : aliasB { int a; };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
auto aliasB = Typedef{2, "aliasB", classB};
|
||||
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 8};
|
||||
classA.parents.push_back(Parent{aliasB, 0});
|
||||
classA.members.push_back(Member{myint, "a", 4 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[1] Typedef: aliasB
|
||||
[2] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 4)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 8)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -483,7 +488,7 @@ TEST(FlattenerTest, Pointer) {
|
||||
// Original:
|
||||
// class B { int b; };
|
||||
// class A : B { int a; };
|
||||
// class C { A a; };
|
||||
// class C { A* a; };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
|
||||
auto classB = Class{3, Class::Kind::Class, "ClassB", 4};
|
||||
@ -497,7 +502,19 @@ TEST(FlattenerTest, Pointer) {
|
||||
auto classC = Class{0, Class::Kind::Class, "ClassC", 8};
|
||||
classC.members.push_back(Member{ptrA, "a", 0});
|
||||
|
||||
test(Flattener::createPass(), {classC}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassC (size: 8)
|
||||
Member: a (offset: 0)
|
||||
[1] Pointer
|
||||
[2] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[3] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Member: a (offset: 4)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassC (size: 8)
|
||||
Member: a (offset: 0)
|
||||
[1] Pointer
|
||||
@ -511,7 +528,7 @@ TEST(FlattenerTest, Pointer) {
|
||||
|
||||
TEST(FlattenerTest, PointerCycle) {
|
||||
// Original:
|
||||
// class B { A a };
|
||||
// class B { A* a };
|
||||
// class A { B b; };
|
||||
//
|
||||
// Flattened:
|
||||
@ -522,7 +539,16 @@ TEST(FlattenerTest, PointerCycle) {
|
||||
classA.members.push_back(Member{classB, "b", 0});
|
||||
classB.members.push_back(Member{ptrA, "a", 0});
|
||||
|
||||
test(Flattener::createPass(), {classA, classB}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 69)
|
||||
Member: b (offset: 0)
|
||||
[1] Class: ClassB (size: 69)
|
||||
Member: a (offset: 0)
|
||||
[2] Pointer
|
||||
[0]
|
||||
[1]
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 69)
|
||||
Member: b (offset: 0)
|
||||
[1] Class: ClassB (size: 69)
|
||||
@ -538,23 +564,8 @@ TEST(FlattenerTest, Alignment) {
|
||||
// class alignas(16) C { int c; };
|
||||
// class B { alignas(8) int b; };
|
||||
// class A : B, C { int a; };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 12};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
classC.setAlign(16);
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
Member memberB{myint, "b", 0};
|
||||
memberB.align = 8;
|
||||
classB.members.push_back(memberB);
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 4 * 8});
|
||||
classA.members.push_back(Member{myint, "a", 8 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 12)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 4)
|
||||
@ -594,7 +605,17 @@ TEST(FlattenerTest, Functions) {
|
||||
classB.functions.push_back(Function{"funcB"});
|
||||
classC.functions.push_back(Function{"funcC"});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 0)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 0)
|
||||
Function: funcB
|
||||
Parent (offset: 0)
|
||||
[2] Class: ClassC (size: 0)
|
||||
Function: funcC
|
||||
Function: funcA
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassA (size: 0)
|
||||
Function: funcA
|
||||
Function: funcB
|
||||
@ -607,21 +628,23 @@ TEST(FlattenerTest, Children) {
|
||||
// class C { int c; };
|
||||
// class B { int b; };
|
||||
// class A : B, C { };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{1, Class::Kind::Class, "ClassA", 8};
|
||||
auto classB = Class{0, Class::Kind::Class, "ClassB", 4};
|
||||
auto classC = Class{2, Class::Kind::Class, "ClassC", 4};
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 4 * 8});
|
||||
|
||||
classB.children.push_back(classA);
|
||||
classC.children.push_back(classA);
|
||||
|
||||
test(Flattener::createPass(), {classB}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
Child
|
||||
[1] Class: ClassA (size: 8)
|
||||
Parent (offset: 0)
|
||||
[0]
|
||||
Parent (offset: 4)
|
||||
[2] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Child
|
||||
[1]
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassB (size: 4)
|
||||
Member: b (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -640,28 +663,31 @@ TEST(FlattenerTest, ChildrenTwoDeep) {
|
||||
// class C { int c; };
|
||||
// class B : D { int b; };
|
||||
// class A : B, C { int a; };
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto classA = Class{2, Class::Kind::Class, "ClassA", 16};
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 8};
|
||||
auto classC = Class{3, Class::Kind::Class, "ClassC", 4};
|
||||
auto classD = Class{0, Class::Kind::Class, "ClassD", 4};
|
||||
|
||||
classD.members.push_back(Member{myint, "d", 0});
|
||||
|
||||
classC.members.push_back(Member{myint, "c", 0});
|
||||
|
||||
classB.parents.push_back(Parent{classD, 0});
|
||||
classB.members.push_back(Member{myint, "b", 4 * 8});
|
||||
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{classC, 8 * 8});
|
||||
classA.members.push_back(Member{myint, "a", 12 * 8});
|
||||
|
||||
classD.children.push_back(classB);
|
||||
classB.children.push_back(classA);
|
||||
classC.children.push_back(classA);
|
||||
|
||||
test(Flattener::createPass(), {classD}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassD (size: 4)
|
||||
Member: d (offset: 0)
|
||||
Primitive: int32_t
|
||||
Child
|
||||
[1] Class: ClassB (size: 8)
|
||||
Parent (offset: 0)
|
||||
[0]
|
||||
Member: b (offset: 4)
|
||||
Primitive: int32_t
|
||||
Child
|
||||
[2] Class: ClassA (size: 16)
|
||||
Parent (offset: 0)
|
||||
[1]
|
||||
Parent (offset: 8)
|
||||
[3] Class: ClassC (size: 4)
|
||||
Member: c (offset: 0)
|
||||
Primitive: int32_t
|
||||
Child
|
||||
[2]
|
||||
Member: a (offset: 12)
|
||||
Primitive: int32_t
|
||||
)",
|
||||
R"(
|
||||
[0] Class: ClassD (size: 4)
|
||||
Member: d (offset: 0)
|
||||
Primitive: int32_t
|
||||
@ -687,16 +713,7 @@ TEST(FlattenerTest, ChildrenTwoDeep) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, ParentContainer) {
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
|
||||
auto vector = getVector();
|
||||
vector.templateParams.push_back(TemplateParam{myint});
|
||||
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 32};
|
||||
classA.parents.push_back(Parent{vector, 0});
|
||||
classA.members.push_back(Member{myint, "a", 24 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 32)
|
||||
Parent (offset: 0)
|
||||
[1] Container: std::vector (size: 24)
|
||||
@ -717,16 +734,7 @@ TEST(FlattenerTest, ParentContainer) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, ParentTwoContainers) {
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
|
||||
auto vector = getVector();
|
||||
vector.templateParams.push_back(TemplateParam{myint});
|
||||
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 48};
|
||||
classA.parents.push_back(Parent{vector, 0});
|
||||
classA.parents.push_back(Parent{vector, 24 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 48)
|
||||
Parent (offset: 0)
|
||||
[1] Container: std::vector (size: 24)
|
||||
@ -747,19 +755,7 @@ TEST(FlattenerTest, ParentTwoContainers) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, ParentClassAndContainer) {
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
|
||||
auto vector = getVector();
|
||||
vector.templateParams.push_back(TemplateParam{myint});
|
||||
|
||||
auto classB = Class{1, Class::Kind::Class, "ClassB", 4};
|
||||
classB.members.push_back(Member{myint, "b", 0});
|
||||
|
||||
auto classA = Class{0, Class::Kind::Class, "ClassA", 32};
|
||||
classA.parents.push_back(Parent{classB, 0});
|
||||
classA.parents.push_back(Parent{vector, 8 * 8});
|
||||
|
||||
test(Flattener::createPass(), {classA}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: ClassA (size: 32)
|
||||
Parent (offset: 0)
|
||||
[1] Class: ClassB (size: 4)
|
||||
@ -782,33 +778,7 @@ TEST(FlattenerTest, ParentClassAndContainer) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, AllocatorParamInParent) {
|
||||
ContainerInfo mapInfo{"std::map", STD_MAP_TYPE, "utility"};
|
||||
mapInfo.stubTemplateParams = {2, 3};
|
||||
|
||||
Primitive myint{Primitive::Kind::Int32};
|
||||
|
||||
auto pair = getPair(3);
|
||||
pair.templateParams.push_back(TemplateParam{myint, {Qualifier::Const}});
|
||||
pair.templateParams.push_back(TemplateParam{myint});
|
||||
|
||||
Class myallocBase{2, Class::Kind::Struct,
|
||||
"MyAllocBase<std::pair<const int, int>>", 1};
|
||||
myallocBase.templateParams.push_back(TemplateParam{pair});
|
||||
myallocBase.functions.push_back(Function{"allocate"});
|
||||
myallocBase.functions.push_back(Function{"deallocate"});
|
||||
|
||||
Class myalloc{1, Class::Kind::Struct, "MyAlloc<std::pair<const int, int>>",
|
||||
1};
|
||||
myalloc.parents.push_back(Parent{myallocBase, 0});
|
||||
myalloc.functions.push_back(Function{"allocate"});
|
||||
myalloc.functions.push_back(Function{"deallocate"});
|
||||
|
||||
Container map{0, mapInfo, 24};
|
||||
map.templateParams.push_back(TemplateParam{myint});
|
||||
map.templateParams.push_back(TemplateParam{myint});
|
||||
map.templateParams.push_back(TemplateParam{myalloc});
|
||||
|
||||
test(Flattener::createPass(), {map}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Container: std::map (size: 24)
|
||||
Param
|
||||
Primitive: int32_t
|
||||
@ -853,26 +823,7 @@ TEST(FlattenerTest, AllocatorParamInParent) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, AllocatorUnfixableNoParent) {
|
||||
Primitive myint{Primitive::Kind::Int32};
|
||||
|
||||
Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1};
|
||||
myalloc.functions.push_back(Function{"allocate"});
|
||||
myalloc.functions.push_back(Function{"deallocate"});
|
||||
|
||||
auto vector = getVector();
|
||||
vector.templateParams.push_back(TemplateParam{myint});
|
||||
vector.templateParams.push_back(TemplateParam{myalloc});
|
||||
|
||||
test(Flattener::createPass(), {vector}, R"(
|
||||
[0] Container: std::vector (size: 24)
|
||||
Param
|
||||
Primitive: int32_t
|
||||
Param
|
||||
[1] Struct: MyAlloc (size: 1)
|
||||
Function: allocate
|
||||
Function: deallocate
|
||||
)",
|
||||
R"(
|
||||
testNoChange(Flattener::createPass(), R"(
|
||||
[0] Container: std::vector (size: 24)
|
||||
Param
|
||||
Primitive: int32_t
|
||||
@ -885,26 +836,7 @@ TEST(FlattenerTest, AllocatorUnfixableNoParent) {
|
||||
|
||||
TEST(FlattenerTest, AllocatorUnfixableParentNotClass) {
|
||||
// This could be supported if need-be, we just don't do it yet
|
||||
Primitive myint{Primitive::Kind::Int32};
|
||||
|
||||
auto pair = getPair(3);
|
||||
pair.templateParams.push_back(TemplateParam{myint, {Qualifier::Const}});
|
||||
pair.templateParams.push_back(TemplateParam{myint});
|
||||
|
||||
ContainerInfo stdAllocatorInfo{"std::allocator", DUMMY_TYPE, "memory"};
|
||||
Container stdAllocator{2, stdAllocatorInfo, 1};
|
||||
stdAllocator.templateParams.push_back(TemplateParam{pair});
|
||||
|
||||
Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1};
|
||||
myalloc.parents.push_back(Parent{stdAllocator, 0});
|
||||
myalloc.functions.push_back(Function{"allocate"});
|
||||
myalloc.functions.push_back(Function{"deallocate"});
|
||||
|
||||
auto vector = getVector();
|
||||
vector.templateParams.push_back(TemplateParam{myint});
|
||||
vector.templateParams.push_back(TemplateParam{myalloc});
|
||||
|
||||
test(Flattener::createPass(), {vector}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Container: std::vector (size: 24)
|
||||
Param
|
||||
Primitive: int32_t
|
||||
@ -943,22 +875,7 @@ TEST(FlattenerTest, AllocatorUnfixableParentNotClass) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, AllocatorUnfixableParentNoParams) {
|
||||
Primitive myint{Primitive::Kind::Int32};
|
||||
|
||||
Class myallocBase{2, Class::Kind::Struct, "MyAllocBase", 1};
|
||||
myallocBase.functions.push_back(Function{"allocate"});
|
||||
myallocBase.functions.push_back(Function{"deallocate"});
|
||||
|
||||
Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1};
|
||||
myalloc.parents.push_back(Parent{myallocBase, 0});
|
||||
myalloc.functions.push_back(Function{"allocate"});
|
||||
myalloc.functions.push_back(Function{"deallocate"});
|
||||
|
||||
auto vector = getVector();
|
||||
vector.templateParams.push_back(TemplateParam{myint});
|
||||
vector.templateParams.push_back(TemplateParam{myalloc});
|
||||
|
||||
test(Flattener::createPass(), {vector}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Container: std::vector (size: 24)
|
||||
Param
|
||||
Primitive: int32_t
|
||||
@ -985,27 +902,7 @@ TEST(FlattenerTest, AllocatorUnfixableParentNoParams) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, AllocatorUnfixableParentParamIsValue) {
|
||||
ContainerInfo mapInfo{"std::map", STD_MAP_TYPE, "utility"};
|
||||
mapInfo.stubTemplateParams = {2, 3};
|
||||
|
||||
Primitive myint{Primitive::Kind::Int32};
|
||||
|
||||
Class myallocBase{2, Class::Kind::Struct, "MyAllocBase", 1};
|
||||
myallocBase.templateParams.push_back(TemplateParam{"123"});
|
||||
myallocBase.functions.push_back(Function{"allocate"});
|
||||
myallocBase.functions.push_back(Function{"deallocate"});
|
||||
|
||||
Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1};
|
||||
myalloc.parents.push_back(Parent{myallocBase, 0});
|
||||
myalloc.functions.push_back(Function{"allocate"});
|
||||
myalloc.functions.push_back(Function{"deallocate"});
|
||||
|
||||
Container map{0, mapInfo, 24};
|
||||
map.templateParams.push_back(TemplateParam{myint});
|
||||
map.templateParams.push_back(TemplateParam{myint});
|
||||
map.templateParams.push_back(TemplateParam{myalloc});
|
||||
|
||||
test(Flattener::createPass(), {map}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Container: std::map (size: 24)
|
||||
Param
|
||||
Primitive: int32_t
|
||||
@ -1038,16 +935,7 @@ TEST(FlattenerTest, AllocatorUnfixableParentParamIsValue) {
|
||||
}
|
||||
|
||||
TEST(FlattenerTest, ClassParam) {
|
||||
auto myint = Primitive{Primitive::Kind::Int32};
|
||||
auto mychild = Class{1, Class::Kind::Class, "MyChild", 4};
|
||||
auto myparent = Class{2, Class::Kind::Class, "MyParent", 4};
|
||||
myparent.members.push_back(Member{myint, "a", 0});
|
||||
mychild.parents.push_back(Parent{myparent, 0});
|
||||
|
||||
auto myclass = Class{0, Class::Kind::Class, "MyClass", 4};
|
||||
myclass.templateParams.push_back(TemplateParam{mychild});
|
||||
|
||||
test(Flattener::createPass(), {myclass}, R"(
|
||||
test(Flattener::createPass(), R"(
|
||||
[0] Class: MyClass (size: 4)
|
||||
Param
|
||||
[1] Class: MyChild (size: 4)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "oi/type_graph/PassManager.h"
|
||||
#include "oi/type_graph/Printer.h"
|
||||
#include "oi/type_graph/TypeGraph.h"
|
||||
#include "test/TypeGraphParser.h"
|
||||
|
||||
using type_graph::Container;
|
||||
using type_graph::NodeId;
|
||||
@ -26,10 +27,39 @@ void check(const std::vector<ref<Type>>& types,
|
||||
printer.print(type);
|
||||
}
|
||||
|
||||
expected.remove_prefix(1); // Remove initial '\n'
|
||||
if (expected[0] == '\n')
|
||||
expected.remove_prefix(1); // Remove initial '\n'
|
||||
ASSERT_EQ(expected, out.str()) << "Test failure " << comment;
|
||||
}
|
||||
|
||||
void test(type_graph::Pass pass,
|
||||
std::string_view input,
|
||||
std::string_view expectedAfter) {
|
||||
input.remove_prefix(1); // Remove initial '\n'
|
||||
TypeGraph typeGraph;
|
||||
TypeGraphParser parser{typeGraph};
|
||||
parser.parse(input);
|
||||
|
||||
// Validate input formatting
|
||||
check(typeGraph.rootTypes(), input, " parsing input graph");
|
||||
|
||||
// Run pass and check results
|
||||
test(pass, typeGraph.rootTypes(), expectedAfter);
|
||||
}
|
||||
|
||||
void testNoChange(type_graph::Pass pass, std::string_view input) {
|
||||
input.remove_prefix(1); // Remove initial '\n'
|
||||
TypeGraph typeGraph;
|
||||
TypeGraphParser parser{typeGraph};
|
||||
parser.parse(input);
|
||||
|
||||
// Validate input formatting
|
||||
check(typeGraph.rootTypes(), input, " parsing input graph");
|
||||
|
||||
// Run pass and check results
|
||||
test(pass, typeGraph.rootTypes(), input);
|
||||
}
|
||||
|
||||
void test(type_graph::Pass pass,
|
||||
std::vector<ref<Type>> rootTypes,
|
||||
std::string_view expectedBefore,
|
||||
|
@ -14,6 +14,12 @@ void check(const std::vector<std::reference_wrapper<type_graph::Type>>& types,
|
||||
std::string_view expected,
|
||||
std::string_view comment);
|
||||
|
||||
void test(type_graph::Pass pass,
|
||||
std::string_view input,
|
||||
std::string_view expectedAfter);
|
||||
|
||||
void testNoChange(type_graph::Pass pass, std::string_view input);
|
||||
|
||||
void test(type_graph::Pass pass,
|
||||
std::vector<std::reference_wrapper<type_graph::Type>> rootTypes,
|
||||
std::string_view expectedBefore,
|
||||
|
Loading…
Reference in New Issue
Block a user