Add qualified types and improve parsing support

This commit is contained in:
Duncan Ogilvie 2023-02-28 21:33:56 +01:00
parent a87d84cc5b
commit 6d3f1f75c4
5 changed files with 258 additions and 48 deletions

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ My Amplifier XE Results */
actual.out actual.out
expected.out expected.out
.vs/ .vs/
cmake-build*/
build*/
.idea/

View File

@ -287,23 +287,23 @@ std::string preprocess(const std::string& input, std::string& error, const std::
t.skip_spaces(); t.skip_spaces();
auto directive = t.identifier(); auto directive = t.identifier();
line.print(); //line.print();
if (directive == "ifndef") if (directive == "ifndef")
{ {
t.skip_spaces(true); t.skip_spaces(true);
auto identifier = t.identifier(); auto identifier = t.identifier();
printf("#ifndef(%s)\n", identifier.c_str()); //printf("#ifndef(%s)\n", identifier.c_str());
stack.push_back({ i, "!defined(" + identifier + ")", state.count(identifier) == 0 }); stack.push_back({ i, "!defined(" + identifier + ")", state.count(identifier) == 0 });
printf("emitting: %d\n", emitting()); //printf("emitting: %d\n", emitting());
} }
else if (directive == "ifdef") else if (directive == "ifdef")
{ {
t.skip_spaces(true); t.skip_spaces(true);
auto identifier = t.identifier(); auto identifier = t.identifier();
printf("#ifdef(%s)\n", identifier.c_str()); //printf("#ifdef(%s)\n", identifier.c_str());
stack.push_back({ i, identifier, state.count(identifier) != 0 }); stack.push_back({ i, identifier, state.count(identifier) != 0 });
printf("emitting: %d\n", emitting()); //printf("emitting: %d\n", emitting());
} }
else if (directive == "else") else if (directive == "else")
{ {
@ -313,16 +313,16 @@ std::string preprocess(const std::string& input, std::string& error, const std::
{ {
stack.back().value = true; stack.back().value = true;
} }
printf("#else (%s)\n", stack.back().condition.c_str()); //printf("#else (%s)\n", stack.back().condition.c_str());
printf("emitting: %d\n", emitting()); //printf("emitting: %d\n", emitting());
} }
else if (directive == "endif") else if (directive == "endif")
{ {
if (stack.empty()) if (stack.empty())
throw std::runtime_error("no matching #if for #endif"); throw std::runtime_error("no matching #if for #endif");
printf("#endif (%s)\n", stack.back().condition.c_str()); //("#endif (%s)\n", stack.back().condition.c_str());
stack.pop_back(); stack.pop_back();
printf("emitting: %d\n", emitting()); //printf("emitting: %d\n", emitting());
} }
else if (directive == "define") else if (directive == "define")
{ {
@ -367,7 +367,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
pretty += parameters[i]; pretty += parameters[i];
} }
printf("#define %s('%s' = '%s')\n", identifier.c_str(), pretty.c_str(), token.c_str()); //printf("#define %s('%s' = '%s')\n", identifier.c_str(), pretty.c_str(), token.c_str());
} }
else else
@ -376,11 +376,11 @@ std::string preprocess(const std::string& input, std::string& error, const std::
auto token = t.remainder(); auto token = t.remainder();
if (token.empty()) if (token.empty())
{ {
printf("#define(%s)\n", identifier.c_str()); //printf("#define(%s)\n", identifier.c_str());
} }
else else
{ {
printf("#define('%s' = '%s')\n", identifier.c_str(), token.c_str()); //printf("#define('%s' = '%s')\n", identifier.c_str(), token.c_str());
} }
if (emitting()) if (emitting())
{ {
@ -396,13 +396,13 @@ std::string preprocess(const std::string& input, std::string& error, const std::
{ {
t.consume(); t.consume();
auto file = t.until('\"'); auto file = t.until('\"');
printf("#include \"%s\"\n", file.c_str()); //printf("#include \"%s\"\n", file.c_str());
} }
else if (type == '<') else if (type == '<')
{ {
t.consume(); t.consume();
auto file = t.until('>'); auto file = t.until('>');
printf("#include <%s>\n", file.c_str()); //printf("#include <%s>\n", file.c_str());
} }
else else
{ {
@ -411,7 +411,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
} }
else else
{ {
printf("directive: '%s'\n", directive.c_str()); //printf("directive: '%s'\n", directive.c_str());
throw std::runtime_error("unknown directive '" + directive + "'"); throw std::runtime_error("unknown directive '" + directive + "'");
} }
} }

View File

@ -1,6 +1,8 @@
#include "types.h" #include "types.h"
#include "helpers.h" #include "helpers.h"
#include <algorithm> #include <algorithm>
#include <unordered_set>
#include <ctime>
using namespace Types; using namespace Types;
@ -175,7 +177,7 @@ bool Types::TypeManager::AddEnumerator(const std::string& enumType, const std::s
return true; return true;
} }
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly) bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly, const QualifiedType& retqtype)
{ {
auto found = functions.find(name); auto found = functions.find(name);
if(found != functions.end() || name.empty() || owner.empty()) if(found != functions.end() || name.empty() || owner.empty())
@ -189,11 +191,13 @@ bool TypeManager::AddFunction(const std::string & owner, const std::string & nam
f.rettype = rettype; f.rettype = rettype;
f.callconv = callconv; f.callconv = callconv;
f.noreturn = noreturn; f.noreturn = noreturn;
f.typeonly = typeonly;
f.retqtype = retqtype;
functions.emplace(f.name, f); functions.emplace(f.name, f);
return true; return true;
} }
bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name) bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name, const QualifiedType& qtype)
{ {
auto found = functions.find(function); auto found = functions.find(function);
if (found == functions.end() || name.empty()) if (found == functions.end() || name.empty())
@ -204,13 +208,14 @@ bool TypeManager::AddArg(const std::string & function, const std::string & type,
Member arg; Member arg;
arg.name = name; arg.name = name;
arg.type = type; arg.type = type;
arg.qtype = qtype;
found->second.args.push_back(arg); found->second.args.push_back(arg);
return true; return true;
} }
bool TypeManager::AppendArg(const std::string & type, const std::string & name) bool TypeManager::AppendArg(const std::string & type, const std::string & name, const QualifiedType& qtype)
{ {
return AddArg(lastfunction, type, name); return AddArg(lastfunction, type, name, qtype);
} }
int TypeManager::Sizeof(const std::string & type) const int TypeManager::Sizeof(const std::string & type) const
@ -467,6 +472,153 @@ bool TypeManager::visitMember(const Member & root, Visitor & visitor) const
return false; return false;
} }
bool TypeManager::GenerateStubs() const
{
std::unordered_map<std::string, const Function*> fnptrs;
std::unordered_set<std::string> declared;
auto formatFunctionPointer = [this](const std::string& name, const Function& fn)
{
std::string r = fn.retqtype.pretty();
r += " (*";
r += name;
r += ")(";
for(size_t i = 0; i < fn.args.size(); i++)
{
const auto& arg = fn.args[i];
if(functions.count(arg.type) != 0)
__debugbreak();
r += arg.qtype.pretty();
if(i + 1 < fn.args.size())
{
r += ", ";
}
}
r += ")";
return r;
};
printf("// THIS WAS AUTOGENERATED YOU MORONS %ju\n", time(nullptr));
puts("#include \"hooks.h\"");
puts("");
std::vector<std::string> hooks;
for(const auto& itr : functions)
{
const auto &fn = itr.second;
// Skip non-declarations
if (fn.typeonly)
{
fnptrs.emplace(fn.name, &fn);
continue;
}
auto variadic = false;
for (const auto &arg: fn.args)
{
if (arg.type == "...")
{
variadic = true;
break;
}
}
if (variadic)
{
printf("// Skipping variadic function %s\n", fn.name.c_str());
continue;
}
hooks.push_back(fn.name);
}
puts("");
for(const auto& hook : hooks)
{
printf("static decltype(&%s) orig_%s;\n", hook.c_str(), hook.c_str());
}
for(const auto& hook : hooks)
{
const auto& fn = functions.at(hook);
puts("");
printf("static ");
printf("%s", fn.retqtype.pretty().c_str());
printf(" hook_%s(\n", fn.name.c_str());
std::vector<std::string> argtypes;
for(size_t i = 0; i < fn.args.size(); i++)
{
const auto& arg = fn.args[i];
printf(" ");
auto argPtr = functions.find(arg.type);
if(argPtr != functions.end())
{
argtypes.push_back(formatFunctionPointer(arg.name, argPtr->second));
printf("%s", argtypes.back().c_str());
if(arg.qtype.isConst)
__debugbreak();
}
else
{
argtypes.push_back(arg.type);
printf("%s", arg.qtype.pretty().c_str());
printf(" ");
printf("%s", arg.name.c_str());
}
if(i + 1 < fn.args.size())
{
printf(",");
}
puts("");
}
puts(")");
puts("{");
printf(" LOG_CALL(\"%s\");\n", fn.name.c_str());
for(size_t i = 0; i < fn.args.size(); i++)
{
printf(" LOG_ARGUMENT(\"%s\", %s);\n", argtypes[i].c_str(), fn.args[i].name.c_str());
}
if(fn.rettype == "void")
{
printf(" orig_%s(", fn.name.c_str());
}
else
{
printf(" auto _hook_result = orig_%s(", fn.name.c_str());
}
for(size_t i = 0; i < fn.args.size(); i++)
{
const auto& arg = fn.args[i];
printf("%s", arg.name.c_str());
if(i + 1 < fn.args.size())
{
printf(", ");
}
}
puts(");");
if(fn.rettype == "void")
{
puts(" LOG_RETURN_VOID();");
}
else
{
puts(" LOG_RETURN(_hook_result);");
puts(" return _hook_result;");
}
puts("}");
}
puts("");
puts("void InstallHooks()");
puts("{");
for(const auto& hook : hooks)
{
printf(" HOOK(%s);\n", hook.c_str(), hook.c_str());
}
puts("}");
return false;
}
bool AddType(const std::string & owner, const std::string & type, const std::string & name) bool AddType(const std::string & owner, const std::string & type, const std::string & name)
{ {
EXCLUSIVE_ACQUIRE(LockTypeManager); EXCLUSIVE_ACQUIRE(LockTypeManager);
@ -503,16 +655,16 @@ bool AddFunction(const std::string & owner, const std::string & name, const std:
return typeManager.AddFunction(owner, name, rettype, callconv, noreturn); return typeManager.AddFunction(owner, name, rettype, callconv, noreturn);
} }
bool AddArg(const std::string & function, const std::string & type, const std::string & name) bool AddArg(const std::string & function, const std::string & type, const std::string & name, const QualifiedType& qtype)
{ {
EXCLUSIVE_ACQUIRE(LockTypeManager); EXCLUSIVE_ACQUIRE(LockTypeManager);
return typeManager.AddArg(function, type, name); return typeManager.AddArg(function, type, name, qtype);
} }
bool AppendArg(const std::string & type, const std::string & name) bool AppendArg(const std::string & type, const std::string & name, const QualifiedType& qtype)
{ {
EXCLUSIVE_ACQUIRE(LockTypeManager); EXCLUSIVE_ACQUIRE(LockTypeManager);
return typeManager.AppendArg(type, name); return typeManager.AppendArg(type, name, qtype);
} }
int SizeofType(const std::string & type) int SizeofType(const std::string & type)
@ -709,7 +861,7 @@ void LoadModel(const std::string & owner, Model & model)
//Add base function types to avoid errors later //Add base function types to avoid errors later
for(auto & function : model.functions) for(auto & function : model.functions)
{ {
auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn, function.typeonly); auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn, function.typeonly, function.retqtype);
if(!success) if(!success)
{ {
//TODO properly handle errors //TODO properly handle errors
@ -743,8 +895,8 @@ void LoadModel(const std::string & owner, Model & model)
{ {
auto& arg = function.args[i]; auto& arg = function.args[i];
if (arg.name.empty()) if (arg.name.empty())
arg.name = "__unnamed" + std::to_string(i); arg.name = "__unnamed_arg_" + std::to_string(i);
auto success = typeManager.AddArg(function.name, arg.type, arg.name); auto success = typeManager.AddArg(function.name, arg.type, arg.name, arg.qtype);
if(!success) if(!success)
{ {
//TODO properly handle errors //TODO properly handle errors
@ -794,3 +946,8 @@ std::string StructUnionPtrType(const std::string & pointto)
{ {
return typeManager.StructUnionPtrType(pointto); return typeManager.StructUnionPtrType(pointto);
} }
bool GenerateStubs()
{
return typeManager.GenerateStubs();
}

View File

@ -36,12 +36,48 @@ namespace Types
int size = 0; //Size in bytes. int size = 0; //Size in bytes.
}; };
struct QualifiedType
{
std::string name; // base name of the type
bool isConst = false; // whether the base type is const
struct Ptr
{
bool isConst = false;
};
std::vector<Ptr> pointers; // arbitrary amount of pointers
std::string pretty() const
{
std::string r;
if(isConst)
r += "const ";
r += name;
for(const auto& ptr : pointers)
{
r += '*';
if(ptr.isConst)
r += " const";
}
return r;
}
std::string noconst() const
{
auto r = name;
for(const auto& ptr : pointers)
r += '*';
return r;
}
};
struct Member struct Member
{ {
std::string name; //Member identifier std::string name; //Member identifier
std::string type; //Type.name std::string type; //Type.name
bool isConst = false; //Whether the member is marked as const QualifiedType qtype; // Qualified Type
int arrsize = 0; //Number of elements if Member is an array int arrsize = 0; //Number of elements if Member is an array (unused for function arguments)
int offset = -1; //Member offset (only stored for reference) int offset = -1; //Member offset (only stored for reference)
}; };
@ -82,6 +118,7 @@ namespace Types
std::string owner; //Function owner std::string owner; //Function owner
std::string name; //Function identifier std::string name; //Function identifier
std::string rettype; //Function return type std::string rettype; //Function return type
QualifiedType retqtype; // Function return qualified type
CallingConvention callconv = DefaultDecl; //Function calling convention CallingConvention callconv = DefaultDecl; //Function calling convention
bool noreturn = false; //Function does not return (ExitProcess, _exit) bool noreturn = false; //Function does not return (ExitProcess, _exit)
bool typeonly = false; //Function is only used as a type (the name is based on where it's used) bool typeonly = false; //Function is only used as a type (the name is based on where it's used)
@ -116,16 +153,17 @@ namespace Types
bool AddUnion(const std::string & owner, const std::string & name); bool AddUnion(const std::string & owner, const std::string & name);
bool AddMember(const std::string & parent, const std::string & type, const std::string & name, int arrsize = 0, int offset = -1); bool AddMember(const std::string & parent, const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
bool AppendMember(const std::string & type, const std::string & name, int arrsize = 0, int offset = -1); bool AppendMember(const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false, bool typeonly = false); bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false, bool typeonly = false, const QualifiedType& retqtype = {});
bool AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value); bool AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value);
bool AddArg(const std::string & function, const std::string & type, const std::string & name); bool AddArg(const std::string & function, const std::string & type, const std::string & name, const QualifiedType& qtype);
bool AppendArg(const std::string & type, const std::string & name); bool AppendArg(const std::string & type, const std::string & name, const QualifiedType& qtype);
int Sizeof(const std::string & type) const; int Sizeof(const std::string & type) const;
bool Visit(const std::string & type, const std::string & name, Visitor & visitor) const; bool Visit(const std::string & type, const std::string & name, Visitor & visitor) const;
void Clear(const std::string & owner = ""); void Clear(const std::string & owner = "");
bool RemoveType(const std::string & type); bool RemoveType(const std::string & type);
void Enumerate(std::vector<Summary> & typeList) const; void Enumerate(std::vector<Summary> & typeList) const;
std::string StructUnionPtrType(const std::string & pointto) const; std::string StructUnionPtrType(const std::string & pointto) const;
bool GenerateStubs() const;
private: private:
std::unordered_map<Primitive, int> primitivesizes; std::unordered_map<Primitive, int> primitivesizes;
@ -162,8 +200,8 @@ bool AddUnion(const std::string & owner, const std::string & name);
bool AddMember(const std::string & parent, const std::string & type, const std::string & name, int arrsize = 0, int offset = -1); bool AddMember(const std::string & parent, const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
bool AppendMember(const std::string & type, const std::string & name, int arrsize = 0, int offset = -1); bool AppendMember(const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, Types::CallingConvention callconv = Types::Cdecl, bool noreturn = false); bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, Types::CallingConvention callconv = Types::Cdecl, bool noreturn = false);
bool AddArg(const std::string & function, const std::string & type, const std::string & name); bool AddArg(const std::string & function, const std::string & type, const std::string & name, const Types::QualifiedType& qtype);
bool AppendArg(const std::string & type, const std::string & name); bool AppendArg(const std::string & type, const std::string & name, const Types::QualifiedType& qtype);
int SizeofType(const std::string & type); int SizeofType(const std::string & type);
bool VisitType(const std::string & type, const std::string & name, Types::TypeManager::Visitor & visitor); bool VisitType(const std::string & type, const std::string & name, Types::TypeManager::Visitor & visitor);
void ClearTypes(const std::string & owner = ""); void ClearTypes(const std::string & owner = "");
@ -173,3 +211,4 @@ bool LoadTypesJson(const std::string & json, const std::string & owner);
bool LoadTypesFile(const std::string & path, const std::string & owner); bool LoadTypesFile(const std::string & path, const std::string & owner);
bool ParseTypes(const std::string & code, const std::string & owner, std::vector<std::string> & errors); bool ParseTypes(const std::string & code, const std::string & owner, std::vector<std::string> & errors);
std::string StructUnionPtrType(const std::string & pointto); std::string StructUnionPtrType(const std::string & pointto);
bool GenerateStubs();

View File

@ -62,10 +62,10 @@ struct Parser
index++; index++;
} }
bool parseVariable(const std::vector<Lexer::TokenState>& tlist, std::string& type, bool& isConst, std::string& name) bool parseVariable(const std::vector<Lexer::TokenState>& tlist, std::string& type, QualifiedType& qtype, std::string& name)
{ {
type.clear(); type.clear();
isConst = false; qtype = QualifiedType();
name.clear(); name.clear();
bool sawPointer = false; bool sawPointer = false;
@ -76,7 +76,10 @@ struct Parser
const auto& t = tlist[i]; const auto& t = tlist[i];
if (t.Is(Lexer::tok_const)) if (t.Is(Lexer::tok_const))
{ {
isConst = true; if(i == 0 || qtype.pointers.empty())
qtype.isConst = true;
else
qtype.pointers.back().isConst = true;
continue; continue;
} }
@ -130,6 +133,11 @@ struct Parser
return false; return false;
} }
if(!sawPointer)
qtype.name = type;
qtype.pointers.emplace_back();
// Apply the pointer to the type on the left // Apply the pointer to the type on the left
type += '*'; type += '*';
sawPointer = true; sawPointer = true;
@ -142,6 +150,8 @@ struct Parser
} }
if (type.empty()) if (type.empty())
__debugbreak(); __debugbreak();
if(!sawPointer)
qtype.name = type;
return true; return true;
} }
@ -156,8 +166,7 @@ struct Parser
// TODO: calling conventions // TODO: calling conventions
std::string retname; std::string retname;
bool retconst = false; if (!parseVariable(rettypes, fn.rettype, fn.retqtype, retname))
if (!parseVariable(rettypes, fn.rettype, retconst, retname))
return false; return false;
if (ptr) if (ptr)
@ -212,7 +221,7 @@ struct Parser
auto finalizeArgument = [&]() auto finalizeArgument = [&]()
{ {
Member am; Member am;
if (!parseVariable(tlist, am.type, am.isConst, am.name)) if (!parseVariable(tlist, am.type, am.qtype, am.name))
return false; return false;
fn.args.push_back(am); fn.args.push_back(am);
tlist.clear(); tlist.clear();
@ -306,9 +315,6 @@ struct Parser
typeToken.Token = Lexer::tok_identifier; typeToken.Token = Lexer::tok_identifier;
typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr"; typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr";
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
auto nameToken = startToken; auto nameToken = startToken;
nameToken.Token = Lexer::tok_identifier; nameToken.Token = Lexer::tok_identifier;
nameToken.IdentifierStr = subfn.name; nameToken.IdentifierStr = subfn.name;
@ -317,6 +323,10 @@ struct Parser
tlist.clear(); tlist.clear();
tlist.push_back(typeToken); tlist.push_back(typeToken);
tlist.push_back(nameToken); tlist.push_back(nameToken);
// Add the function to the model
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
} }
else else
{ {
@ -362,7 +372,7 @@ struct Parser
return false; return false;
} }
if (!parseVariable(tlist, m.type, m.isConst, m.name)) if (!parseVariable(tlist, m.type, m.qtype, m.name))
return false; return false;
if (m.type == "void") if (m.type == "void")
@ -472,9 +482,6 @@ struct Parser
typeToken.Token = Lexer::tok_identifier; typeToken.Token = Lexer::tok_identifier;
typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr"; typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr";
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
auto nameToken = startToken; auto nameToken = startToken;
nameToken.Token = Lexer::tok_identifier; nameToken.Token = Lexer::tok_identifier;
nameToken.IdentifierStr = subfn.name; nameToken.IdentifierStr = subfn.name;
@ -484,6 +491,10 @@ struct Parser
tlist.push_back(typeToken); tlist.push_back(typeToken);
tlist.push_back(nameToken); tlist.push_back(nameToken);
// Add the function to the model
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
return true; return true;
} }
else if (t.Is(Lexer::tok_comma)) else if (t.Is(Lexer::tok_comma))
@ -814,7 +825,7 @@ struct Parser
} }
Member tm; Member tm;
if (!parseVariable(tlist, tm.type, tm.isConst, tm.name)) if (!parseVariable(tlist, tm.type, tm.qtype, tm.name))
return false; return false;
model.types.push_back(tm); model.types.push_back(tm);
} }