mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-11-09 21:24:14 +00:00
TypeGraph: Fix handling for classes which inherit from containers
We previously moved container identification later in CodeGen in order to preserve information for AlignmentCalc. However, Flattener needs to know if a class is a container in order to apply its special handling for this case. This new approach moves container identification in front of Flattener, but has Container own a type node, representing its layout. This underlying type node can be used for calculating a container's alignment in a later pass.
This commit is contained in:
parent
aa87c3f2d1
commit
688d483c0c
@ -1157,9 +1157,9 @@ void CodeGen::transform(TypeGraph& typeGraph) {
|
|||||||
|
|
||||||
// Simplify the type graph first so there is less work for later passes
|
// Simplify the type graph first so there is less work for later passes
|
||||||
pm.addPass(RemoveTopLevelPointer::createPass());
|
pm.addPass(RemoveTopLevelPointer::createPass());
|
||||||
|
pm.addPass(IdentifyContainers::createPass(containerInfos_));
|
||||||
pm.addPass(Flattener::createPass());
|
pm.addPass(Flattener::createPass());
|
||||||
pm.addPass(AlignmentCalc::createPass());
|
pm.addPass(AlignmentCalc::createPass());
|
||||||
pm.addPass(IdentifyContainers::createPass(containerInfos_));
|
|
||||||
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
|
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
|
||||||
if (config_.features[Feature::PruneTypeGraph])
|
if (config_.features[Feature::PruneTypeGraph])
|
||||||
pm.addPass(Prune::createPass());
|
pm.addPass(Prune::createPass());
|
||||||
@ -1173,9 +1173,9 @@ void CodeGen::transform(TypeGraph& typeGraph) {
|
|||||||
pm.addPass(AddChildren::createPass(drgnParser, symbols_));
|
pm.addPass(AddChildren::createPass(drgnParser, symbols_));
|
||||||
|
|
||||||
// Re-run passes over newly added children
|
// Re-run passes over newly added children
|
||||||
|
pm.addPass(IdentifyContainers::createPass(containerInfos_));
|
||||||
pm.addPass(Flattener::createPass());
|
pm.addPass(Flattener::createPass());
|
||||||
pm.addPass(AlignmentCalc::createPass());
|
pm.addPass(AlignmentCalc::createPass());
|
||||||
pm.addPass(IdentifyContainers::createPass(containerInfos_));
|
|
||||||
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
|
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
|
||||||
if (config_.features[Feature::PruneTypeGraph])
|
if (config_.features[Feature::PruneTypeGraph])
|
||||||
pm.addPass(Prune::createPass());
|
pm.addPass(Prune::createPass());
|
||||||
|
@ -48,15 +48,7 @@ void AlignmentCalc::accept(Type& type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AlignmentCalc::visit(Class& c) {
|
void AlignmentCalc::visit(Class& c) {
|
||||||
for (const auto& param : c.templateParams) {
|
RecursiveVisitor::visit(c);
|
||||||
accept(param.type());
|
|
||||||
}
|
|
||||||
for (const auto& parent : c.parents) {
|
|
||||||
accept(parent.type());
|
|
||||||
}
|
|
||||||
for (const auto& child : c.children) {
|
|
||||||
accept(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t alignment = 1;
|
uint64_t alignment = 1;
|
||||||
for (auto& member : c.members) {
|
for (auto& member : c.members) {
|
||||||
@ -82,4 +74,12 @@ void AlignmentCalc::visit(Class& c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlignmentCalc::visit(Container& c) {
|
||||||
|
RecursiveVisitor::visit(c);
|
||||||
|
|
||||||
|
if (c.underlying()) {
|
||||||
|
c.setAlign(c.underlying()->align());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace oi::detail::type_graph
|
} // namespace oi::detail::type_graph
|
||||||
|
@ -41,6 +41,7 @@ class AlignmentCalc final : public RecursiveVisitor {
|
|||||||
|
|
||||||
void accept(Type& type) override;
|
void accept(Type& type) override;
|
||||||
void visit(Class& c) override;
|
void visit(Class& c) override;
|
||||||
|
void visit(Container& c) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_set<Type*> visited_;
|
std::unordered_set<Type*> visited_;
|
||||||
|
@ -81,7 +81,7 @@ class DrgnParser {
|
|||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
T& makeType(struct drgn_type* drgnType, Args&&... args) {
|
T& makeType(struct drgn_type* drgnType, Args&&... args) {
|
||||||
auto& newType = typeGraph_.makeType<T>(std::forward<Args>(args)...);
|
auto& newType = typeGraph_.makeType<T>(std::forward<Args>(args)...);
|
||||||
drgn_types_.insert({drgnType, newType});
|
drgn_types_.insert_or_assign(drgnType, newType);
|
||||||
return newType;
|
return newType;
|
||||||
}
|
}
|
||||||
bool chasePointer() const;
|
bool chasePointer() const;
|
||||||
|
@ -134,8 +134,6 @@ void Flattener::visit(Class& c) {
|
|||||||
// };
|
// };
|
||||||
// TODO comment about virtual inheritance
|
// TODO comment about virtual inheritance
|
||||||
|
|
||||||
// TODO alignment of parent classes
|
|
||||||
|
|
||||||
// Flatten types referenced by template params, parents and members
|
// Flatten types referenced by template params, parents and members
|
||||||
for (const auto& param : c.templateParams) {
|
for (const auto& param : c.templateParams) {
|
||||||
accept(param.type());
|
accept(param.type());
|
||||||
@ -207,12 +205,4 @@ void Flattener::visit(Class& c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flattener::visit(Container& c) {
|
|
||||||
// Containers themselves don't need to be flattened, but their template
|
|
||||||
// parameters might need to be
|
|
||||||
for (const auto& templateParam : c.templateParams) {
|
|
||||||
accept(templateParam.type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace oi::detail::type_graph
|
} // namespace oi::detail::type_graph
|
||||||
|
@ -42,7 +42,6 @@ class Flattener : public RecursiveVisitor {
|
|||||||
|
|
||||||
void accept(Type& type) override;
|
void accept(Type& type) override;
|
||||||
void visit(Class& c) override;
|
void visit(Class& c) override;
|
||||||
void visit(Container& c) override;
|
|
||||||
|
|
||||||
static const inline std::string ParentPrefix = "__oi_parent";
|
static const inline std::string ParentPrefix = "__oi_parent";
|
||||||
|
|
||||||
|
@ -57,11 +57,12 @@ Type& IdentifyContainers::visit(Class& c) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& container = typeGraph_.makeType<Container>(*containerInfo, c.size());
|
auto& container =
|
||||||
|
typeGraph_.makeType<Container>(*containerInfo, c.size(), &c);
|
||||||
container.templateParams = c.templateParams;
|
container.templateParams = c.templateParams;
|
||||||
|
|
||||||
tracker_.set(c, &container);
|
tracker_.set(c, &container);
|
||||||
RecursiveMutator::visit(container);
|
visit(container);
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,4 +71,15 @@ Type& IdentifyContainers::visit(Class& c) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type& IdentifyContainers::visit(Container& c) {
|
||||||
|
for (auto& param : c.templateParams) {
|
||||||
|
param.setType(mutate(param.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not mutate the underlying class further here as that would result in it
|
||||||
|
// getting replaced with this Container node
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace oi::detail::type_graph
|
} // namespace oi::detail::type_graph
|
||||||
|
@ -48,6 +48,7 @@ class IdentifyContainers : public RecursiveMutator {
|
|||||||
|
|
||||||
Type& mutate(Type& type) override;
|
Type& mutate(Type& type) override;
|
||||||
Type& visit(Class& c) override;
|
Type& visit(Class& c) override;
|
||||||
|
Type& visit(Container& c) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ResultTracker<Type*> tracker_;
|
ResultTracker<Type*> tracker_;
|
||||||
|
@ -85,7 +85,7 @@ void Printer::visit(const Class& c) {
|
|||||||
print_function(function);
|
print_function(function);
|
||||||
}
|
}
|
||||||
for (auto& child : c.children) {
|
for (auto& child : c.children) {
|
||||||
print_child(child);
|
print_type("Child", child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +93,14 @@ void Printer::visit(const Container& c) {
|
|||||||
if (prefix(c))
|
if (prefix(c))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
out_ << "Container: " << c.name() << " (size: " << c.size() << ")"
|
out_ << "Container: " << c.name() << " (size: " << c.size()
|
||||||
<< std::endl;
|
<< align_str(c.align()) << ")" << std::endl;
|
||||||
for (const auto& param : c.templateParams) {
|
for (const auto& param : c.templateParams) {
|
||||||
print_param(param);
|
print_param(param);
|
||||||
}
|
}
|
||||||
|
if (c.underlying()) {
|
||||||
|
print_type("Underlying", *c.underlying());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Printer::visit(const Primitive& p) {
|
void Printer::visit(const Primitive& p) {
|
||||||
@ -240,11 +243,11 @@ void Printer::print_function(const Function& function) {
|
|||||||
depth_--;
|
depth_--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Printer::print_child(const Type& child) {
|
void Printer::print_type(std::string_view header, const Type& type) {
|
||||||
depth_++;
|
depth_++;
|
||||||
prefix();
|
prefix();
|
||||||
out_ << "Child" << std::endl;
|
out_ << header << std::endl;
|
||||||
print(child);
|
print(type);
|
||||||
depth_--;
|
depth_--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class Printer : public ConstVisitor {
|
|||||||
void print_parent(const Parent& parent);
|
void print_parent(const Parent& parent);
|
||||||
void print_member(const Member& member);
|
void print_member(const Member& member);
|
||||||
void print_function(const Function& function);
|
void print_function(const Function& function);
|
||||||
void print_child(const Type& child);
|
void print_type(std::string_view header, const Type& type);
|
||||||
void print_value(const std::string& value);
|
void print_value(const std::string& value);
|
||||||
void print_qualifiers(const QualifierSet& qualifiers);
|
void print_qualifiers(const QualifierSet& qualifiers);
|
||||||
void print_enumerator(int64_t val, const std::string& name);
|
void print_enumerator(int64_t val, const std::string& name);
|
||||||
|
@ -39,18 +39,7 @@ void Prune::accept(Type& type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Prune::visit(Class& c) {
|
void Prune::visit(Class& c) {
|
||||||
for (const auto& param : c.templateParams) {
|
RecursiveVisitor::visit(c);
|
||||||
accept(param.type());
|
|
||||||
}
|
|
||||||
for (const auto& parent : c.parents) {
|
|
||||||
accept(parent.type());
|
|
||||||
}
|
|
||||||
for (const auto& member : c.members) {
|
|
||||||
accept(member.type());
|
|
||||||
}
|
|
||||||
for (const auto& child : c.children) {
|
|
||||||
accept(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.templateParams.clear();
|
c.templateParams.clear();
|
||||||
c.parents.clear();
|
c.parents.clear();
|
||||||
@ -62,4 +51,10 @@ void Prune::visit(Class& c) {
|
|||||||
c.functions.shrink_to_fit();
|
c.functions.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Prune::visit(Container& c) {
|
||||||
|
RecursiveVisitor::visit(c);
|
||||||
|
|
||||||
|
c.setUnderlying(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace oi::detail::type_graph
|
} // namespace oi::detail::type_graph
|
||||||
|
@ -43,6 +43,7 @@ class Prune : public RecursiveVisitor {
|
|||||||
|
|
||||||
void accept(Type& type) override;
|
void accept(Type& type) override;
|
||||||
void visit(Class& c) override;
|
void visit(Class& c) override;
|
||||||
|
void visit(Container& c) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeTracker& tracker_;
|
NodeTracker& tracker_;
|
||||||
|
@ -79,7 +79,8 @@ void TypeIdentifier::visit(Container& c) {
|
|||||||
it != passThroughTypeDummys_.end()) {
|
it != passThroughTypeDummys_.end()) {
|
||||||
dummy = &it->second.get();
|
dummy = &it->second.get();
|
||||||
} else {
|
} else {
|
||||||
dummy = &typeGraph_.makeType<Container>(info, param.type().size());
|
dummy = &typeGraph_.makeType<Container>(
|
||||||
|
info, param.type().size(), paramClass);
|
||||||
dummy->templateParams = paramClass->templateParams;
|
dummy->templateParams = paramClass->templateParams;
|
||||||
passThroughTypeDummys_.insert(it,
|
passThroughTypeDummys_.insert(it,
|
||||||
{paramClass->id(), std::ref(*dummy)});
|
{paramClass->id(), std::ref(*dummy)});
|
||||||
|
@ -150,7 +150,6 @@ struct Function {
|
|||||||
int virtuality;
|
int virtuality;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Class;
|
|
||||||
struct Parent {
|
struct Parent {
|
||||||
Parent(Type& type, uint64_t bitOffset) : type_(type), bitOffset(bitOffset) {
|
Parent(Type& type, uint64_t bitOffset) : type_(type), bitOffset(bitOffset) {
|
||||||
}
|
}
|
||||||
@ -298,10 +297,6 @@ class Class : public Type {
|
|||||||
|
|
||||||
DECLARE_ACCEPT
|
DECLARE_ACCEPT
|
||||||
|
|
||||||
Kind kind() const {
|
|
||||||
return kind_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const std::string& name() const override {
|
virtual const std::string& name() const override {
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
@ -326,12 +321,16 @@ class Class : public Type {
|
|||||||
return align_;
|
return align_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAlign(uint64_t alignment) {
|
||||||
|
align_ = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
virtual NodeId id() const override {
|
virtual NodeId id() const override {
|
||||||
return id_;
|
return id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAlign(uint64_t alignment) {
|
Kind kind() const {
|
||||||
align_ = alignment;
|
return kind_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int virtuality() const {
|
int virtuality() const {
|
||||||
@ -371,10 +370,19 @@ class Class : public Type {
|
|||||||
bool packed_ = false;
|
bool packed_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Container
|
||||||
|
*
|
||||||
|
* A type of class for which we can do special processing.
|
||||||
|
*/
|
||||||
class Container : public Type {
|
class Container : public Type {
|
||||||
public:
|
public:
|
||||||
Container(NodeId id, const ContainerInfo& containerInfo, size_t size)
|
Container(NodeId id,
|
||||||
|
const ContainerInfo& containerInfo,
|
||||||
|
size_t size,
|
||||||
|
Type* underlying)
|
||||||
: containerInfo_(containerInfo),
|
: containerInfo_(containerInfo),
|
||||||
|
underlying_(underlying),
|
||||||
name_(containerInfo.typeName),
|
name_(containerInfo.typeName),
|
||||||
inputName_(containerInfo.typeName),
|
inputName_(containerInfo.typeName),
|
||||||
size_(size),
|
size_(size),
|
||||||
@ -386,6 +394,7 @@ class Container : public Type {
|
|||||||
const ContainerInfo& containerInfo)
|
const ContainerInfo& containerInfo)
|
||||||
: templateParams(other.templateParams),
|
: templateParams(other.templateParams),
|
||||||
containerInfo_(containerInfo),
|
containerInfo_(containerInfo),
|
||||||
|
underlying_(other.underlying_),
|
||||||
name_(other.name_),
|
name_(other.name_),
|
||||||
inputName_(other.inputName_),
|
inputName_(other.inputName_),
|
||||||
size_(other.size_),
|
size_(other.size_),
|
||||||
@ -396,22 +405,18 @@ class Container : public Type {
|
|||||||
|
|
||||||
DECLARE_ACCEPT
|
DECLARE_ACCEPT
|
||||||
|
|
||||||
const std::string& containerName() const {
|
|
||||||
return containerInfo_.typeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const std::string& name() const override {
|
virtual const std::string& name() const override {
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setName(std::string name) {
|
|
||||||
name_ = std::move(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual std::string_view inputName() const override {
|
virtual std::string_view inputName() const override {
|
||||||
return inputName_;
|
return inputName_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setName(std::string name) {
|
||||||
|
name_ = std::move(name);
|
||||||
|
}
|
||||||
|
|
||||||
void setInputName(std::string name) {
|
void setInputName(std::string name) {
|
||||||
inputName_ = std::move(name);
|
inputName_ = std::move(name);
|
||||||
}
|
}
|
||||||
@ -424,18 +429,31 @@ class Container : public Type {
|
|||||||
return align_;
|
return align_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAlign(uint64_t alignment) {
|
||||||
|
align_ = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
virtual NodeId id() const override {
|
virtual NodeId id() const override {
|
||||||
return id_;
|
return id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAlign(uint64_t alignment) {
|
const std::string& containerName() const {
|
||||||
align_ = alignment;
|
return containerInfo_.typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type* underlying() const {
|
||||||
|
return underlying_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUnderlying(Type* underlying) {
|
||||||
|
underlying_ = underlying;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TemplateParam> templateParams;
|
std::vector<TemplateParam> templateParams;
|
||||||
const ContainerInfo& containerInfo_;
|
const ContainerInfo& containerInfo_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Type* underlying_;
|
||||||
std::string name_;
|
std::string name_;
|
||||||
std::string inputName_;
|
std::string inputName_;
|
||||||
size_t size_;
|
size_t size_;
|
||||||
|
@ -93,6 +93,7 @@ class RecursiveVisitor : public Visitor<void> {
|
|||||||
for (const auto& param : c.templateParams) {
|
for (const auto& param : c.templateParams) {
|
||||||
accept(param.type());
|
accept(param.type());
|
||||||
}
|
}
|
||||||
|
accept(c.underlying());
|
||||||
}
|
}
|
||||||
virtual void visit(Primitive&) {
|
virtual void visit(Primitive&) {
|
||||||
}
|
}
|
||||||
@ -126,6 +127,11 @@ class RecursiveMutator : public Visitor<Type&> {
|
|||||||
public:
|
public:
|
||||||
virtual ~RecursiveMutator() = default;
|
virtual ~RecursiveMutator() = default;
|
||||||
virtual Type& mutate(Type&) = 0;
|
virtual Type& mutate(Type&) = 0;
|
||||||
|
virtual Type* mutate(Type* type) {
|
||||||
|
if (type)
|
||||||
|
return &mutate(*type);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
virtual Type& visit(Incomplete& i) {
|
virtual Type& visit(Incomplete& i) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -148,6 +154,7 @@ class RecursiveMutator : public Visitor<Type&> {
|
|||||||
for (auto& param : c.templateParams) {
|
for (auto& param : c.templateParams) {
|
||||||
param.setType(mutate(param.type()));
|
param.setType(mutate(param.type()));
|
||||||
}
|
}
|
||||||
|
c.setUnderlying(mutate(c.underlying()));
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
virtual Type& visit(Primitive& p) {
|
virtual Type& visit(Primitive& p) {
|
||||||
|
@ -265,10 +265,11 @@ Type& TypeGraphParser::parseType(std::string_view& input, size_t rootIndent) {
|
|||||||
|
|
||||||
auto size = parseNumericAttribute(line, nodeTypeName, "size: ");
|
auto size = parseNumericAttribute(line, nodeTypeName, "size: ");
|
||||||
|
|
||||||
Container& c = typeGraph_.makeType<Container>(id, info, size);
|
Container& c = typeGraph_.makeType<Container>(id, info, size, nullptr);
|
||||||
nodesById_.insert({id, c});
|
nodesById_.insert({id, c});
|
||||||
|
|
||||||
parseParams(c, input, indent + 2);
|
parseParams(c, input, indent + 2);
|
||||||
|
parseUnderlying(c, input, indent + 2);
|
||||||
|
|
||||||
type = &c;
|
type = &c;
|
||||||
} else if (nodeTypeName == "Primitive") {
|
} else if (nodeTypeName == "Primitive") {
|
||||||
@ -452,3 +453,26 @@ void TypeGraphParser::parseChildren(Class& c,
|
|||||||
// No more children for us - put back the line we just read
|
// No more children for us - put back the line we just read
|
||||||
input = origInput;
|
input = origInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeGraphParser::parseUnderlying(Container& c,
|
||||||
|
std::string_view& input,
|
||||||
|
size_t rootIndent) {
|
||||||
|
std::string_view origInput = input;
|
||||||
|
std::string_view line;
|
||||||
|
getline(input, line);
|
||||||
|
|
||||||
|
size_t indent = stripIndent(line);
|
||||||
|
if (indent != rootIndent) {
|
||||||
|
input = origInput;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format: "Underlying"
|
||||||
|
if (!tryRemovePrefix(line, "Underlying")) {
|
||||||
|
input = origInput;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type& type = parseType(input, rootIndent + 2);
|
||||||
|
c.setUnderlying(&type);
|
||||||
|
}
|
||||||
|
@ -34,6 +34,9 @@ class TypeGraphParser {
|
|||||||
void parseMembers(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 parseFunctions(Class& c, std::string_view& input, size_t rootIndent);
|
||||||
void parseChildren(Class& c, std::string_view& input, size_t rootIndent);
|
void parseChildren(Class& c, std::string_view& input, size_t rootIndent);
|
||||||
|
void parseUnderlying(Container& c,
|
||||||
|
std::string_view& input,
|
||||||
|
size_t rootIndent);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TypeGraphParserError : public std::runtime_error {
|
class TypeGraphParserError : public std::runtime_error {
|
||||||
|
@ -106,7 +106,7 @@ TEST_F(AddChildrenTest, InheritancePolymorphic) {
|
|||||||
Function: ~allocator
|
Function: ~allocator
|
||||||
Function: allocate
|
Function: allocate
|
||||||
Function: deallocate
|
Function: deallocate
|
||||||
*
|
*
|
||||||
Function: ~B (virtual)
|
Function: ~B (virtual)
|
||||||
Function: myfunc (virtual)
|
Function: myfunc (virtual)
|
||||||
Function: B
|
Function: B
|
||||||
@ -159,7 +159,7 @@ TEST_F(AddChildrenTest, InheritancePolymorphic) {
|
|||||||
Function: ~allocator
|
Function: ~allocator
|
||||||
Function: allocate
|
Function: allocate
|
||||||
Function: deallocate
|
Function: deallocate
|
||||||
*
|
*
|
||||||
Function: operator=
|
Function: operator=
|
||||||
Function: B
|
Function: B
|
||||||
Function: B
|
Function: B
|
||||||
|
@ -58,16 +58,16 @@ TEST(AlignmentCalcTest, StructInContainer) {
|
|||||||
Member: n (offset: 0)
|
Member: n (offset: 0)
|
||||||
Primitive: int8_t
|
Primitive: int8_t
|
||||||
Member: n (offset: 8)
|
Member: n (offset: 8)
|
||||||
Primitive: int64_t
|
Primitive: int32_t
|
||||||
)",
|
)",
|
||||||
R"(
|
R"(
|
||||||
[0] Container: std::vector (size: 8)
|
[0] Container: std::vector (size: 8)
|
||||||
Param
|
Param
|
||||||
[1] Class: MyClass (size: 16, align: 8)
|
[1] Class: MyClass (size: 16, align: 4)
|
||||||
Member: n (offset: 0, align: 1)
|
Member: n (offset: 0, align: 1)
|
||||||
Primitive: int8_t
|
Primitive: int8_t
|
||||||
Member: n (offset: 8, align: 8)
|
Member: n (offset: 8, align: 4)
|
||||||
Primitive: int64_t
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,3 +264,33 @@ TEST(AlignmentCalcTest, Typedef) {
|
|||||||
Primitive: int8_t
|
Primitive: int8_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(AlignmentCalcTest, Container) {
|
||||||
|
test(AlignmentCalc::createPass(),
|
||||||
|
R"(
|
||||||
|
[0] Container: std::vector (size: 24)
|
||||||
|
Underlying
|
||||||
|
[1] Class: vector (size: 24)
|
||||||
|
Member: n (offset: 0)
|
||||||
|
Primitive: int8_t
|
||||||
|
Member: s (offset: 4)
|
||||||
|
[2] Struct: MyStruct (size: 8)
|
||||||
|
Member: n1 (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: n2 (offset: 4)
|
||||||
|
Primitive: int32_t
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Container: std::vector (size: 24, align: 4)
|
||||||
|
Underlying
|
||||||
|
[1] Class: vector (size: 24, align: 4)
|
||||||
|
Member: n (offset: 0, align: 1)
|
||||||
|
Primitive: int8_t
|
||||||
|
Member: s (offset: 4, align: 4)
|
||||||
|
[2] Struct: MyStruct (size: 8, align: 4)
|
||||||
|
Member: n1 (offset: 0, align: 4)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: n2 (offset: 4, align: 4)
|
||||||
|
Primitive: int32_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
@ -45,6 +45,8 @@ void testTransform(OICodeGen::Config& config,
|
|||||||
|
|
||||||
void testTransform(std::string_view input, std::string_view expectedAfter) {
|
void testTransform(std::string_view input, std::string_view expectedAfter) {
|
||||||
OICodeGen::Config config;
|
OICodeGen::Config config;
|
||||||
|
config.features[Feature::PruneTypeGraph] = true;
|
||||||
|
config.features[Feature::TreeBuilderV2] = true;
|
||||||
testTransform(config, input, expectedAfter);
|
testTransform(config, input, expectedAfter);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -62,7 +64,7 @@ TEST(CodeGenTest, TransformContainerAllocator) {
|
|||||||
Function: deallocate
|
Function: deallocate
|
||||||
)",
|
)",
|
||||||
R"(
|
R"(
|
||||||
[2] Container: std::vector<int32_t, DummyAllocator<int32_t, 8, 1, 3>> (size: 24)
|
[2] Container: std::vector<int32_t, DummyAllocator<int32_t, 8, 1, 3>> (size: 24, align: 1)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
Param
|
Param
|
||||||
@ -95,14 +97,14 @@ TEST(CodeGenTest, TransformContainerAllocatorParamInParent) {
|
|||||||
Function: deallocate
|
Function: deallocate
|
||||||
)",
|
)",
|
||||||
R"(
|
R"(
|
||||||
[4] Container: std::map<int32_t, int32_t, DummyAllocator<std::pair<int32_t const, int32_t>, 0, 1, 6>> (size: 24)
|
[4] Container: std::map<int32_t, int32_t, DummyAllocator<std::pair<int32_t const, int32_t>, 0, 1, 6>> (size: 24, align: 1)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
Param
|
Param
|
||||||
[6] DummyAllocator [MyAlloc<std::pair<const int, int>>] (size: 0, align: 1)
|
[6] DummyAllocator [MyAlloc<std::pair<const int, int>>] (size: 0, align: 1)
|
||||||
[5] Container: std::pair<int32_t const, int32_t> (size: 8)
|
[5] Container: std::pair<int32_t const, int32_t> (size: 8, align: 1)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
Qualifiers: const
|
Qualifiers: const
|
||||||
@ -168,7 +170,7 @@ TEST(CodeGenTest, ReplaceContainersAndDummies) {
|
|||||||
Function: deallocate
|
Function: deallocate
|
||||||
)",
|
)",
|
||||||
R"(
|
R"(
|
||||||
[2] Container: std::vector<uint32_t, DummyAllocator<uint32_t, 0, 1, 3>> (size: 24)
|
[2] Container: std::vector<uint32_t, DummyAllocator<uint32_t, 0, 1, 3>> (size: 24, align: 1)
|
||||||
Param
|
Param
|
||||||
Primitive: uint32_t
|
Primitive: uint32_t
|
||||||
Param
|
Param
|
||||||
@ -176,3 +178,73 @@ TEST(CodeGenTest, ReplaceContainersAndDummies) {
|
|||||||
Primitive: uint32_t
|
Primitive: uint32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CodeGenTest, ContainerAlignment) {
|
||||||
|
testTransform(R"(
|
||||||
|
[0] Class: MyClass (size: 24)
|
||||||
|
Member: container (offset: 0)
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: __impl__ (offset: 0)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
Member: __impl__ (offset: 8)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
Member: __impl__ (offset: 16)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass_0 [MyClass] (size: 24, align: 8)
|
||||||
|
Member: container_0 [container] (offset: 0, align: 8)
|
||||||
|
[2] Container: std::vector<int32_t> (size: 24, align: 8)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CodeGenTest, InheritFromContainer) {
|
||||||
|
testTransform(R"(
|
||||||
|
[0] Class: MyClass (size: 24)
|
||||||
|
Parent (offset: 0)
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: __impl__ (offset: 0)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
Member: __impl__ (offset: 8)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
Member: __impl__ (offset: 16)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass_0 [MyClass] (size: 24, align: 8)
|
||||||
|
Member: __oi_parent_0 [__oi_parent] (offset: 0, align: 8)
|
||||||
|
[2] Container: std::vector<int32_t> (size: 24, align: 8)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CodeGenTest, InheritFromContainerCompat) {
|
||||||
|
OICodeGen::Config config;
|
||||||
|
testTransform(config,
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass (size: 24)
|
||||||
|
Parent (offset: 0)
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: __impl__ (offset: 0)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
Member: __impl__ (offset: 8)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
Member: __impl__ (offset: 16)
|
||||||
|
Primitive: StubbedPointer
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass_0 [MyClass] (size: 24, align: 8)
|
||||||
|
Member: __oi_padding_0 (offset: 0)
|
||||||
|
[3] Array: [int8_t[24]] (length: 24)
|
||||||
|
Primitive: int8_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
@ -799,6 +799,27 @@ TEST(FlattenerTest, ParentClassAndContainer) {
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FlattenerTest, ContainerWithParent) {
|
||||||
|
// This is necessary to correctly calculate container alignment
|
||||||
|
test(Flattener::createPass(),
|
||||||
|
R"(
|
||||||
|
[0] Container: std::vector (size: 24)
|
||||||
|
Underlying
|
||||||
|
[1] Class: vector (size: 24)
|
||||||
|
Parent (offset: 0)
|
||||||
|
[2] Class: Parent (size: 4)
|
||||||
|
Member: x (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Container: std::vector (size: 24)
|
||||||
|
Underlying
|
||||||
|
[1] Class: vector (size: 24)
|
||||||
|
Member: x (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(FlattenerTest, AllocatorParamInParent) {
|
TEST(FlattenerTest, AllocatorParamInParent) {
|
||||||
test(Flattener::createPass(),
|
test(Flattener::createPass(),
|
||||||
R"(
|
R"(
|
||||||
@ -857,7 +878,7 @@ TEST(FlattenerTest, AllocatorUnfixableNoParent) {
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FlattenerTest, AllocatorUnfixableParentNotClass) {
|
TEST(FlattenerTest, AllocatorParamInParentContainer) {
|
||||||
// This could be supported if need-be, we just don't do it yet
|
// This could be supported if need-be, we just don't do it yet
|
||||||
test(Flattener::createPass(),
|
test(Flattener::createPass(),
|
||||||
R"(
|
R"(
|
||||||
|
@ -27,6 +27,12 @@ TEST(IdentifyContainers, Container) {
|
|||||||
[1] Container: std::vector (size: 24)
|
[1] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[0] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,14 +58,26 @@ TEST(IdentifyContainers, ContainerInClass) {
|
|||||||
[4] Container: std::vector (size: 24)
|
[4] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
Parent (offset: 0)
|
Parent (offset: 0)
|
||||||
[5] Container: std::vector (size: 24)
|
[5] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[2] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
Member: a (offset: 0)
|
Member: a (offset: 0)
|
||||||
[6] Container: std::vector (size: 24)
|
[6] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[3] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +95,14 @@ TEST(IdentifyContainers, ContainerInContainer) {
|
|||||||
[3] Container: std::vector (size: 24)
|
[3] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[0] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
[1]
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +120,10 @@ TEST(IdentifyContainers, ContainerInContainer2) {
|
|||||||
[2] Container: std::vector (size: 24)
|
[2] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +139,10 @@ TEST(IdentifyContainers, ContainerInArray) {
|
|||||||
[2] Container: std::vector (size: 24)
|
[2] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +158,10 @@ TEST(IdentifyContainers, ContainerInTypedef) {
|
|||||||
[2] Container: std::vector (size: 24)
|
[2] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +177,10 @@ TEST(IdentifyContainers, ContainerInPointer) {
|
|||||||
[2] Container: std::vector (size: 24)
|
[2] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +197,12 @@ TEST(IdentifyContainers, ContainerDuplicate) {
|
|||||||
[1] Container: std::vector (size: 24)
|
[1] Container: std::vector (size: 24)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[0] Class: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
[1]
|
[1]
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
@ -190,5 +238,9 @@ TEST(IdentifyContainers, CycleContainer) {
|
|||||||
[2] Container: std::vector (size: 0)
|
[2] Container: std::vector (size: 0)
|
||||||
Param
|
Param
|
||||||
[0]
|
[0]
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::vector (size: 0)
|
||||||
|
Param
|
||||||
|
[0]
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -63,3 +63,33 @@ TEST(PruneTest, RecurseClassChild) {
|
|||||||
[1] Class: ClassA (size: 12)
|
[1] Class: ClassA (size: 12)
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PruneTest, PruneContainer) {
|
||||||
|
test(Prune::createPass(),
|
||||||
|
R"(
|
||||||
|
[0] Container: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Param
|
||||||
|
Value: "123"
|
||||||
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: vector<int32_t> (size: 24)
|
||||||
|
Parent (offset: 0)
|
||||||
|
[2] Class: MyParent (size: 4)
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: b (offset: 4)
|
||||||
|
Primitive: int32_t
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Container: std::vector (size: 24)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Param
|
||||||
|
Value: "123"
|
||||||
|
Primitive: int32_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
@ -108,6 +108,12 @@ TEST(TypeIdentifierTest, PassThroughTypes) {
|
|||||||
[2] Container: std::allocator (size: 1)
|
[2] Container: std::allocator (size: 1)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::allocator (size: 1)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Function: allocate
|
||||||
|
Function: deallocate
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +143,12 @@ TEST(TypeIdentifierTest, PassThroughSameType) {
|
|||||||
[2] Container: std::allocator (size: 1)
|
[2] Container: std::allocator (size: 1)
|
||||||
Param
|
Param
|
||||||
Primitive: int32_t
|
Primitive: int32_t
|
||||||
|
Underlying
|
||||||
|
[1] Class: std::allocator (size: 1)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Function: allocate
|
||||||
|
Function: deallocate
|
||||||
Param
|
Param
|
||||||
[2]
|
[2]
|
||||||
)");
|
)");
|
||||||
|
@ -86,22 +86,23 @@ std::vector<std::unique_ptr<ContainerInfo>> getContainerInfos() {
|
|||||||
Container getVector(NodeId id) {
|
Container getVector(NodeId id) {
|
||||||
static ContainerInfo info{"std::vector", SEQ_TYPE, "vector"};
|
static ContainerInfo info{"std::vector", SEQ_TYPE, "vector"};
|
||||||
info.stubTemplateParams = {1};
|
info.stubTemplateParams = {1};
|
||||||
return Container{id, info, 24};
|
return Container{id, info, 24, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
Container getMap(NodeId id) {
|
Container getMap(NodeId id) {
|
||||||
static ContainerInfo info{"std::map", STD_MAP_TYPE, "map"};
|
static ContainerInfo info{"std::map", STD_MAP_TYPE, "map"};
|
||||||
info.stubTemplateParams = {2, 3};
|
info.stubTemplateParams = {2, 3};
|
||||||
return Container{id, info, 48};
|
return Container{id, info, 48, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
Container getList(NodeId id) {
|
Container getList(NodeId id) {
|
||||||
static ContainerInfo info{"std::list", LIST_TYPE, "list"};
|
static ContainerInfo info{"std::list", LIST_TYPE, "list"};
|
||||||
info.stubTemplateParams = {1};
|
info.stubTemplateParams = {1};
|
||||||
return Container{id, info, 24};
|
return Container{id, info, 24, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
Container getPair(NodeId id) {
|
Container getPair(NodeId id) {
|
||||||
static ContainerInfo info{"std::pair", PAIR_TYPE, "utility"};
|
static ContainerInfo info{"std::pair", PAIR_TYPE, "utility"};
|
||||||
return Container{id, info, 8}; // Nonsense size, shouldn't matter for tests
|
return Container{
|
||||||
|
id, info, 8, nullptr}; // Nonsense size, shouldn't matter for tests
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user