From a87d84cc5bfd04ef02d8c2b04e902885e844ab07 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Fri, 10 Feb 2023 14:00:44 +0100 Subject: [PATCH] (Somewhat) correctly handle function declarations and function pointer types --- btparser/types.cpp | 120 ++++++++++++++++++++++++++++++++------- btparser/types.h | 21 +++++-- btparser/typesparser.cpp | 78 ++++++++++++++++++------- 3 files changed, 173 insertions(+), 46 deletions(-) diff --git a/btparser/types.cpp b/btparser/types.cpp index 487007b..7c8e29e 100644 --- a/btparser/types.cpp +++ b/btparser/types.cpp @@ -15,8 +15,11 @@ TypeManager::TypeManager() { primitivesizes[p] = size; auto splits = StringUtils::Split(n, ','); - for(const auto & split : splits) + for (const auto& split : splits) + { + primitivenames.emplace(p, split); addType("", p, split); + } }; p("int8_t,int8,char,byte,bool,signed char", Int8, sizeof(char)); p("uint8_t,uint8,uchar,unsigned char,ubyte", Uint8, sizeof(unsigned char)); @@ -27,14 +30,20 @@ TypeManager::TypeManager() p("int64_t,int64,long long", Int64, sizeof(long long)); p("uint64_t,uint64,unsigned long long", Uint64, sizeof(unsigned long long)); p("dsint", Dsint, sizeof(void*)); - p("duint,size_t", Duint, sizeof(void*)); + p("size_t,duint", Duint, sizeof(void*)); p("float", Float, sizeof(float)); p("double", Double, sizeof(double)); - p("ptr,void*", Pointer, sizeof(void*)); + p("void*,ptr", Pointer, sizeof(void*)); p("char*,const char*", PtrString, sizeof(char*)); p("wchar_t*,const wchar_t*", PtrWString, sizeof(wchar_t*)); } +std::string Types::TypeManager::PrimitiveName(Primitive primitive) +{ + auto found = primitivenames.find(primitive); + return found == primitivenames.end() ? "" : found->second; +} + bool TypeManager::AddType(const std::string & owner, const std::string & type, const std::string & name) { if(owner.empty()) @@ -46,6 +55,32 @@ bool TypeManager::AddType(const std::string & owner, const std::string & type, c return addType(owner, found->second.primitive, name); } +bool Types::TypeManager::AddEnum(const std::string& owner, const std::string& name, const std::string& type) +{ + Enum e; + e.name = name; + e.owner = owner; + auto found = types.find(type); + if (found == types.end()) + return false; + if (!found->second.pointto.empty()) + return false; + switch (found->second.primitive) + { + case Void: + case Float: + case Double: + case Pointer: + case PtrString: + case PtrWString: + return false; + default: + break; + } + e.type = found->second.primitive; + return addEnum(e); +} + bool TypeManager::AddStruct(const std::string & owner, const std::string & name) { StructUnion s; @@ -122,7 +157,25 @@ bool TypeManager::AppendMember(const std::string & type, const std::string & nam return AddMember(laststruct, type, name, arrsize, offset); } -bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn) +bool Types::TypeManager::AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value) +{ + auto found = enums.find(enumType); + if (found == enums.end()) + return false; + auto& e = found->second; + for (const auto& v : e.values) + { + if (v.name == name || v.value == value) + return false; + } + EnumValue v; + v.name = name; + v.value = value; + e.values.push_back(v); + return true; +} + +bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly) { auto found = functions.find(name); if(found != functions.end() || name.empty() || owner.empty()) @@ -136,16 +189,16 @@ bool TypeManager::AddFunction(const std::string & owner, const std::string & nam f.rettype = rettype; f.callconv = callconv; f.noreturn = noreturn; - functions.insert({f.name, f}); + functions.emplace(f.name, f); return true; } bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name) { - if(!isDefined(type) && !validPtr(type)) - return false; auto found = functions.find(function); - if(found == functions.end() || function.empty() || name.empty() || !isDefined(type)) + if (found == functions.end() || name.empty()) + return false; + if(type != "..." && !isDefined(type) && !validPtr(type)) return false; lastfunction = function; Member arg; @@ -255,7 +308,7 @@ static void enumType(const std::unordered_map & map, std::vector & typeList) const +void TypeManager::Enumerate(std::vector & typeList) const { typeList.clear(); enumType(types, typeList); @@ -310,7 +363,7 @@ static bool mapContains(const std::unordered_map & map, const K & k) bool TypeManager::isDefined(const std::string & id) const { - return mapContains(types, id) || mapContains(structs, id); + return mapContains(types, id) || mapContains(structs, id) || mapContains(enums, id) || mapContains(functions, id); } bool TypeManager::validPtr(const std::string & id) @@ -337,16 +390,22 @@ bool TypeManager::addStructUnion(const StructUnion & s) laststruct = s.name; if(s.owner.empty() || s.name.empty() || isDefined(s.name)) return false; - structs.insert({s.name, s}); - return true; + return structs.emplace(s.name, s).second; +} + +bool Types::TypeManager::addEnum(const Enum& e) +{ + lastenum = e.name; + if (e.owner.empty() || e.name.empty() || isDefined(e.name)) + return false; + return enums.emplace(e.name, e).second; } bool TypeManager::addType(const Type & t) { if(t.name.empty() || isDefined(t.name)) return false; - types.insert({t.name, t}); - return true; + return types.emplace(t.name, t).second; } bool TypeManager::addType(const std::string & owner, Primitive primitive, const std::string & name, const std::string & pointto) @@ -483,7 +542,7 @@ bool RemoveType(const std::string & type) void EnumTypes(std::vector & typeList) { SHARED_ACQUIRE(LockTypeManager); - return typeManager.Enum(typeList); + return typeManager.Enumerate(typeList); } #if 0 @@ -602,7 +661,6 @@ static void loadFunctions(const JSON froot, std::vector & functions) void LoadModel(const std::string & owner, Model & model) { //Add all base struct/union types first to avoid errors later - // TODO: handle forward declarations for(auto & su : model.structUnions) { auto success = su.isunion ? typeManager.AddUnion(owner, su.name) : typeManager.AddStruct(owner, su.name); @@ -626,15 +684,32 @@ void LoadModel(const std::string & owner, Model & model) } //Add enums - for (auto& enumm : model.enums) + for (auto& kv : model.enums) { - __debugbreak(); + auto& e = kv.first; + const auto& etype = kv.second; + auto success = typeManager.AddEnum(owner, e.name, etype); + if (!success) + { + dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add enum %s;\n"), e.name.c_str()); + e.name.clear(); // signal error + } + else + { + for (const auto& v : e.values) + { + if (!typeManager.AddEnumerator(e.name, v.name, v.value)) + { + dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add enum member %s.%s = %llu;\n"), e.name.c_str(), v.name.c_str(), v.value); + } + } + } } //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); + auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn, function.typeonly); if(!success) { //TODO properly handle errors @@ -664,13 +739,16 @@ void LoadModel(const std::string & owner, Model & model) { if(function.name.empty()) //skip error-signalled functions continue; - for(auto & arg : function.args) + for (size_t i = 0; i < function.args.size(); i++) { + 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); if(!success) { //TODO properly handle errors - dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add argument %s %s.%s;\n"), arg.type.c_str(), function.name.c_str(), arg.name.c_str()); + dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add argument %s[%zu]: %s %s;\n"), function.name.c_str(), i, arg.type.c_str(), arg.name.c_str()); } } } diff --git a/btparser/types.h b/btparser/types.h index 29d3137..ea9b16b 100644 --- a/btparser/types.h +++ b/btparser/types.h @@ -23,14 +23,15 @@ namespace Types Double, Pointer, PtrString, //char* (null-terminated) - PtrWString //wchar_t* (null-terminated) + PtrWString, //wchar_t* (null-terminated) + Varargs, }; struct Type { std::string owner; //Type owner std::string name; //Type identifier. - std::string pointto; //Type identifier of *Type + std::string pointto; //Type identifier of *Type (empty when the type is a primitive type) Primitive primitive = Void; //Primitive type. int size = 0; //Size in bytes. }; @@ -64,7 +65,7 @@ namespace Types std::string owner; // Enum owner std::string name; // Enum name std::vector values; // Enum values - Type type; // Enum value type + Primitive type; // Enum value type }; enum CallingConvention @@ -83,6 +84,7 @@ namespace Types std::string rettype; //Function return 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) std::vector args; //Function arguments }; @@ -107,32 +109,39 @@ namespace Types }; explicit TypeManager(); + std::string PrimitiveName(Primitive primitive); bool AddType(const std::string & owner, const std::string & type, const std::string & name); + bool AddEnum(const std::string& owner, const std::string& name, const std::string & type); bool AddStruct(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 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 AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false, bool typeonly = false); + 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); 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 Enum(std::vector & typeList) const; + void Enumerate(std::vector & typeList) const; std::string StructUnionPtrType(const std::string & pointto) const; private: std::unordered_map primitivesizes; + std::unordered_map primitivenames; std::unordered_map types; + std::unordered_map enums; std::unordered_map structs; std::unordered_map functions; + std::string lastenum; std::string laststruct; std::string lastfunction; bool isDefined(const std::string & id) const; bool validPtr(const std::string & id); bool addStructUnion(const StructUnion & s); + bool addEnum(const Enum& e); bool addType(const std::string & owner, Primitive primitive, const std::string & name, const std::string & pointto = ""); bool addType(const Type & t); bool visitMember(const Member & root, Visitor & visitor) const; @@ -141,7 +150,7 @@ namespace Types struct Model { std::vector types; - std::vector enums; + std::vector> enums; std::vector structUnions; std::vector functions; }; diff --git a/btparser/typesparser.cpp b/btparser/typesparser.cpp index cc17746..136c10e 100644 --- a/btparser/typesparser.cpp +++ b/btparser/typesparser.cpp @@ -16,6 +16,7 @@ struct Parser std::vector tokens; size_t index = 0; Model model; + std::unordered_map structUnions; Parser(const std::string& code, const std::string& owner, std::vector& errors) : owner(owner), errors(errors) @@ -296,20 +297,17 @@ struct Parser // Function pointer argument to a function Function subfn; + subfn.typeonly = true; if(!parseFunction(tlist, subfn, true)) return false; - - // TODO: store this somewhere - - // Create fake tokens - // TODO: handle this properly somehow auto typeToken = tlist.back(); typeToken.Token = Lexer::tok_identifier; typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr"; - - printf("TODO function pointer: %s %s\n", typeToken.IdentifierStr.c_str(), subfn.name.c_str()); + + subfn.name = typeToken.IdentifierStr; + model.functions.push_back(subfn); auto nameToken = startToken; nameToken.Token = Lexer::tok_identifier; @@ -455,8 +453,9 @@ struct Parser index++; // Function pointer type - Function fn; - if (!parseFunction(tlist, fn, true)) + Function subfn; + subfn.typeonly = true; + if (!parseFunction(tlist, subfn, true)) { return false; } @@ -468,9 +467,22 @@ struct Parser } eatSemic(); - // TODO: put the function somewhere + // Create fake tokens + auto typeToken = tlist.back(); + typeToken.Token = Lexer::tok_identifier; + typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr"; - printf("TODO function pointer: %s\n", fn.name.c_str()); + subfn.name = typeToken.IdentifierStr; + model.functions.push_back(subfn); + + auto nameToken = startToken; + nameToken.Token = Lexer::tok_identifier; + nameToken.IdentifierStr = subfn.name; + + // Replace the return type with the fake tokens + tlist.clear(); + tlist.push_back(typeToken); + tlist.push_back(nameToken); return true; } @@ -515,6 +527,7 @@ struct Parser bool parseStructUnion() { + auto startToken = curToken(); if (isToken(Lexer::tok_struct) || isToken(Lexer::tok_union)) { StructUnion su; @@ -547,7 +560,25 @@ struct Parser } index++; - model.structUnions.push_back(su); + // Handle forward declarations + auto found = structUnions.find(su.name); + if (found != structUnions.end()) + { + auto& oldSu = model.structUnions[found->second]; + if (oldSu.size != -1) + { + errLine(startToken, "cannot redeclare type"); + return false; + } + // Replace the forward declared type with the full type + oldSu = su; + } + else + { + structUnions.emplace(su.name, model.structUnions.size()); + model.structUnions.push_back(su); + } + if (!isToken(Lexer::tok_semic)) { errLine(curToken(), "expected semicolon!"); @@ -559,7 +590,13 @@ struct Parser else if (isToken(Lexer::tok_semic)) { // Forward declaration - model.structUnions.push_back(su); + su.size = -1; + auto found = structUnions.find(su.name); + if (found == structUnions.end()) + { + structUnions.emplace(su.name, model.structUnions.size()); + model.structUnions.push_back(su); + } eatSemic(); return true; } @@ -577,12 +614,16 @@ struct Parser if (isToken(Lexer::tok_enum)) { Enum e; + std::string etype; index++; if (isTokenList({ Lexer::tok_identifier, Lexer::tok_bropen })) { e.name = lexer.TokString(curToken()); index += 2; + // TODO: support custom enum types (: type) + etype = "int"; + while (!isToken(Lexer::tok_brclose)) { if (isToken(Lexer::tok_eof)) @@ -653,7 +694,7 @@ struct Parser } index++; //eat tok_brclose - model.enums.push_back(e); + model.enums.emplace_back(e, etype); if (!isToken(Lexer::tok_semic)) { errLine(curToken(), "expected semicolon!"); @@ -740,6 +781,7 @@ struct Parser index++; Function fn; + fn.typeonly = true; if (!parseFunction(tlist, fn, true)) { return false; @@ -752,9 +794,9 @@ struct Parser } eatSemic(); - // TODO: put the function somewhere + model.functions.push_back(fn); - printf("TODO function pointer: %s\n", fn.name.c_str()); + // TODO: handle pointer stuff correctly? return true; } @@ -849,9 +891,7 @@ struct Parser } eatSemic(); - // TODO: put the function somewhere - - printf("TODO function: %s\n", fn.name.c_str()); + model.functions.push_back(fn); return true; }