mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-11-13 22:06:55 +00:00
b4b3e86c47
Containers store references to ContainerInfos, so the ContainerInfos must live beyond the stack they were created on. Use static variables for simplicity.
424 lines
13 KiB
C++
424 lines
13 KiB
C++
#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 TypeGraphParserError{"Invalid Primitive::Kind: " +
|
|
std::string{kindStr}};
|
|
}
|
|
|
|
ContainerInfo& getContainerInfo(std::string_view name) {
|
|
if (name == "std::vector") {
|
|
static ContainerInfo info{"std::vector", SEQ_TYPE, "vector"};
|
|
info.stubTemplateParams = {1};
|
|
return info;
|
|
}
|
|
if (name == "std::map") {
|
|
static ContainerInfo info{"std::map", STD_MAP_TYPE, "utility"};
|
|
info.stubTemplateParams = {2, 3};
|
|
return info;
|
|
}
|
|
if (name == "std::pair") {
|
|
static ContainerInfo info{"std::pair", SEQ_TYPE, "utility"};
|
|
return info;
|
|
}
|
|
if (name == "std::allocator") {
|
|
static ContainerInfo info{"std::allocator", DUMMY_TYPE, "memory"};
|
|
return info;
|
|
}
|
|
throw TypeGraphParserError{"Unsupported container: " + std::string{name}};
|
|
}
|
|
|
|
Qualifier getQualifier(std::string_view line) {
|
|
if (line == "const") {
|
|
return Qualifier::Const;
|
|
}
|
|
throw TypeGraphParserError{"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 TypeGraphParserError{"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 TypeGraphParserError{
|
|
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 TypeGraphParserError{"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 TypeGraphParserError{"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 TypeGraphParserError{"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 TypeGraphParserError{"Invalid type for child"};
|
|
|
|
c.children.push_back(*childClass);
|
|
}
|
|
// No more children for us - put back the line we just read
|
|
input = origInput;
|
|
}
|