From 6d3f1f75c4e2fae986f68c4d312b65f42932f101 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Tue, 28 Feb 2023 21:33:56 +0100 Subject: [PATCH] Add qualified types and improve parsing support --- .gitignore | 3 + btparser/preprocessor.cpp | 30 +++---- btparser/types.cpp | 179 +++++++++++++++++++++++++++++++++++--- btparser/types.h | 55 ++++++++++-- btparser/typesparser.cpp | 39 ++++++--- 5 files changed, 258 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index 7275071..dfd8bfa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ My Amplifier XE Results */ actual.out expected.out .vs/ +cmake-build*/ +build*/ +.idea/ diff --git a/btparser/preprocessor.cpp b/btparser/preprocessor.cpp index bbfc99d..4565f3e 100644 --- a/btparser/preprocessor.cpp +++ b/btparser/preprocessor.cpp @@ -287,23 +287,23 @@ std::string preprocess(const std::string& input, std::string& error, const std:: t.skip_spaces(); auto directive = t.identifier(); - line.print(); + //line.print(); if (directive == "ifndef") { t.skip_spaces(true); 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 }); - printf("emitting: %d\n", emitting()); + //printf("emitting: %d\n", emitting()); } else if (directive == "ifdef") { t.skip_spaces(true); 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 }); - printf("emitting: %d\n", emitting()); + //printf("emitting: %d\n", emitting()); } else if (directive == "else") { @@ -313,16 +313,16 @@ std::string preprocess(const std::string& input, std::string& error, const std:: { stack.back().value = true; } - printf("#else (%s)\n", stack.back().condition.c_str()); - printf("emitting: %d\n", emitting()); + //printf("#else (%s)\n", stack.back().condition.c_str()); + //printf("emitting: %d\n", emitting()); } else if (directive == "endif") { if (stack.empty()) 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(); - printf("emitting: %d\n", emitting()); + //printf("emitting: %d\n", emitting()); } else if (directive == "define") { @@ -367,7 +367,7 @@ std::string preprocess(const std::string& input, std::string& error, const std:: 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 @@ -376,11 +376,11 @@ std::string preprocess(const std::string& input, std::string& error, const std:: auto token = t.remainder(); if (token.empty()) { - printf("#define(%s)\n", identifier.c_str()); + //printf("#define(%s)\n", identifier.c_str()); } 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()) { @@ -396,13 +396,13 @@ std::string preprocess(const std::string& input, std::string& error, const std:: { t.consume(); auto file = t.until('\"'); - printf("#include \"%s\"\n", file.c_str()); + //printf("#include \"%s\"\n", file.c_str()); } else if (type == '<') { t.consume(); auto file = t.until('>'); - printf("#include <%s>\n", file.c_str()); + //printf("#include <%s>\n", file.c_str()); } else { @@ -411,7 +411,7 @@ std::string preprocess(const std::string& input, std::string& error, const std:: } else { - printf("directive: '%s'\n", directive.c_str()); + //printf("directive: '%s'\n", directive.c_str()); throw std::runtime_error("unknown directive '" + directive + "'"); } } diff --git a/btparser/types.cpp b/btparser/types.cpp index 7c8e29e..bc116a1 100644 --- a/btparser/types.cpp +++ b/btparser/types.cpp @@ -1,6 +1,8 @@ #include "types.h" #include "helpers.h" #include +#include +#include using namespace Types; @@ -175,7 +177,7 @@ bool Types::TypeManager::AddEnumerator(const std::string& enumType, const std::s 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); 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.callconv = callconv; f.noreturn = noreturn; + f.typeonly = typeonly; + f.retqtype = retqtype; functions.emplace(f.name, f); 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); if (found == functions.end() || name.empty()) @@ -204,13 +208,14 @@ bool TypeManager::AddArg(const std::string & function, const std::string & type, Member arg; arg.name = name; arg.type = type; + arg.qtype = qtype; found->second.args.push_back(arg); 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 @@ -467,6 +472,153 @@ bool TypeManager::visitMember(const Member & root, Visitor & visitor) const return false; } +bool TypeManager::GenerateStubs() const +{ + std::unordered_map fnptrs; + std::unordered_set 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 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 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) { 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); } -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); - 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); - return typeManager.AppendArg(type, name); + return typeManager.AppendArg(type, name, qtype); } 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 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) { //TODO properly handle errors @@ -743,8 +895,8 @@ void LoadModel(const std::string & owner, Model & model) { auto& arg = function.args[i]; if (arg.name.empty()) - arg.name = "__unnamed" + std::to_string(i); - auto success = typeManager.AddArg(function.name, arg.type, arg.name); + arg.name = "__unnamed_arg_" + std::to_string(i); + auto success = typeManager.AddArg(function.name, arg.type, arg.name, arg.qtype); if(!success) { //TODO properly handle errors @@ -794,3 +946,8 @@ std::string StructUnionPtrType(const std::string & pointto) { return typeManager.StructUnionPtrType(pointto); } + +bool GenerateStubs() +{ + return typeManager.GenerateStubs(); +} diff --git a/btparser/types.h b/btparser/types.h index ea9b16b..ae375bf 100644 --- a/btparser/types.h +++ b/btparser/types.h @@ -36,12 +36,48 @@ namespace Types 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 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 { std::string name; //Member identifier std::string type; //Type.name - bool isConst = false; //Whether the member is marked as const - int arrsize = 0; //Number of elements if Member is an array + QualifiedType qtype; // Qualified Type + int arrsize = 0; //Number of elements if Member is an array (unused for function arguments) int offset = -1; //Member offset (only stored for reference) }; @@ -82,6 +118,7 @@ namespace Types std::string owner; //Function owner std::string name; //Function identifier std::string rettype; //Function return type + QualifiedType retqtype; // Function return qualified type CallingConvention callconv = DefaultDecl; //Function calling convention 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) @@ -116,16 +153,17 @@ namespace Types 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 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 AddArg(const std::string & function, const std::string & type, const std::string & name); - bool AppendArg(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, const QualifiedType& qtype); int Sizeof(const std::string & type) const; bool Visit(const std::string & type, const std::string & name, Visitor & visitor) const; void Clear(const std::string & owner = ""); bool RemoveType(const std::string & type); void Enumerate(std::vector & typeList) const; std::string StructUnionPtrType(const std::string & pointto) const; + bool GenerateStubs() const; private: std::unordered_map 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 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 AddArg(const std::string & function, const std::string & type, const std::string & name); -bool AppendArg(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, const Types::QualifiedType& qtype); int SizeofType(const std::string & type); bool VisitType(const std::string & type, const std::string & name, Types::TypeManager::Visitor & visitor); void ClearTypes(const std::string & owner = ""); @@ -172,4 +210,5 @@ void EnumTypes(std::vector & typeList); bool LoadTypesJson(const std::string & json, 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 & errors); -std::string StructUnionPtrType(const std::string & pointto); \ No newline at end of file +std::string StructUnionPtrType(const std::string & pointto); +bool GenerateStubs(); \ No newline at end of file diff --git a/btparser/typesparser.cpp b/btparser/typesparser.cpp index 136c10e..ee5733d 100644 --- a/btparser/typesparser.cpp +++ b/btparser/typesparser.cpp @@ -62,10 +62,10 @@ struct Parser index++; } - bool parseVariable(const std::vector& tlist, std::string& type, bool& isConst, std::string& name) + bool parseVariable(const std::vector& tlist, std::string& type, QualifiedType& qtype, std::string& name) { type.clear(); - isConst = false; + qtype = QualifiedType(); name.clear(); bool sawPointer = false; @@ -76,7 +76,10 @@ struct Parser const auto& t = tlist[i]; if (t.Is(Lexer::tok_const)) { - isConst = true; + if(i == 0 || qtype.pointers.empty()) + qtype.isConst = true; + else + qtype.pointers.back().isConst = true; continue; } @@ -130,6 +133,11 @@ struct Parser return false; } + if(!sawPointer) + qtype.name = type; + + qtype.pointers.emplace_back(); + // Apply the pointer to the type on the left type += '*'; sawPointer = true; @@ -142,6 +150,8 @@ struct Parser } if (type.empty()) __debugbreak(); + if(!sawPointer) + qtype.name = type; return true; } @@ -156,8 +166,7 @@ struct Parser // TODO: calling conventions std::string retname; - bool retconst = false; - if (!parseVariable(rettypes, fn.rettype, retconst, retname)) + if (!parseVariable(rettypes, fn.rettype, fn.retqtype, retname)) return false; if (ptr) @@ -212,7 +221,7 @@ struct Parser auto finalizeArgument = [&]() { Member am; - if (!parseVariable(tlist, am.type, am.isConst, am.name)) + if (!parseVariable(tlist, am.type, am.qtype, am.name)) return false; fn.args.push_back(am); tlist.clear(); @@ -305,9 +314,6 @@ struct Parser auto typeToken = tlist.back(); typeToken.Token = Lexer::tok_identifier; typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr"; - - subfn.name = typeToken.IdentifierStr; - model.functions.push_back(subfn); auto nameToken = startToken; nameToken.Token = Lexer::tok_identifier; @@ -317,6 +323,10 @@ struct Parser tlist.clear(); tlist.push_back(typeToken); tlist.push_back(nameToken); + + // Add the function to the model + subfn.name = typeToken.IdentifierStr; + model.functions.push_back(subfn); } else { @@ -362,7 +372,7 @@ struct Parser return false; } - if (!parseVariable(tlist, m.type, m.isConst, m.name)) + if (!parseVariable(tlist, m.type, m.qtype, m.name)) return false; if (m.type == "void") @@ -472,9 +482,6 @@ struct Parser typeToken.Token = Lexer::tok_identifier; typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr"; - subfn.name = typeToken.IdentifierStr; - model.functions.push_back(subfn); - auto nameToken = startToken; nameToken.Token = Lexer::tok_identifier; nameToken.IdentifierStr = subfn.name; @@ -484,6 +491,10 @@ struct Parser tlist.push_back(typeToken); tlist.push_back(nameToken); + // Add the function to the model + subfn.name = typeToken.IdentifierStr; + model.functions.push_back(subfn); + return true; } else if (t.Is(Lexer::tok_comma)) @@ -814,7 +825,7 @@ struct Parser } Member tm; - if (!parseVariable(tlist, tm.type, tm.isConst, tm.name)) + if (!parseVariable(tlist, tm.type, tm.qtype, tm.name)) return false; model.types.push_back(tm); }