This series implements a couple of enhancements to symbol undecoration in msvcrt: - more C++ constructs are correctly supported (pointer to member, constructors/destructors from template class, non-type function pointer template parameter...) - some internal cleanups & simplification (most of them based on revamped helper to build up the undecorated C++ symbols)
-- v2: dlls/msvcrt: undecorate function signature as template argument dlls/msvcrt: correctly undecorate ptr to member data type dlls/msvcrt: simplify str_build not to allocate buffer when possible dlls/msvcrt: simplify some calls to str_build by not forcing separator dlls/msvcrt: support %S operator in str_build dlls/msvcrt: rename str_print into str_build dlls/msvcrt: fix white space output for undecorating typecast operator dlls/msvcrt: improve support for template in methods
From: Eric Pouech eric.pouech@gmail.com
adding function template support for methods that need specific post processing in demangling (constructor, destructor, cast operator)
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/tests/cpp.c | 3 +++ dlls/msvcrt/undname.c | 36 ++++++++++++++++-------------------- 2 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/dlls/msvcrt/tests/cpp.c b/dlls/msvcrt/tests/cpp.c index 58ecd1327a5..5beaea3bd1a 100644 --- a/dlls/msvcrt/tests/cpp.c +++ b/dlls/msvcrt/tests/cpp.c @@ -1295,6 +1295,9 @@ static void test_demangle(void) /* 132 */ {"??$meth@FD@DD@CC@@QAE_NK@Z", "public: bool __thiscall CC::DD::meth<short,char>(unsigned long)"}, /* 133 */ {"?func@@YAXPIFAH@Z", "void __cdecl func(int __unaligned * __restrict)"}, /* 135 */ {"?x@@3PAY02HA", "int (* x)[3]"}, +/* 136 */ {"??$?0AEAVzzz@BB4@AA@@AEAV012@$0A@@?$pair@Vzzz@BB4@AA@@V123@@std@@QEAA@AEAVzzz@BB4@AA@@0@Z", + "public: __cdecl std::pair<class AA::BB4::zzz,class AA::BB4::zzz>::pair<class AA::BB4::zzz,class AA::BB4::zzz><class AA::BB4::zzz & __ptr64,class AA::BB4::zzz & __ptr64,0>(class AA::BB4::zzz & __ptr64,class AA::BB4::zzz & __ptr64) __ptr64"}, +/* 137 */ {"??$?BH@?$foo@N@@QEAAHXZ", "public: __cdecl foo<double>::operator<int> int(void) __ptr64"}, }; int i, num_test = ARRAY_SIZE(test); char* name; diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index acfe96fe436..be736631626 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -1362,7 +1362,6 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) { BOOL ret = FALSE; unsigned do_after = 0; - static CHAR dashed_null[] = "--null--";
/* FIXME seems wrong as name, as it demangles a simple data type */ if (sym->flags & UNDNAME_NO_ARGUMENTS) @@ -1385,18 +1384,19 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) if (*sym->current == '?') { const char* function_name = NULL; + BOOL in_template = FALSE;
if (sym->current[1] == '$' && sym->current[2] == '?') { - do_after = 5; + in_template = TRUE; sym->current += 2; }
/* C++ operator code (one character, or two if the first is '_') */ switch (*++sym->current) { - case '0': do_after = 1; break; - case '1': do_after = 2; break; + case '0': function_name = ""; do_after = 1; break; + case '1': function_name = ""; do_after = 2; break; case '2': function_name = "operator new"; break; case '3': function_name = "operator delete"; break; case '4': function_name = "operator="; break; @@ -1531,26 +1531,22 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) return FALSE; } sym->current++; + if (in_template) + { + const char *args; + struct array array_pmt; + + str_array_init(&array_pmt); + args = get_args(sym, &array_pmt, FALSE, '<', '>'); + if (args) function_name = function_name ? str_printf(sym, "%s%s", function_name, args) : args; + sym->names.num = 0; + } switch (do_after) { - case 1: case 2: - if (!str_array_push(sym, dashed_null, -1, &sym->stack)) - return FALSE; - break; case 4: sym->result = (char*)function_name; ret = TRUE; goto done; - case 5: - { - char *args; - struct array array_pmt; - - str_array_init(&array_pmt); - args = get_args(sym, &array_pmt, FALSE, '<', '>'); - if (args != NULL) function_name = str_printf(sym, "%s%s", function_name, args); - sym->names.num = 0; - } /* fall through */ default: if (!str_array_push(sym, function_name, -1, &sym->stack)) @@ -1585,9 +1581,9 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) /* it's time to set the member name for ctor & dtor */ if (sym->stack.num <= 1) goto done; if (do_after == 1) - sym->stack.elts[0] = sym->stack.elts[1]; + sym->stack.elts[0] = str_printf(sym, "%s%s", sym->stack.elts[1], sym->stack.elts[0]); else - sym->stack.elts[0] = str_printf(sym, "~%s", sym->stack.elts[1]); + sym->stack.elts[0] = str_printf(sym, "~%s%s", sym->stack.elts[1], sym->stack.elts[0]); /* ctors and dtors don't have return type */ sym->flags |= UNDNAME_NO_FUNCTION_RETURNS; break;
From: Eric Pouech eric.pouech@gmail.com
introduce support for reporting cases where builtin's demangling only differs with white space from native
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/tests/cpp.c | 28 ++++++++++++++++++++++------ dlls/msvcrt/undname.c | 4 ++-- 2 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/dlls/msvcrt/tests/cpp.c b/dlls/msvcrt/tests/cpp.c index 5beaea3bd1a..4449392e9c9 100644 --- a/dlls/msvcrt/tests/cpp.c +++ b/dlls/msvcrt/tests/cpp.c @@ -1107,7 +1107,14 @@ static void test_demangle_datatype(void)
static void test_demangle(void) { - static struct {const char* in; const char* out; const char *broken; unsigned int flags;} test[] = { + static struct { + const char* in; + const char* out; + const char *broken; + /* to report white space only discrepancies between wine & native */ + const char *wine_whitespace; + unsigned int flags; + } test[] = { /* 0 */ {"??0bad_alloc@std@@QAE@ABV01@@Z", "public: __thiscall std::bad_alloc::bad_alloc(class std::bad_alloc const &)", "public: __thiscall std::bad_alloc::bad_alloc(class bad_alloc::bad_alloc const &)"}, @@ -1246,9 +1253,9 @@ static void test_demangle(void) /* 101 */ {"?$_aaa@Vbbb@@", "_aaa<class bbb>"}, /* 102 */ {"?$aaa@Vbbb@ccc@@Vddd@2@", "aaa<class ccc::bbb,class ccc::ddd>"}, /* 103 */ { "??0?$Foo@P6GHPAX0@Z@@QAE@PAD@Z", "public: __thiscall Foo<int (__stdcall*)(void *,void *)>::Foo<int (__stdcall*)(void *,void *)>(char *)"}, -/* 104 */ { "??0?$Foo@P6GHPAX0@Z@@QAE@PAD@Z", "__thiscall Foo<int (__stdcall*)(void *,void *)>::Foo<int (__stdcall*)(void *,void *)>(char *)", NULL, 0x880}, +/* 104 */ { "??0?$Foo@P6GHPAX0@Z@@QAE@PAD@Z", "__thiscall Foo<int (__stdcall*)(void *,void *)>::Foo<int (__stdcall*)(void *,void *)>(char *)", NULL, NULL, 0x880}, /* 105 */ { "?Qux@Bar@@0PAP6AHPAV1@AAH1PAH@ZA", "private: static int (__cdecl** Bar::Qux)(class Bar *,int &,int &,int *)" }, -/* 106 */ { "?Qux@Bar@@0PAP6AHPAV1@AAH1PAH@ZA", "Bar::Qux", NULL, 0x1800}, +/* 106 */ { "?Qux@Bar@@0PAP6AHPAV1@AAH1PAH@ZA", "Bar::Qux", NULL, NULL, 0x1800}, /* 107 */ {"?$AAA@$DBAB@", "AAA<`template-parameter257'>"}, /* 108 */ {"?$AAA@?C@", "AAA<`template-parameter-2'>"}, /* 109 */ {"?$AAA@PAUBBB@@", "AAA<struct BBB *>"}, @@ -1281,11 +1288,11 @@ static void test_demangle(void) /* 125 */ {"?_dispatch@_impl_Engine@SalomeApp@@$R4CE@BA@PPPPPPPM@7AE_NAAVomniCallHandle@@@Z", "[thunk]:public: virtual bool __thiscall SalomeApp::_impl_Engine::_dispatch`vtordispex{36,16,4294967292,8}' (class omniCallHandle &)", "?_dispatch@_impl_Engine@SalomeApp@@$R4CE@BA@PPPPPPPM@7AE_NAAVomniCallHandle@@@Z"}, -/* 126 */ {"?_Doraise@bad_cast@std@@MEBAXXZ", "protected: virtual void __cdecl std::bad_cast::_Doraise(void)", NULL, 0x60}, -/* 127 */ {"??Xstd@@YAAEAV?$complex@M@0@AEAV10@AEBV10@@Z", "class std::complex<float> & ptr64 cdecl std::operator*=(class std::complex<float> & ptr64,class std::complex<float> const & ptr64)", NULL, 1}, +/* 126 */ {"?_Doraise@bad_cast@std@@MEBAXXZ", "protected: virtual void __cdecl std::bad_cast::_Doraise(void)", NULL, NULL, 0x60}, +/* 127 */ {"??Xstd@@YAAEAV?$complex@M@0@AEAV10@AEBV10@@Z", "class std::complex<float> & ptr64 cdecl std::operator*=(class std::complex<float> & ptr64,class std::complex<float> const & ptr64)", NULL, NULL, 1}, /* 128 */ {"??Xstd@@YAAEAV?$complex@M@0@AEAV10@AEBV10@@Z", "class std::complex<float> & std::operator*=(class std::complex<float> &,class std::complex<float> const &)", - "??Xstd@@YAAEAV?$complex@M@0@AEAV10@AEBV10@@Z", 2}, + "??Xstd@@YAAEAV?$complex@M@0@AEAV10@AEBV10@@Z", NULL, 2}, /* 129 */ {"??$run@XVTask_Render_Preview@@@QtConcurrent@@YA?AV?$QFuture@X@@PEAVTask_Render_Preview@@P82@EAAXXZ@Z", "class QFuture<void> __cdecl QtConcurrent::run<void,class Task_Render_Preview>(class Task_Render_Preview * __ptr64,void (__cdecl Task_Render_Preview::*)(void) __ptr64)", "??$run@XVTask_Render_Preview@@@QtConcurrent@@YA?AV?$QFuture@X@@PEAVTask_Render_Preview@@P82@EAAXXZ@Z"}, @@ -1298,6 +1305,8 @@ static void test_demangle(void) /* 136 */ {"??$?0AEAVzzz@BB4@AA@@AEAV012@$0A@@?$pair@Vzzz@BB4@AA@@V123@@std@@QEAA@AEAVzzz@BB4@AA@@0@Z", "public: __cdecl std::pair<class AA::BB4::zzz,class AA::BB4::zzz>::pair<class AA::BB4::zzz,class AA::BB4::zzz><class AA::BB4::zzz & __ptr64,class AA::BB4::zzz & __ptr64,0>(class AA::BB4::zzz & __ptr64,class AA::BB4::zzz & __ptr64) __ptr64"}, /* 137 */ {"??$?BH@?$foo@N@@QEAAHXZ", "public: __cdecl foo<double>::operator<int> int(void) __ptr64"}, +/* 138 */ {"??Bcastop@@QAEHXZ", "public: __thiscall castop::operator int(void)"}, +/* 139 */ {"??Bcastop@@QAE?BHXZ", "public: __thiscall castop::operator int const (void)", NULL, "public: __thiscall castop::operator int const(void)"}, }; int i, num_test = ARRAY_SIZE(test); char* name; @@ -1307,12 +1316,19 @@ static void test_demangle(void) name = p__unDName(0, test[i].in, 0, malloc, free, test[i].flags); ok(name != NULL, "%u: unDName failed\n", i); if (!name) continue; + todo_wine_if(test[i].wine_whitespace) ok( !strcmp(test[i].out, name) || broken(test[i].broken && !strcmp(test[i].broken, name)), "%u: Got name "%s"\n", i, name ); + todo_wine_if(test[i].wine_whitespace) ok( !strcmp(test[i].out, name) || broken(test[i].broken && !strcmp(test[i].broken, name)), "%u: Expected "%s"\n", i, test[i].out ); + if (test[i].wine_whitespace && !strcmp(winetest_platform, "wine")) + { + ok( !strcmp(test[i].wine_whitespace, name), "%u: Got name "%s"\n", i, name ); + ok( !strcmp(test[i].wine_whitespace, name), "%u: Expected (white space) "%s"\n", i, test[i].wine_whitespace ); + } free(name); } } diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index be736631626..c30b7ebb95b 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -1331,7 +1331,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) ct_ret.left = ct_ret.right = NULL; if (cast_op) { - name = str_printf(sym, "%s%s%s", name, ct_ret.left, ct_ret.right); + name = str_printf(sym, "%s %s%s", name, ct_ret.left, ct_ret.right); ct_ret.left = ct_ret.right = NULL; }
@@ -1406,7 +1406,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) case '8': function_name = "operator=="; break; case '9': function_name = "operator!="; break; case 'A': function_name = "operator[]"; break; - case 'B': function_name = "operator "; do_after = 3; break; + case 'B': function_name = "operator"; do_after = 3; break; case 'C': function_name = "operator->"; break; case 'D': function_name = "operator*"; break; case 'E': function_name = "operator++"; break;
From: Eric Pouech eric.pouech@gmail.com
in preparation of semantics modification to this helper (and calling it str_printf might confuse devs as new semantics will diverge from printf's)
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/undname.c | 122 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 61 deletions(-)
diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index c30b7ebb95b..0aa22b485ac 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -226,11 +226,11 @@ static char* str_array_get_ref(struct array* cref, unsigned idx) }
/****************************************************************** - * str_printf + * str_build * Helper for printf type of command (only %s and %c are implemented) * while dynamically allocating the buffer */ -static char* WINAPIV str_printf(struct parsed_symbol* sym, const char* format, ...) +static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, ...) { va_list args; unsigned int len = 1, i, sz; @@ -365,7 +365,7 @@ static char* get_args(struct parsed_symbol* sym, struct array* pmt_ref, BOOL z_t return NULL; /* 'void' terminates an argument list in a function */ if (z_term && !strcmp(ct.left, "void")) break; - if (!str_array_push(sym, str_printf(sym, "%s%s", ct.left, ct.right), -1, + if (!str_array_push(sym, str_build(sym, "%s%s", ct.left, ct.right), -1, &arg_collect)) return NULL; if (!strcmp(ct.left, "...")) break; @@ -377,20 +377,20 @@ static char* get_args(struct parsed_symbol* sym, struct array* pmt_ref, BOOL z_t
if (arg_collect.num == 0 || (arg_collect.num == 1 && !strcmp(arg_collect.elts[0], "void"))) - return str_printf(sym, "%cvoid%c", open_char, close_char); + return str_build(sym, "%cvoid%c", open_char, close_char); for (i = 1; i < arg_collect.num; i++) { - args_str = str_printf(sym, "%s,%s", args_str, arg_collect.elts[i]); + args_str = str_build(sym, "%s,%s", args_str, arg_collect.elts[i]); }
last = args_str ? args_str : arg_collect.elts[0]; if (close_char == '>' && last[strlen(last) - 1] == '>') - args_str = str_printf(sym, "%c%s%s %c", - open_char, arg_collect.elts[0], args_str, close_char); + args_str = str_build(sym, "%c%s%s %c", + open_char, arg_collect.elts[0], args_str, close_char); else - args_str = str_printf(sym, "%c%s%s%c", - open_char, arg_collect.elts[0], args_str, close_char); - + args_str = str_build(sym, "%c%s%s%c", + open_char, arg_collect.elts[0], args_str, close_char); + return args_str; }
@@ -400,7 +400,7 @@ static void append_extended_modifier(struct parsed_symbol *sym, const char **whe { if (sym->flags & UNDNAME_NO_LEADING_UNDERSCORES) str += 2; - *where = *where ? str_printf(sym, "%s %s", *where, str) : str; + *where = *where ? str_build(sym, "%s %s", *where, str) : str; } }
@@ -438,7 +438,7 @@ static BOOL get_modifier(struct parsed_symbol *sym, struct datatype_t *xdt) default: return FALSE; } if (xdt->left && mod) - xdt->left = str_printf(sym, "%s %s", xdt->left, mod); + xdt->left = str_build(sym, "%s %s", xdt->left, mod); else if (mod) xdt->left = mod; return TRUE; @@ -466,11 +466,11 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, default: return FALSE; } if (ref || str_modif || xdt.left || xdt.right) - ct->left = str_printf(sym, " %s%s%s%s%s%s%s", - xdt.left, - xdt.left && ref ? " " : NULL, ref, - (xdt.left || ref) && xdt.right ? " " : NULL, xdt.right, - (xdt.left || ref || xdt.right) && str_modif ? " " : NULL, str_modif); + ct->left = str_build(sym, " %s%s%s%s%s%s%s", + xdt.left, + xdt.left && ref ? " " : NULL, ref, + (xdt.left || ref) && xdt.right ? " " : NULL, xdt.right, + (xdt.left || ref || xdt.right) && str_modif ? " " : NULL, str_modif); else ct->left = NULL; ct->right = NULL; @@ -493,27 +493,27 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, if (ct->left && ct->left[0] == ' ' && !xdt.left) ct->left++;
- ct->left = str_printf(sym, " (%s%s", xdt.left, ct->left); + ct->left = str_build(sym, " (%s%s", xdt.left, ct->left); ct->right = ")"; xdt.left = NULL;
while (num--) - ct->right = str_printf(sym, "%s[%s]", ct->right, get_number(sym)); + ct->right = str_build(sym, "%s[%s]", ct->right, get_number(sym)); }
/* Recurse to get the referred-to type */ if (!demangle_datatype(sym, &sub_ct, pmt_ref, FALSE)) return FALSE; if (xdt.left) - ct->left = str_printf(sym, "%s %s%s", sub_ct.left, xdt.left, ct->left); + ct->left = str_build(sym, "%s %s%s", sub_ct.left, xdt.left, ct->left); else { /* don't insert a space between duplicate '*' */ if (!in_args && ct->left && ct->left[0] && ct->left[1] == '*' && sub_ct.left[strlen(sub_ct.left)-1] == '*') ct->left++; - ct->left = str_printf(sym, "%s%s", sub_ct.left, ct->left); + ct->left = str_build(sym, "%s%s", sub_ct.left, ct->left); } - if (sub_ct.right) ct->right = str_printf(sym, "%s%s", ct->right, sub_ct.right); + if (sub_ct.right) ct->right = str_build(sym, "%s%s", ct->right, sub_ct.right); sym->stack.num = mark; } return TRUE; @@ -572,7 +572,7 @@ static char* get_template_name(struct parsed_symbol* sym) str_array_init(&array_pmt); args = get_args(sym, &array_pmt, FALSE, '<', '>'); if (args != NULL) - name = str_printf(sym, "%s%s", name, args); + name = str_build(sym, "%s%s", name, args); sym->names.num = num_mark; sym->names.start = start_mark; sym->stack.num = stack_mark; @@ -623,7 +623,7 @@ static BOOL get_class(struct parsed_symbol* sym) unsigned int num = sym->names.num;
str_array_init( &sym->stack ); - if (symbol_demangle( sym )) name = str_printf( sym, "`%s'", sym->result ); + if (symbol_demangle( sym )) name = str_build( sym, "`%s'", sym->result ); sym->names.start = start; sym->names.num = num; sym->stack = stack; @@ -631,7 +631,7 @@ static BOOL get_class(struct parsed_symbol* sym) break; default: if (!(name = get_number( sym ))) return FALSE; - name = str_printf( sym, "`%s'", name ); + name = str_build( sym, "`%s'", name ); break; } break; @@ -849,7 +849,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, case 'Y': type_name = "cointerface "; break; } } - ct->left = str_printf(sym, "%s%s", type_name, struct_name); + ct->left = str_build(sym, "%s%s", type_name, struct_name); } break; case '?': @@ -858,7 +858,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, { const char* ptr; if (!(ptr = get_number(sym))) goto done; - ct->left = str_printf(sym, "`template-parameter-%s'", ptr); + ct->left = str_build(sym, "`template-parameter-%s'", ptr); } else { @@ -898,9 +898,9 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, if (!get_modifier(sym, &xdt)) goto done; if (xdt.left) - xdt.left = str_printf(sym, "%s %s", xdt.left, xdt.right); + xdt.left = str_build(sym, "%s %s", xdt.left, xdt.right); else if (xdt.right) - xdt.left = str_printf(sym, " %s", xdt.right); + xdt.left = str_build(sym, " %s", xdt.right); if (!get_calling_convention(*sym->current++, &call_conv, &exported, sym->flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE)) @@ -912,9 +912,9 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, if (!args) goto done; sym->stack.num = mark;
- ct->left = str_printf(sym, "%s%s (%s %s::*", - sub_ct.left, sub_ct.right, call_conv, class); - ct->right = str_printf(sym, ")%s%s", args, xdt.left); + ct->left = str_build(sym, "%s%s (%s %s::*", + sub_ct.left, sub_ct.right, call_conv, class); + ct->right = str_build(sym, ")%s%s", args, xdt.left); } else if (*sym->current == '6') { @@ -936,9 +936,9 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, if (!args) goto done; sym->stack.num = mark;
- ct->left = str_printf(sym, "%s%s (%s*", - sub_ct.left, sub_ct.right, call_conv); - ct->right = str_printf(sym, ")%s", args); + ct->left = str_build(sym, "%s%s (%s*", + sub_ct.left, sub_ct.right, call_conv); + ct->right = str_build(sym, ")%s", args); } else goto done; } @@ -954,7 +954,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, if (sym->flags & UNDNAME_NO_COMPLEX_TYPE) ct->left = enum_name; else - ct->left = str_printf(sym, "enum %s", enum_name); + ct->left = str_build(sym, "enum %s", enum_name); } else goto done; break; @@ -978,7 +978,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, { const char* ptr; if (!(ptr = get_number(sym))) goto done; - ct->left = str_printf(sym, "`template-parameter%s'", ptr); + ct->left = str_build(sym, "`template-parameter%s'", ptr); } break; case 'F': @@ -987,7 +987,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, const char* p2; if (!(p1 = get_number(sym))) goto done; if (!(p2 = get_number(sym))) goto done; - ct->left = str_printf(sym, "{%s,%s}", p1, p2); + ct->left = str_build(sym, "{%s,%s}", p1, p2); } break; case 'G': @@ -998,14 +998,14 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, if (!(p1 = get_number(sym))) goto done; if (!(p2 = get_number(sym))) goto done; if (!(p3 = get_number(sym))) goto done; - ct->left = str_printf(sym, "{%s,%s,%s}", p1, p2, p3); + ct->left = str_build(sym, "{%s,%s,%s}", p1, p2, p3); } break; case 'Q': { const char* ptr; if (!(ptr = get_number(sym))) goto done; - ct->left = str_printf(sym, "`non-type-template-parameter%s'", ptr); + ct->left = str_build(sym, "`non-type-template-parameter%s'", ptr); } break; case '$': @@ -1027,13 +1027,13 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, num = atoi(n1);
while (num--) - arr = str_printf(sym, "%s[%s]", arr, get_number(sym)); + arr = str_build(sym, "%s[%s]", arr, get_number(sym)); }
if (!demangle_datatype(sym, &sub_ct, pmt_ref, FALSE)) goto done;
if (arr) - ct->left = str_printf(sym, "%s %s", sub_ct.left, arr); + ct->left = str_build(sym, "%s %s", sub_ct.left, arr); else ct->left = sub_ct.left; ct->right = sub_ct.right; @@ -1046,7 +1046,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, sym->current++; if (!get_modifier(sym, &xdt)) goto done; if (!demangle_datatype(sym, ct, pmt_ref, in_args)) goto done; - ct->left = str_printf(sym, "%s %s", ct->left, xdt.left); + ct->left = str_build(sym, "%s %s", ct->left, xdt.left); } else if (*sym->current == 'Q') { @@ -1127,7 +1127,7 @@ static BOOL handle_data(struct parsed_symbol* sym)
if (!demangle_datatype(sym, &ct, &pmt, FALSE)) goto done; if (!get_modifier(sym, &xdt)) goto done; - if (xdt.left && xdt.right) xdt.left = str_printf(sym, "%s %s", xdt.left, xdt.right); + if (xdt.left && xdt.right) xdt.left = str_build(sym, "%s %s", xdt.left, xdt.right); else if (!xdt.left) xdt.left = xdt.right; sym->stack.num = mark; } @@ -1142,7 +1142,7 @@ static BOOL handle_data(struct parsed_symbol* sym)
if (!(cls = get_class_name(sym))) goto done; - ct.right = str_printf(sym, "{for `%s'}", cls); + ct.right = str_build(sym, "{for `%s'}", cls); } break; case '8': @@ -1153,7 +1153,7 @@ static BOOL handle_data(struct parsed_symbol* sym) } if (sym->flags & UNDNAME_NAME_ONLY) ct.left = ct.right = xdt.left = NULL;
- sym->result = str_printf(sym, "%s%s%s%s%s%s%s%s", access, + sym->result = str_build(sym, "%s%s%s%s%s%s%s%s", access, member_type, ct.left, xdt.left && ct.left ? " " : NULL, xdt.left, xdt.left || ct.left ? " " : NULL, name, ct.right); @@ -1241,7 +1241,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) case 2: access = "public: "; break; } if (accmem == '$' || (accmem - 'A') % 8 == 6 || (accmem - 'A') % 8 == 7) - access = str_printf(sym, "[thunk]:%s", access ? access : " "); + access = str_build(sym, "[thunk]:%s", access ? access : " ");
if (accmem == '$' && *sym->current != 'B') member_type = "virtual "; @@ -1269,7 +1269,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) n = get_number(sym);
if(!n || *sym->current++ != 'A') goto done; - name = str_printf(sym, "%s{%s,{flat}}' }'", name, n); + name = str_build(sym, "%s{%s,{flat}}' }'", name, n); has_args = FALSE; has_ret = FALSE; } @@ -1284,7 +1284,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) n4 = get_number(sym);
if(!n1 || !n2 || !n3 || !n4) goto done; - name = str_printf(sym, "%s`vtordispex{%s,%s,%s,%s}' ", name, n1, n2, n3, n4); + name = str_build(sym, "%s`vtordispex{%s,%s,%s,%s}' ", name, n1, n2, n3, n4); } else if (accmem == '$') /* vtordisp thunk */ { @@ -1295,10 +1295,10 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) n2 = get_number(sym);
if (!n1 || !n2) goto done; - name = str_printf(sym, "%s`vtordisp{%s,%s}' ", name, n1, n2); + name = str_build(sym, "%s`vtordisp{%s,%s}' ", name, n1, n2); } else if ((accmem - 'A') % 8 == 6 || (accmem - 'A') % 8 == 7) /* a thunk */ - name = str_printf(sym, "%s`adjustor{%s}' ", name, get_number(sym)); + name = str_build(sym, "%s`adjustor{%s}' ", name, get_number(sym));
if (has_args && (accmem == '$' || (accmem <= 'X' && (accmem - 'A') % 8 != 2 && (accmem - 'A') % 8 != 3))) @@ -1306,7 +1306,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) /* Implicit 'this' pointer */ /* If there is an implicit this pointer, const modifier follows */ if (!get_modifier(sym, &xdt)) goto done; - if (xdt.left || xdt.right) xdt.left = str_printf(sym, "%s %s", xdt.left, xdt.right); + if (xdt.left || xdt.right) xdt.left = str_build(sym, "%s %s", xdt.left, xdt.right); }
if (!get_calling_convention(*sym->current++, &call_conv, &exported, @@ -1331,7 +1331,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) ct_ret.left = ct_ret.right = NULL; if (cast_op) { - name = str_printf(sym, "%s %s%s", name, ct_ret.left, ct_ret.right); + name = str_build(sym, "%s %s%s", name, ct_ret.left, ct_ret.right); ct_ret.left = ct_ret.right = NULL; }
@@ -1344,7 +1344,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) /* Note: '()' after 'Z' means 'throws', but we don't care here * Yet!!! FIXME */ - sym->result = str_printf(sym, "%s%s%s%s%s%s%s%s%s%s%s", + sym->result = str_build(sym, "%s%s%s%s%s%s%s%s%s%s%s", access, member_type, ct_ret.left, (ct_ret.left && !ct_ret.right) ? " " : NULL, call_conv, call_conv ? " " : NULL, exported, @@ -1370,7 +1370,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym)
if (demangle_datatype(sym, &ct, NULL, FALSE)) { - sym->result = str_printf(sym, "%s%s", ct.left, ct.right); + sym->result = str_build(sym, "%s%s", ct.left, ct.right); ret = TRUE; } goto done; @@ -1470,7 +1470,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) sym->current++; if (!demangle_datatype(sym, &ct, NULL, FALSE)) goto done; - function_name = str_printf(sym, "%s%s `RTTI Type Descriptor'", + function_name = str_build(sym, "%s%s `RTTI Type Descriptor'", ct.left, ct.right); sym->current--; } @@ -1484,7 +1484,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) n3 = get_number(sym); n4 = get_number(sym); sym->current--; - function_name = str_printf(sym, "`RTTI Base Class Descriptor at (%s,%s,%s,%s)'", + function_name = str_build(sym, "`RTTI Base Class Descriptor at (%s,%s,%s,%s)'", n1, n2, n3, n4); } break; @@ -1507,7 +1507,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) { case 'K': sym->current++; - function_name = str_printf(sym, "operator "" %s", get_literal_string(sym)); + function_name = str_build(sym, "operator "" %s", get_literal_string(sym)); --sym->current; break; default: @@ -1538,7 +1538,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym)
str_array_init(&array_pmt); args = get_args(sym, &array_pmt, FALSE, '<', '>'); - if (args) function_name = function_name ? str_printf(sym, "%s%s", function_name, args) : args; + if (args) function_name = function_name ? str_build(sym, "%s%s", function_name, args) : args; sym->names.num = 0; } switch (do_after) @@ -1581,9 +1581,9 @@ static BOOL symbol_demangle(struct parsed_symbol* sym) /* it's time to set the member name for ctor & dtor */ if (sym->stack.num <= 1) goto done; if (do_after == 1) - sym->stack.elts[0] = str_printf(sym, "%s%s", sym->stack.elts[1], sym->stack.elts[0]); + sym->stack.elts[0] = str_build(sym, "%s%s", sym->stack.elts[1], sym->stack.elts[0]); else - sym->stack.elts[0] = str_printf(sym, "~%s%s", sym->stack.elts[1], sym->stack.elts[0]); + sym->stack.elts[0] = str_build(sym, "~%s%s", sym->stack.elts[1], sym->stack.elts[0]); /* ctors and dtors don't have return type */ sym->flags |= UNDNAME_NO_FUNCTION_RETURNS; break;
From: Eric Pouech eric.pouech@gmail.com
this will append a string with potentially prepending ' ' as separator
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/undname.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index 0aa22b485ac..8a30e0b45b6 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -227,8 +227,12 @@ static char* str_array_get_ref(struct array* cref, unsigned idx)
/****************************************************************** * str_build - * Helper for printf type of command (only %s and %c are implemented) - * while dynamically allocating the buffer + * Helper for printf type of command while dynamically allocating the buffer + * Supported operators: + * + %s inserts the string (as printf does) ; argument can be NULL (inserts nothing) + * + %S same as %s, but insert before the string a ' ' as separator if in + * previous contiguous %s or %S something has been inserted and argument isn't NULL + * + %c inserts a char */ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, ...) { @@ -237,6 +241,7 @@ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, .. char* tmp; char* p; char* t; + BOOL needs_delim = FALSE;
va_start(args, format); for (i = 0; format[i]; i++) @@ -245,10 +250,12 @@ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, .. { switch (format[++i]) { + case 'S': len++; /* fall through */ case 's': t = va_arg(args, char*); if (t) len += strlen(t); break; case 'c': (void)va_arg(args, int); len++; break; - default: i--; /* fall through */ - case '%': len++; break; + default: + ERR("Unsupported escape '%c' in format\n", format[i]); + return NULL; } } else len++; @@ -262,23 +269,30 @@ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, .. { switch (format[++i]) { + case 'S': case 's': t = va_arg(args, char*); - if (t) + if (t && *t) { + if (format[i] == 'S' && needs_delim) + *p++ = ' '; sz = strlen(t); memcpy(p, t, sz); p += sz; + needs_delim = TRUE; } break; case 'c': *p++ = (char)va_arg(args, int); + needs_delim = FALSE; break; - default: i--; /* fall through */ - case '%': *p++ = '%'; break; } } - else *p++ = format[i]; + else + { + *p++ = format[i]; + needs_delim = FALSE; + } } va_end(args); *p = '\0'; @@ -466,11 +480,7 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, default: return FALSE; } if (ref || str_modif || xdt.left || xdt.right) - ct->left = str_build(sym, " %s%s%s%s%s%s%s", - xdt.left, - xdt.left && ref ? " " : NULL, ref, - (xdt.left || ref) && xdt.right ? " " : NULL, xdt.right, - (xdt.left || ref || xdt.right) && str_modif ? " " : NULL, str_modif); + ct->left = str_build(sym, " %s%S%S%S", xdt.left, ref, xdt.right, str_modif); else ct->left = NULL; ct->right = NULL; @@ -1153,10 +1163,7 @@ static BOOL handle_data(struct parsed_symbol* sym) } if (sym->flags & UNDNAME_NAME_ONLY) ct.left = ct.right = xdt.left = NULL;
- sym->result = str_build(sym, "%s%s%s%s%s%s%s%s", access, - member_type, ct.left, - xdt.left && ct.left ? " " : NULL, xdt.left, - xdt.left || ct.left ? " " : NULL, name, ct.right); + sym->result = str_build(sym, "%s%s%s%S%S%s", access, member_type, ct.left, xdt.left, name, ct.right); ret = TRUE; done: return ret;
From: Eric Pouech eric.pouech@gmail.com
making better use of %S operator (especially don't force space at beginning of generated string but rather insert space as separator with %S operator)
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/undname.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index 8a30e0b45b6..8e69213e412 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -480,7 +480,7 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, default: return FALSE; } if (ref || str_modif || xdt.left || xdt.right) - ct->left = str_build(sym, " %s%S%S%S", xdt.left, ref, xdt.right, str_modif); + ct->left = str_build(sym, "%s%S%S%S", xdt.left, ref, xdt.right, str_modif); else ct->left = NULL; ct->right = NULL; @@ -500,10 +500,7 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, if (!(n1 = get_number(sym))) return FALSE; num = atoi(n1);
- if (ct->left && ct->left[0] == ' ' && !xdt.left) - ct->left++; - - ct->left = str_build(sym, " (%s%s", xdt.left, ct->left); + ct->left = str_build(sym, "(%s%S", xdt.left, ct->left); ct->right = ")"; xdt.left = NULL;
@@ -514,15 +511,15 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, /* Recurse to get the referred-to type */ if (!demangle_datatype(sym, &sub_ct, pmt_ref, FALSE)) return FALSE; - if (xdt.left) - ct->left = str_build(sym, "%s %s%s", sub_ct.left, xdt.left, ct->left); - else - { - /* don't insert a space between duplicate '*' */ - if (!in_args && ct->left && ct->left[0] && ct->left[1] == '*' && sub_ct.left[strlen(sub_ct.left)-1] == '*') - ct->left++; + /* don't insert a space between duplicate '*' */ + if (!in_args && + sub_ct.left && sub_ct.left[0] && sub_ct.left[strlen(sub_ct.left) - 1] == '*' && + !xdt.left && + ct->left && ct->left[0] == '*') ct->left = str_build(sym, "%s%s", sub_ct.left, ct->left); - } + else + ct->left = str_build(sym, "%s%S%S", sub_ct.left, xdt.left, ct->left); + if (sub_ct.right) ct->right = str_build(sym, "%s%s", ct->right, sub_ct.right); sym->stack.num = mark; }
From: Eric Pouech eric.pouech@gmail.com
simplifying a bunch of invocations to str_build
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/undname.c | 75 +++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 31 deletions(-)
diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index 8e69213e412..5976333a56e 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -233,6 +233,12 @@ static char* str_array_get_ref(struct array* cref, unsigned idx) * + %S same as %s, but insert before the string a ' ' as separator if in * previous contiguous %s or %S something has been inserted and argument isn't NULL * + %c inserts a char + * + * Returns: + * + if format is only made of %s and %S, + * + returns NULL if all arguments are NULL + * + returns arg if only one argument (call it arg) is not NULL + + + allocates a new buffer in all the other cases */ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, ...) { @@ -242,6 +248,8 @@ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, .. char* p; char* t; BOOL needs_delim = FALSE; + BOOL requires_alloc = FALSE; + char* single = NULL;
va_start(args, format); for (i = 0; format[i]; i++) @@ -250,17 +258,37 @@ static char* WINAPIV str_build(struct parsed_symbol* sym, const char* format, .. { switch (format[++i]) { - case 'S': len++; /* fall through */ - case 's': t = va_arg(args, char*); if (t) len += strlen(t); break; - case 'c': (void)va_arg(args, int); len++; break; + case 'S': + len++; + /* fall through */ + case 's': + t = va_arg(args, char*); + if (t) + { + len += strlen(t); + if (!single) single = t; + else requires_alloc = TRUE; + } + break; + case 'c': + (void)va_arg(args, int); + len++; + requires_alloc = TRUE; + break; default: ERR("Unsupported escape '%c' in format\n", format[i]); return NULL; } } - else len++; + else + { + len++; + requires_alloc = TRUE; + } } va_end(args); + if (!requires_alloc) return single; + if (!(tmp = und_alloc(sym, len))) return NULL; va_start(args, format); for (p = tmp, i = 0; format[i]; i++) @@ -414,7 +442,7 @@ static void append_extended_modifier(struct parsed_symbol *sym, const char **whe { if (sym->flags & UNDNAME_NO_LEADING_UNDERSCORES) str += 2; - *where = *where ? str_build(sym, "%s %s", *where, str) : str; + *where = str_build(sym, "%s%S", *where, str); } }
@@ -451,10 +479,7 @@ static BOOL get_modifier(struct parsed_symbol *sym, struct datatype_t *xdt) case 'D': mod = "const volatile"; break; default: return FALSE; } - if (xdt->left && mod) - xdt->left = str_build(sym, "%s %s", xdt->left, mod); - else if (mod) - xdt->left = mod; + xdt->left = str_build(sym, "%s%S", xdt->left, mod); return TRUE; }
@@ -479,10 +504,7 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, case '$': ref = "&&"; str_modif = NULL; break; default: return FALSE; } - if (ref || str_modif || xdt.left || xdt.right) - ct->left = str_build(sym, "%s%S%S%S", xdt.left, ref, xdt.right, str_modif); - else - ct->left = NULL; + ct->left = str_build(sym, "%s%S%S%S", xdt.left, ref, xdt.right, str_modif); ct->right = NULL;
if (get_modifier(sym, &xdt)) @@ -520,7 +542,7 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, else ct->left = str_build(sym, "%s%S%S", sub_ct.left, xdt.left, ct->left);
- if (sub_ct.right) ct->right = str_build(sym, "%s%s", ct->right, sub_ct.right); + ct->right = str_build(sym, "%s%s", ct->right, sub_ct.right); sym->stack.num = mark; } return TRUE; @@ -565,7 +587,7 @@ static char* get_literal_string(struct parsed_symbol* sym) */ static char* get_template_name(struct parsed_symbol* sym) { - char *name, *args; + char *name; unsigned num_mark = sym->names.num; unsigned start_mark = sym->names.start; unsigned stack_mark = sym->stack.num; @@ -577,9 +599,7 @@ static char* get_template_name(struct parsed_symbol* sym) return FALSE; } str_array_init(&array_pmt); - args = get_args(sym, &array_pmt, FALSE, '<', '>'); - if (args != NULL) - name = str_build(sym, "%s%s", name, args); + name = str_build(sym, "%s%s", name, get_args(sym, &array_pmt, FALSE, '<', '>')); sym->names.num = num_mark; sym->names.start = start_mark; sym->stack.num = stack_mark; @@ -904,10 +924,6 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, goto done; if (!get_modifier(sym, &xdt)) goto done; - if (xdt.left) - xdt.left = str_build(sym, "%s %s", xdt.left, xdt.right); - else if (xdt.right) - xdt.left = str_build(sym, " %s", xdt.right); if (!get_calling_convention(*sym->current++, &call_conv, &exported, sym->flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE)) @@ -921,7 +937,8 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct,
ct->left = str_build(sym, "%s%s (%s %s::*", sub_ct.left, sub_ct.right, call_conv, class); - ct->right = str_build(sym, ")%s%s", args, xdt.left); + ct->right = str_build(sym, ")%s%s%s%s", + args, xdt.left, xdt.left || xdt.right ? " " : NULL, xdt.right); } else if (*sym->current == '6') { @@ -1039,10 +1056,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct,
if (!demangle_datatype(sym, &sub_ct, pmt_ref, FALSE)) goto done;
- if (arr) - ct->left = str_build(sym, "%s %s", sub_ct.left, arr); - else - ct->left = sub_ct.left; + ct->left = str_build(sym, "%s%S", sub_ct.left, arr); ct->right = sub_ct.right; sym->stack.num = mark; } @@ -1134,8 +1148,7 @@ static BOOL handle_data(struct parsed_symbol* sym)
if (!demangle_datatype(sym, &ct, &pmt, FALSE)) goto done; if (!get_modifier(sym, &xdt)) goto done; - if (xdt.left && xdt.right) xdt.left = str_build(sym, "%s %s", xdt.left, xdt.right); - else if (!xdt.left) xdt.left = xdt.right; + xdt.left = str_build(sym, "%s%S", xdt.left, xdt.right); sym->stack.num = mark; } break; @@ -1310,7 +1323,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) /* Implicit 'this' pointer */ /* If there is an implicit this pointer, const modifier follows */ if (!get_modifier(sym, &xdt)) goto done; - if (xdt.left || xdt.right) xdt.left = str_build(sym, "%s %s", xdt.left, xdt.right); + xdt.left = str_build(sym, "%s%s%s", xdt.left, xdt.left || xdt.right ? " " : NULL, xdt.right); }
if (!get_calling_convention(*sym->current++, &call_conv, &exported, @@ -1542,7 +1555,7 @@ static BOOL symbol_demangle(struct parsed_symbol* sym)
str_array_init(&array_pmt); args = get_args(sym, &array_pmt, FALSE, '<', '>'); - if (args) function_name = function_name ? str_build(sym, "%s%s", function_name, args) : args; + function_name = str_build(sym, "%s%s", function_name, args); sym->names.num = 0; } switch (do_after)
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/tests/cpp.c | 6 +++++ dlls/msvcrt/undname.c | 56 ++++++++++++++++++++++++++++++----------- 2 files changed, 47 insertions(+), 15 deletions(-)
diff --git a/dlls/msvcrt/tests/cpp.c b/dlls/msvcrt/tests/cpp.c index 4449392e9c9..5560b28e182 100644 --- a/dlls/msvcrt/tests/cpp.c +++ b/dlls/msvcrt/tests/cpp.c @@ -1307,6 +1307,12 @@ static void test_demangle(void) /* 137 */ {"??$?BH@?$foo@N@@QEAAHXZ", "public: __cdecl foo<double>::operator<int> int(void) __ptr64"}, /* 138 */ {"??Bcastop@@QAEHXZ", "public: __thiscall castop::operator int(void)"}, /* 139 */ {"??Bcastop@@QAE?BHXZ", "public: __thiscall castop::operator int const (void)", NULL, "public: __thiscall castop::operator int const(void)"}, +/* 140 */ {"?pfield@@3PTAA@@DT1@", "char const volatile AA::* const volatile pfield"}, +/* 141 */ {"?ptititi1@@3PEQtititi@@IEQ1@", "unsigned int tititi::* __ptr64 __ptr64 ptititi1"}, +/* 142 */ {"?ptititi2@@3PERtititi@@IER1@", "unsigned int const tititi::* __ptr64 const __ptr64 ptititi2"}, +/* 143 */ {"?ptititi3@@3PEStititi@@IES1@", "unsigned int volatile tititi::* __ptr64 volatile __ptr64 ptititi3"}, +/* 144 */ {"?ptititi4@@3PETtititi@@IET1@", "unsigned int const volatile tititi::* __ptr64 const volatile __ptr64 ptititi4"}, +/* 145 */ {"?ptititi4v@@3RETtititi@@IET1@", "unsigned int const volatile tititi::* __ptr64 const volatile __ptr64 ptititi4v"}, }; int i, num_test = ARRAY_SIZE(test); char* name; diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index 5976333a56e..17d0330bc7d 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -85,6 +85,7 @@ struct datatype_t };
static BOOL symbol_demangle(struct parsed_symbol* sym); +static char* get_class_name(struct parsed_symbol* sym);
/****************************************************************** * und_alloc @@ -466,31 +467,50 @@ static void get_extended_modifier(struct parsed_symbol *sym, struct datatype_t * * get_modifier * Parses the type modifier. Always returns static strings. */ -static BOOL get_modifier(struct parsed_symbol *sym, struct datatype_t *xdt) +static BOOL get_modifier(struct parsed_symbol *sym, struct datatype_t *xdt, const char** pclass) { const char* mod; + char ch;
get_extended_modifier(sym, xdt); - switch (*sym->current++) + switch (ch = *sym->current++) { case 'A': mod = NULL; break; case 'B': mod = "const"; break; case 'C': mod = "volatile"; break; case 'D': mod = "const volatile"; break; + case 'Q': mod = NULL; break; + case 'R': mod = "const"; break; + case 'S': mod = "volatile"; break; + case 'T': mod = "const volatile"; break; default: return FALSE; } xdt->left = str_build(sym, "%s%S", xdt->left, mod); + if (ch >= 'Q' && ch <= 'T') /* pointer to member, fetch class */ + { + const char* class = get_class_name(sym); + if (!class) return FALSE; + if (!pclass) + { + FIXME("Got pointer to class %s member without storage\n", class); + return FALSE; + } + *pclass = class; + } + else if (pclass) *pclass = NULL; return TRUE; }
static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, struct array *pmt_ref, char modif, BOOL in_args) { - struct datatype_t xdt; + struct datatype_t xdt1; + struct datatype_t xdt2; const char* ref; const char* str_modif; + const char* class;
- get_extended_modifier(sym, &xdt); + get_extended_modifier(sym, &xdt1);
switch (modif) { @@ -504,14 +524,17 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, case '$': ref = "&&"; str_modif = NULL; break; default: return FALSE; } - ct->left = str_build(sym, "%s%S%S%S", xdt.left, ref, xdt.right, str_modif); ct->right = NULL;
- if (get_modifier(sym, &xdt)) + if (get_modifier(sym, &xdt2, &class)) { unsigned mark = sym->stack.num; struct datatype_t sub_ct;
+ if (class) + ct->left = str_build(sym, "%s%S%s%s%S%s", xdt1.left, class, class ? "::" : NULL, ref, xdt1.right, str_modif); + else + ct->left = str_build(sym, "%s%S%S%S", xdt1.left, ref, xdt1.right, str_modif); /* multidimensional arrays */ if (*sym->current == 'Y') { @@ -522,9 +545,9 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, if (!(n1 = get_number(sym))) return FALSE; num = atoi(n1);
- ct->left = str_build(sym, "(%s%S", xdt.left, ct->left); + ct->left = str_build(sym, "(%s%S", xdt2.left, ct->left); ct->right = ")"; - xdt.left = NULL; + xdt2.left = NULL;
while (num--) ct->right = str_build(sym, "%s[%s]", ct->right, get_number(sym)); @@ -536,15 +559,17 @@ static BOOL get_modified_type(struct datatype_t *ct, struct parsed_symbol* sym, /* don't insert a space between duplicate '*' */ if (!in_args && sub_ct.left && sub_ct.left[0] && sub_ct.left[strlen(sub_ct.left) - 1] == '*' && - !xdt.left && + !xdt2.left && ct->left && ct->left[0] == '*') ct->left = str_build(sym, "%s%s", sub_ct.left, ct->left); else - ct->left = str_build(sym, "%s%S%S", sub_ct.left, xdt.left, ct->left); + ct->left = str_build(sym, "%s%S%S", sub_ct.left, xdt2.left, ct->left);
ct->right = str_build(sym, "%s%s", ct->right, sub_ct.right); sym->stack.num = mark; } + else + ct->left = str_build(sym, "%s%S%S%S", xdt1.left, ref, xdt1.right, str_modif); return TRUE; }
@@ -922,7 +947,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct,
if (!(class = get_class_name(sym))) goto done; - if (!get_modifier(sym, &xdt)) + if (!get_modifier(sym, &xdt, NULL)) goto done; if (!get_calling_convention(*sym->current++, &call_conv, &exported, @@ -1065,7 +1090,7 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, struct datatype_t xdt;
sym->current++; - if (!get_modifier(sym, &xdt)) goto done; + if (!get_modifier(sym, &xdt, NULL)) goto done; if (!demangle_datatype(sym, ct, pmt_ref, in_args)) goto done; ct->left = str_build(sym, "%s %s", ct->left, xdt.left); } @@ -1143,11 +1168,12 @@ static BOOL handle_data(struct parsed_symbol* sym) { unsigned mark = sym->stack.num; struct array pmt; + const char* class;
str_array_init(&pmt);
if (!demangle_datatype(sym, &ct, &pmt, FALSE)) goto done; - if (!get_modifier(sym, &xdt)) goto done; + if (!get_modifier(sym, &xdt, &class)) goto done; xdt.left = str_build(sym, "%s%S", xdt.left, xdt.right); sym->stack.num = mark; } @@ -1155,7 +1181,7 @@ static BOOL handle_data(struct parsed_symbol* sym) case '6' : /* compiler generated static */ case '7' : /* compiler generated static */ ct.left = ct.right = NULL; - if (!get_modifier(sym, &xdt)) goto done; + if (!get_modifier(sym, &xdt, NULL)) goto done; if (*sym->current != '@') { char* cls = NULL; @@ -1322,7 +1348,7 @@ static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) { /* Implicit 'this' pointer */ /* If there is an implicit this pointer, const modifier follows */ - if (!get_modifier(sym, &xdt)) goto done; + if (!get_modifier(sym, &xdt, NULL)) goto done; xdt.left = str_build(sym, "%s%s%s", xdt.left, xdt.left || xdt.right ? " " : NULL, xdt.right); }
From: Eric Pouech eric.pouech@gmail.com
introducing get_function_signature to factorize parsing of function signature
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/msvcrt/tests/cpp.c | 1 + dlls/msvcrt/undname.c | 86 +++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/dlls/msvcrt/tests/cpp.c b/dlls/msvcrt/tests/cpp.c index 5560b28e182..b62946221a5 100644 --- a/dlls/msvcrt/tests/cpp.c +++ b/dlls/msvcrt/tests/cpp.c @@ -1313,6 +1313,7 @@ static void test_demangle(void) /* 143 */ {"?ptititi3@@3PEStititi@@IES1@", "unsigned int volatile tititi::* __ptr64 volatile __ptr64 ptititi3"}, /* 144 */ {"?ptititi4@@3PETtititi@@IET1@", "unsigned int const volatile tititi::* __ptr64 const volatile __ptr64 ptititi4"}, /* 145 */ {"?ptititi4v@@3RETtititi@@IET1@", "unsigned int const volatile tititi::* __ptr64 const volatile __ptr64 ptititi4v"}, +/* 146 */ {"?foo@@YAX_NV?$B@$$A6A_N_N@Z@@@Z", "void __cdecl foo(bool,class B<bool __cdecl(bool)>)"}, }; int i, num_test = ARRAY_SIZE(test); char* name; diff --git a/dlls/msvcrt/undname.c b/dlls/msvcrt/undname.c index 17d0330bc7d..1fa37dd3297 100644 --- a/dlls/msvcrt/undname.c +++ b/dlls/msvcrt/undname.c @@ -851,6 +851,32 @@ static const char* get_extended_type(char c) return type_string; }
+struct function_signature +{ + const char* call_conv; + const char* exported; + struct datatype_t return_ct; + const char* arguments; +}; + +static BOOL get_function_signature(struct parsed_symbol* sym, struct array* pmt_ref, + struct function_signature* fs) +{ + unsigned mark = sym->stack.num; + + if (!get_calling_convention(*sym->current++, + &fs->call_conv, &fs->exported, + sym->flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE) || + !demangle_datatype(sym, &fs->return_ct, pmt_ref, FALSE)) + return FALSE; + + if (!(fs->arguments = get_args(sym, pmt_ref, TRUE, '(', ')'))) + return FALSE; + sym->stack.num = mark; + + return TRUE; +} + /******************************************************************* * demangle_datatype * @@ -935,13 +961,9 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, * others who knows.. */ if (*sym->current == '8') { - char* args = NULL; - const char* call_conv; - const char* exported; - struct datatype_t sub_ct; - unsigned mark = sym->stack.num; - const char* class; - struct datatype_t xdt; + struct function_signature fs; + const char* class; + struct datatype_t xdt;
sym->current++;
@@ -949,45 +971,26 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, goto done; if (!get_modifier(sym, &xdt, NULL)) goto done; - if (!get_calling_convention(*sym->current++, - &call_conv, &exported, - sym->flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE)) + if (!get_function_signature(sym, pmt_ref, &fs)) goto done; - if (!demangle_datatype(sym, &sub_ct, pmt_ref, FALSE)) - goto done; - - args = get_args(sym, pmt_ref, TRUE, '(', ')'); - if (!args) goto done; - sym->stack.num = mark;
ct->left = str_build(sym, "%s%s (%s %s::*", - sub_ct.left, sub_ct.right, call_conv, class); + fs.return_ct.left, fs.return_ct.right, fs.call_conv, class); ct->right = str_build(sym, ")%s%s%s%s", - args, xdt.left, xdt.left || xdt.right ? " " : NULL, xdt.right); + fs.arguments, xdt.left, xdt.left || xdt.right ? " " : NULL, xdt.right); } else if (*sym->current == '6') { - char* args = NULL; - const char* call_conv; - const char* exported; - struct datatype_t sub_ct; - unsigned mark = sym->stack.num; + struct function_signature fs;
sym->current++;
- if (!get_calling_convention(*sym->current++, - &call_conv, &exported, - sym->flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE) || - !demangle_datatype(sym, &sub_ct, pmt_ref, FALSE)) + if (!get_function_signature(sym, pmt_ref, &fs)) goto done;
- args = get_args(sym, pmt_ref, TRUE, '(', ')'); - if (!args) goto done; - sym->stack.num = mark; - ct->left = str_build(sym, "%s%s (%s*", - sub_ct.left, sub_ct.right, call_conv); - ct->right = str_build(sym, ")%s", args); + fs.return_ct.left, fs.return_ct.right, fs.call_conv); + ct->right = str_build(sym, ")%s", fs.arguments); } else goto done; } @@ -1058,7 +1061,22 @@ static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, } break; case '$': - if (*sym->current == 'B') + if (*sym->current == 'A') + { + sym->current++; + if (*sym->current == '6') + { + struct function_signature fs; + + sym->current++; + + if (!get_function_signature(sym, pmt_ref, &fs)) + goto done; + ct->left = str_build(sym, "%s%s %s%s", + fs.return_ct.left, fs.return_ct.right, fs.call_conv, fs.arguments); + } + } + else if (*sym->current == 'B') { unsigned mark = sym->stack.num; struct datatype_t sub_ct;
pushed a V2: patch #1: - added test case for cast operator with template - updated commit message
patch #2: - added test case for space after qualifier in cast operator - updated unmangling test infra to support wine only (white space) changes
patch #3: - unchanged
patch #4: - renamed local variable s_printed into needs_delim - fails on format outside [sSc] - remove stray empty line
patch #5: - unchanged
patch #6: - renamed local variable pure_concat into requires_alloc (and inverted logic) - removed stray while line
for Piotr, from previous threads - I implemented a simple white space handling (as wine's output will always be the same, testing a different string should do the trick instead of trying to implement a function to test the changes)
when this series is applied, I'll send a new series with: - use of enums for do_after - removing the quirk for consecutive '*'...
Have you considered something like in attached patch instead of introducing %S format? I think it's much more robust and I hope it will be possible to remove all hacks/special handling this way (probably except of end of templates case (> >)).
The general idea is to introduce OPTIONAL_WS that is used to insert space in all the places where it's optional. The space is added only if first string ends with OPTIONAL_WS and second starts with OPTIONAL_WS. The attached patch was not really tested, it's just a quick WIP version to demonstrate the idea.
[tmp.diff](/uploads/399b0a804f25b1c9adb6e820ba309429/tmp.diff)
On Mon Jul 25 15:31:45 2022 +0000, Piotr Caban wrote:
Have you considered something like in attached patch instead of introducing %S format? I think it's much more robust and I hope it will be possible to remove all hacks/special handling this way (probably except of end of templates case (> >)). The general idea is to introduce OPTIONAL_WS that is used to insert space in all the places where it's optional. The space is added only if first string ends with OPTIONAL_WS and second starts with OPTIONAL_WS. The attached patch was not really tested, it's just a quick WIP version to demonstrate the idea. [tmp.diff](/uploads/399b0a804f25b1c9adb6e820ba309429/tmp.diff)
Hi Piotr,
the good side: it seems to be a step into the right direction (after adding a couple of missed items - not in wine regression list, though, --they should be added--), this seems to output things fine
so perhaps, we can avoid adding ws only discrepancies in regression testing (*)
what it shows (compared to current implementation): - current implement relies on string concatenation and hard coding spaces (with a couple of issues, and trying to patch things) - introducing a state (pending WS '\1') in the output buffer seems an interesting idea and helps contextualize when ws are needed or not
*BUT* there are a bunch of things I don't like in the solution: - inserting optional WS both on LHS and RHS of a same word doesn't seem right - it looks like the dual LHS/RHS have been inserted to handle the '*' case (with either a space before in most of the cases, and sometimes not) - and even defining a keyword with OPTIONAL_WS doesn't seem right (whatever the side)
my gut feeling is that native code looks similar to (in C++)
``` std:stringstream sstr;
if (...) sstr << "const" << undname::ws; else if (...) sstr << "volatile" << undname::ws; else if (...) sstr << "const volatile" << undname::ws;
//
if (...) sstr << "*" << undname::ws; ```
where undname::ws should an iomanip for handling a pending white space separator
in other words: - the control of ws should be attached to stream output/handling functions, not the keyword them selves - it requires to store a pending ws state to be resolved later on, depending on incoming output
here's an attempt to get closer to the above: - derived from your proposal - it only uses RHS optional white spaces - not all possible cleanups have been done - still WIP at this stage - using a trailing '\1' to mark the str_print internal buffer with potential trailing space, and next inserted character is used to decide how to handle the '\1'
[err](/uploads/f1bc1e97dec61387a09ea216d641011f/err)
I'm still unhappy with - the new operator %t to cancel pending ws => still have to test whether moving the optional ws inserting to callers solve the issue
so I'd rather go for a solution where: - '\1' trailing in strings (internal to str_printf) is here to mark optinal WS - callers should use (say '%w') to insert an optional ws
comments welcomed
(*) this one shows also bogus output (note the ' ' after the const and before the ',') (and I have other template examples with 'T const' parameters not generating the extra space... sigh)
?meth1@ZZZ@?$meth2@V?$YYY@V?$WWW@DU?$XXXs@D@RRR@@V?$UUU@D@2@@RRR@@PAVVVV@TTT@@U?$less@V?$WWW@DU?$XXX@D@RRR@@V?$UUU@D@2@@RRR@@@2@V?$UUU@U?$pair@$$CBV?$WWW@DU?$XXX@D@RRR@@V?$UUU@D@2@@std@@PAVVVV@TTT@@@RRR@@@2@$0A@@RRR@@@RRR@@QAEXXZ public: void __thiscall RRR::meth2<class RRR::YYY<class RRR::WWW<char,struct RRR::XXXs<char>,class RRR::UUU<char> >,class TTT::VVV *,struct RRR::less<class RRR::WWW<char,struct sRRR::XXX<char>,class RRR::UUU<char> > >,class RRR::UUU<struct RRR::pair<class std::WWW<char,struct RRR::XXX<char>,class RRR::UUU<char> > const ,class TTT::VVV *> >,0> >::ZZZ::meth1(void)"
On Mon Jul 25 15:31:45 2022 +0000, eric pouech wrote:
Hi Piotr, the good side: it seems to be a step into the right direction (after adding a couple of missed items - not in wine regression list, though, --they should be added--), this seems to output things fine so perhaps, we can avoid adding ws only discrepancies in regression testing (*) what it shows (compared to current implementation):
- current implement relies on string concatenation and hard coding
spaces (with a couple of issues, and trying to patch things)
- introducing a state (pending WS '\1') in the output buffer seems an
interesting idea and helps contextualize when ws are needed or not *BUT* there are a bunch of things I don't like in the solution:
- inserting optional WS both on LHS and RHS of a same word doesn't seem right
- it looks like the dual LHS/RHS have been inserted to handle the '*'
case (with either a space before in most of the cases, and sometimes not)
- and even defining a keyword with OPTIONAL_WS doesn't seem right
(whatever the side) my gut feeling is that native code looks similar to (in C++)
std:stringstream sstr; if (...) sstr << "const" << undname::ws; else if (...) sstr << "volatile" << undname::ws; else if (...) sstr << "const volatile" << undname::ws; // if (...) sstr << "*" << undname::ws;
where undname::ws should an iomanip for handling a pending white space separator in other words:
- the control of ws should be attached to stream output/handling
functions, not the keyword them selves
- it requires to store a pending ws state to be resolved later on,
depending on incoming output here's an attempt to get closer to the above:
- derived from your proposal
- it only uses RHS optional white spaces
- not all possible cleanups have been done
- still WIP at this stage
- using a trailing '\1' to mark the str_print internal buffer with
potential trailing space, and next inserted character is used to decide how to handle the '\1' [err](/uploads/f1bc1e97dec61387a09ea216d641011f/err) I'm still unhappy with
- the new operator %t to cancel pending ws
=> still have to test whether moving the optional ws inserting to callers solve the issue so I'd rather go for a solution where:
- '\1' trailing in strings (internal to str_printf) is here to mark
optinal WS
- callers should use (say '%w') to insert an optional ws
comments welcomed (*) this one shows also bogus output (note the ' ' after the const and before the ',') (and I have other template examples with 'T const' parameters not generating the extra space... sigh) ?meth1@ZZZ@?$meth2@V?$YYY@V?$WWW@DU?$XXXs@D@RRR@@V?$UUU@D@2@@RRR@@PAVVVV@TTT@@U?$less@V?$WWW@DU?$XXX@D@RRR@@V?$UUU@D@2@@RRR@@@2@V?$UUU@U?$pair@$$CBV?$WWW@DU?$XXX@D@RRR@@V?$UUU@D@2@@std@@PAVVVV@TTT@@@RRR@@@2@$0A@@RRR@@@RRR@@QAEXXZ public: void __thiscall RRR::meth2<class RRR::YYY<class RRR::WWW<char,struct RRR::XXXs<char>,class RRR::UUU<char> >,class TTT::VVV *,struct RRR::less<class RRR::WWW<char,struct sRRR::XXX<char>,class RRR::UUU<char> > >,class RRR::UUU<struct RRR::pair<class std::WWW<char,struct RRR::XXX<char>,class RRR::UUU<char>
const ,class TTT::VVV *> >,0> >::ZZZ::meth1(void)"
I generally like your idea but I have few comments: - I also don't like the %t format, I think it would be best if you find a way to avoid it (I see it as (maybe) useful way of adding hacks that would be nice to avoid) - I don't like handle_trailing_ws helper design, I don't think the decision should be made basing on the character value (it adds some hidden restrictions about str_printf usage e.g. in case of differentiating between operator* and pointer)
Maybe the code can be changed in a way that "optinal white space" affects only %s parameter instead (so it's closer to your initial patch but adds trailing WS instead of leading WS and preserves the OPTIONAL_WS tag between str_printf calls)?
On Tue Jul 26 11:26:36 2022 +0000, Piotr Caban wrote:
I generally like your idea but I have few comments:
- I also don't like the %t format, I think it would be best if you find
a way to avoid it (I see it as (maybe) useful way of adding hacks that would be nice to avoid)
- I don't like handle_trailing_ws helper design, I don't think the
decision should be made basing on the character value (it adds some hidden restrictions about str_printf usage e.g. in case of differentiating between operator* and pointer) Maybe the code can be changed in a way that "optinal white space" affects only %s parameter instead (so it's closer to your initial patch but adds trailing WS instead of leading WS and preserves the OPTIONAL_WS tag between str_printf calls)?
just to summarize: - we need to define when inserting spaces (or not) between <X> and <Y> - current codes tries to guess from <X> and <Y> output, but it's fragile, doesn't cover all cases - we tried with black magic on <X> and <Y> (with RHS and LHS optional ws); but it hides the reason why it has to be inserted or not and lacks potential flexibility - we tried with starting character of <Y>; it hides the reason why is has to be inserted, and lacks potential flexibility - furthermore, + we have cases (like the consecutive '*') where it's inner type (ptr to function) that controls outter type (pointer to) for not inserting a char + but we have cases, where it's the outter type (cast operator to) which controls whether a ws has to be inserted or not (cast operators)
I only see two options: - pass information along to control the ws output (up & down unfortunately) - rewrite all the type handling code to decouple parsing from synthetizing output and let caller do what they want
second option allows any possible flexibility but at the cost of rewriting / changing all the type handling code in undname.c. Looks like a heavy hammer for a couple of changes it'll also put the hacks where we need them, whereas first option will push that complexity/operation to other places (even if we tried to make them as generic as possible)
given the size of the change, I tried first option by passing up (in datatype_t) and down (as flags to internal helpers) here's the series that tackle: - the '*' consecutive handling - the templates in ctor/dtor/castop (needed for next one) - the cast operator support (including correct ws support)
using this, it'll (likely) be: - we don't need ws_wine_broken support in tests - should cover a couple of ws issues (at least the ones that are ok to fix...)
please let me known what you think of this...[err](/uploads/f480690aab88eea594938134c5872174/err)