Mercurial > pub > dyncall > dyncall
changeset 611:d94b053311a7
test/plain_c++:
- refactored to share code
- extended to treat default this calls and explicitly as cdecl declared ones, separately
- prev point fixes implicitly on x86 a wrong dcMode setting (assumed that cdecl for methods would default to the native this call convention, but actually does not, applies cdecl verbatim)
- extended aggr tests to also test explicit cdecl as well as MS thiscalls on x86
- simplified
author | Tassilo Philipp |
---|---|
date | Thu, 29 Sep 2022 11:47:54 +0200 |
parents | fea865cd1305 |
children | 086362f4ae3f |
files | test/plain_c++/test_main.cc |
diffstat | 1 files changed, 114 insertions(+), 147 deletions(-) [+] |
line wrap: on
line diff
--- a/test/plain_c++/test_main.cc Thu Sep 29 10:51:24 2022 +0200 +++ b/test/plain_c++/test_main.cc Thu Sep 29 11:47:54 2022 +0200 @@ -46,27 +46,6 @@ /* ------------------------------------------------------------------------- - * test: identity function calls - * ------------------------------------------------------------------------- */ - -#define DEF_FUNCS(API,NAME) \ -void API fun_##NAME##_v() { } \ -DCbool API fun_##NAME##_b(DCbool x) { return x; } \ -DCint API fun_##NAME##_i(DCint x) { return x; } \ -DClong API fun_##NAME##_j(DClong x) { return x; } \ -DClonglong API fun_##NAME##_l(DClonglong x) { return x; } \ -DCfloat API fun_##NAME##_f(DCfloat x) { return x; } \ -DCdouble API fun_##NAME##_d(DCdouble x) { return x; } \ -DCpointer API fun_##NAME##_p(DCpointer x) { return x; } - -/* __cdecl */ - -#if !defined(DC__OS_Win32) -# define __cdecl -#endif - - -/* ------------------------------------------------------------------------- * test: identity this calls * ------------------------------------------------------------------------- */ @@ -81,12 +60,10 @@ DCpointer p; }; -/* C++ class using __cdecl this call */ - -// #define VTBI_DESTRUCTOR 0 /* - * the layout of the VTable is non-standard and it is not clear what is the initial real first method index. + * the layout of the VTable is non-standard and it is not clear what is the + * initial real first method index. * so far it turns out, *iff* dtor is defined, that: * on msvc/x86 : 1 * on msvc/x64 : 1 @@ -119,32 +96,44 @@ #define VTBI_GET_POINTER VTBI_BASE+13 #define VTBI_SUM_3_INTS VTBI_BASE+14 -class Value -{ -public: - virtual ~Value() {} +#define TEST_CLASS(NAME, CCONV) \ +class NAME \ +{ \ +public: \ + virtual ~NAME() { } \ + \ + virtual void CCONV setBool(DCbool x) { mValue.B = x; } \ + virtual DCbool CCONV getBool() { return mValue.B; } \ + virtual void CCONV setInt(DCint x) { mValue.i = x; } \ + virtual DCint CCONV getInt() { return mValue.i; } \ + virtual void CCONV setLong(DClong x) { mValue.j = x; } \ + virtual DClong CCONV getLong() { return mValue.j; } \ + virtual void CCONV setLongLong(DClonglong x) { mValue.l = x; } \ + virtual DClonglong CCONV getLongLong() { return mValue.l; } \ + virtual void CCONV setFloat(DCfloat x) { mValue.f = x; } \ + virtual DCfloat CCONV getFloat() { return mValue.f; } \ + virtual void CCONV setDouble(DCdouble x) { mValue.d = x; } \ + virtual DCdouble CCONV getDouble() { return mValue.d; } \ + virtual void CCONV setPtr(DCpointer x) { mValue.p = x; } \ + virtual DCpointer CCONV getPtr() { return mValue.p; } \ + \ + /* ellipsis test w/ this ptr */ \ + virtual int CCONV sum3Ints(DCint x, ...) { va_list va; va_start(va,x); x += va_arg(va,int); x += va_arg(va,int); va_end(va); return x; } \ + \ +private: \ + ValueUnion mValue; \ +}; - virtual void __cdecl setBool(DCbool x) { mValue.B = x; } - virtual DCbool __cdecl getBool() { return mValue.B; } - virtual void __cdecl setInt(DCint x) { mValue.i = x; } - virtual DCint __cdecl getInt() { return mValue.i; } - virtual void __cdecl setLong(DClong x) { mValue.j = x; } - virtual DClong __cdecl getLong() { return mValue.j; } - virtual void __cdecl setLongLong(DClonglong x) { mValue.l = x; } - virtual DClonglong __cdecl getLongLong() { return mValue.l; } - virtual void __cdecl setFloat(DCfloat x) { mValue.f = x; } - virtual DCfloat __cdecl getFloat() { return mValue.f; } - virtual void __cdecl setDouble(DCdouble x) { mValue.d = x; } - virtual DCdouble __cdecl getDouble() { return mValue.d; } - virtual void __cdecl setPtr(DCpointer x) { mValue.p = x; } - virtual DCpointer __cdecl getPtr() { return mValue.p; } +TEST_CLASS(ValueThisDef, /*empty/default*/) /* default */ +#if defined(DC__Arch_Intel_x86) +TEST_CLASS(ValueThisCdecl, __cdecl) /* methods explicitly declared as cdecl */ +#if defined(DC__OS_Win32) && defined(DC__C_MSVC) +TEST_CLASS(ValueThisMS, /*empty/default*/) /* microsoft this call */ +#endif +#endif - /* ellipsis test w/ this ptr */ - virtual int __cdecl sum3Ints(DCint x, ...) { va_list va; va_start(va,x); x += va_arg(va,int); x += va_arg(va,int); va_end(va); return x; } -private: - ValueUnion mValue; -}; + template<typename T> bool testCallValue(DCCallVM* pc, const char* name) @@ -268,60 +257,17 @@ } -#if defined(DC__OS_Win32) && defined(DC__C_MSVC) - -/* C++ class using (on win32: microsoft) this call */ - -class ValueMS -{ -public: - virtual ~ValueMS() {} - - virtual void setBool(DCbool x) { mValue.B = x; } - virtual DCbool getBool() { return mValue.B; } - virtual void setInt(DCint x) { mValue.i = x; } - virtual DCint getInt() { return mValue.i; } - virtual void setLong(DClong x) { mValue.j = x; } - virtual DClong getLong() { return mValue.j; } - virtual void setLongLong(DClonglong x) { mValue.l = x; } - virtual DClonglong getLongLong() { return mValue.l; } - virtual void setFloat(DCfloat x) { mValue.f = x; } - virtual DCfloat getFloat() { return mValue.f; } - virtual void setDouble(DCdouble x) { mValue.d = x; } - virtual DCdouble getDouble() { return mValue.d; } - virtual void setPtr(DCpointer x) { mValue.p = x; } - virtual DCpointer getPtr() { return mValue.p; } -private: - ValueUnion mValue; -}; - -static bool testCallThisMS() +template<class T> +static bool testCallThis(DCint mode, const char* str) { bool r = false; DCCallVM* pc = dcNewCallVM(4096); - dcMode(pc, DC_CALL_C_X86_WIN32_THIS_MS); + dcMode(pc, mode); dcReset(pc); if(setjmp(jbuf) != 0) printf("sigsegv\n"), r=false; else - r = testCallValue<ValueMS>(pc, "MS"); - dcFree(pc); - return r; -} - -#endif - - -static bool testCallThisC() -{ - bool r = false; - DCCallVM* pc = dcNewCallVM(4096); - dcMode(pc, DC_CALL_C_DEFAULT_THIS); - dcReset(pc); - if(setjmp(jbuf) != 0) - printf("sigsegv\n"), r=false; - else - r = testCallValue<Value>(pc, "c"); + r = testCallValue<T>(pc, str); dcFree(pc); return r; } @@ -329,65 +275,76 @@ #if defined(DC__Feature_AggrByVal) -class ValueAggr -{ -public: - struct S { int i, j, k, l, m; }; - - virtual ~ValueAggr() {} - - virtual void __cdecl setAggr(S x) { mS.i = x.i; mS.j = x.j; mS.k = x.k; mS.l = x.l; mS.m = x.m; } - virtual S __cdecl getAggr() { return mS; } - - /* ellipsis test w/ this ptr and big (!) aggregate return */ - struct Big { int sum; long long dummy[50]; /*dummy to make it not fit in any regs*/ }; - virtual struct Big __cdecl sum3RetAggr(DCint x, ...) { va_list va; va_start(va,x); struct Big r = { x + va_arg(va,int) + va_arg(va,int) }; va_end(va); return r; } +#define TEST_CLASS_AGGR(NAME, CCONV) \ +class NAME \ +{ \ +public: \ + struct S { int i, j, k, l, m; }; \ + \ + virtual ~NAME() { } \ + \ + virtual void CCONV setAggr(S x) { mS.i = x.i; mS.j = x.j; mS.k = x.k; mS.l = x.l; mS.m = x.m; } \ + virtual S CCONV getAggr() { return mS; } \ + \ + /* ellipsis test w/ this ptr and big (!) aggregate return */ \ + struct Big { int sum; long long dummy[50]; /*dummy to make it not fit in any regs*/ }; \ + virtual struct Big CCONV sum3RetAggr(DCint x, ...) { va_list va; va_start(va,x); struct Big r = { x + va_arg(va,int) + va_arg(va,int) }; va_end(va); return r; } \ + \ + /* non-trivial aggregate */ \ + struct NonTriv { \ + int i, j; \ + NonTriv(int a, int b) : i(a),j(b) { } \ + NonTriv(const NonTriv& rhs) { static int a=13, b=37; i = a++; j = b++; } \ + }; \ + /* by value, so on first invocation a = 13,37, b = 14,38 and retval = 13*14,37*38, no matter the contents of the instances as copy ctor is called */ \ + /* NOTE: copy of return value is subject to C++ "copy elision", so it is *not* calling the copy ctor for the return value */ \ + virtual struct NonTriv CCONV squareFields(NonTriv a, NonTriv b) { return NonTriv(a.i*b.i, a.j*b.j); } \ + \ +private: \ + struct S mS; \ +}; - /* non-trivial aggregate */ - struct NonTriv { - int i, j; - NonTriv(int a, int b) : i(a),j(b) { } - NonTriv(const NonTriv& rhs) { static int a=13, b=37; i = a++; j = b++; } - }; - /* by value, so on first invocation a = 13,37, b = 14,38 and retval = 13*14,37*38, no matter the contents of the instances as copy ctor is called */ - /* NOTE: copy of return value is subject to C++ "copy elision", so it is *not* calling the copy ctor for the return value */ - virtual struct NonTriv __cdecl squareFields(NonTriv a, NonTriv b) { return NonTriv(a.i*b.i, a.j*b.j); } +TEST_CLASS_AGGR(ValueAggrThisDef, /*empty/default*/) /* default */ +#if defined(DC__Arch_Intel_x86) +TEST_CLASS_AGGR(ValueAggrThisCdecl, __cdecl) /* methods explicitly declared as cdecl */ +#if defined(DC__OS_Win32) && defined(DC__C_MSVC) +TEST_CLASS_AGGR(ValueAggrThisMS, /*empty/default*/) /* microsoft this call */ +#endif +#endif -private: - struct S mS; -}; #if (__cplusplus >= 201103L) # include <type_traits> #endif /* special case w/ e.g. MS x64 C++ calling cconf: struct return ptr is passed as *2nd* arg */ -static bool testCallThisAggr() +template<class T> +static bool testCallThisAggr(DCint mode, const char* str) { bool r = false; DCCallVM* pc = dcNewCallVM(4096); - dcMode(pc, DC_CALL_C_DEFAULT_THIS); + dcMode(pc, mode); if(setjmp(jbuf) != 0) printf("sigsegv\n"), r=false; else { - ValueAggr o; + T o; DCpointer* vtbl = *( (DCpointer**) &o ); /* vtbl is located at beginning of class */ - ValueAggr::S st = { 124, -12, 434, 20202, -99999 }, returned; + typename T::S st = { 124, -12, 434, 20202, -99999 }, returned; #if (__cplusplus >= 201103L) - bool istriv = std::is_trivial<ValueAggr::S>::value; + bool istriv = std::is_trivial<typename T::S>::value; #else bool istriv = true; /* own deduction as no type trait */ #endif - DCaggr *s = dcNewAggr(5, sizeof(ValueAggr::S)); - dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, i), 1); - dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, j), 1); - dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, k), 1); - dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, l), 1); - dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, m), 1); + DCaggr *s = dcNewAggr(5, sizeof(typename T::S)); + dcAggrField(s, DC_SIGCHAR_INT, offsetof(typename T::S, i), 1); + dcAggrField(s, DC_SIGCHAR_INT, offsetof(typename T::S, j), 1); + dcAggrField(s, DC_SIGCHAR_INT, offsetof(typename T::S, k), 1); + dcAggrField(s, DC_SIGCHAR_INT, offsetof(typename T::S, l), 1); + dcAggrField(s, DC_SIGCHAR_INT, offsetof(typename T::S, m), 1); dcCloseAggr(s); // set S::mS @@ -405,7 +362,7 @@ dcFreeAggr(s); r = returned.i == st.i && returned.j == st.j && returned.k == st.k && returned.l == st.l && returned.m == st.m && istriv; - printf("r:{iiiii} (this/trivial): %d\n", r); + printf("r:{iiiii} (%s/trivial): %d\n", str, r); @@ -416,16 +373,16 @@ * DC_CALL_C_ELLIPSIS_VARARGS (test is useful on win64 where thisptr is * passed *after* return aggregate's hidden ptr) */ #if (__cplusplus >= 201103L) - istriv = std::is_trivial<ValueAggr::Big>::value; + istriv = std::is_trivial<typename T::Big>::value; #else istriv = true; /* own deduction as no type trait */ #endif - s = dcNewAggr(2, sizeof(struct ValueAggr::Big)); - dcAggrField(s, DC_SIGCHAR_INT, offsetof(struct ValueAggr::Big, sum), 1); - dcAggrField(s, DC_SIGCHAR_LONGLONG, offsetof(struct ValueAggr::Big, dummy), 50); + s = dcNewAggr(2, sizeof(struct T::Big)); + dcAggrField(s, DC_SIGCHAR_INT, offsetof(struct T::Big, sum), 1); + dcAggrField(s, DC_SIGCHAR_LONGLONG, offsetof(struct T::Big, dummy), 50); dcCloseAggr(s); dcReset(pc); - dcMode(pc, DC_CALL_C_DEFAULT_THIS); /* <-- needed on x64/win64 */ + dcMode(pc, mode); dcBeginCallAggr(pc, s); dcArgPointer(pc, &o); @@ -434,34 +391,34 @@ dcMode(pc, DC_CALL_C_ELLIPSIS_VARARGS); dcArgInt(pc, -157); dcArgInt(pc, 888); - struct ValueAggr::Big big; + struct T::Big big; dcCallAggr(pc, vtbl[VTBI_BASE+2], s, &big); dcFreeAggr(s); bool b = (big.sum == 820) && istriv; r = r && b; - printf("r:{il[50]} (this/trivial/ellipsis): %d\n", b); + printf("r:{il[50]} (%s/trivial/ellipsis): %d\n", str, b); /* non-trivial test ----------------------------------------------------------- */ #if (__cplusplus >= 201103L) - istriv = std::is_trivial<ValueAggr::NonTriv>::value; + istriv = std::is_trivial<typename T::NonTriv>::value; #else istriv = false; /* own deduction as no type trait */ #endif dcReset(pc); - dcMode(pc, DC_CALL_C_DEFAULT_THIS); + dcMode(pc, mode); /* non trivial aggregates: pass NULL for DCaggr* and do copy on our own (see doc) */ dcBeginCallAggr(pc, NULL); - ValueAggr::NonTriv nt0(5, 6), nt1(7, 8), ntr(0, 0); + typename T::NonTriv nt0(5, 6), nt1(7, 8), ntr(0, 0); dcArgAggr(pc, NULL, &o); // this ptr /* make *own* copies, as dyncall cannot know how to call copy ctor */ //@@@ put into doc - ValueAggr::NonTriv nt0_ = nt0, nt1_ = nt1; + typename T::NonTriv nt0_ = nt0, nt1_ = nt1; dcArgAggr(pc, NULL, &nt0_); /* use *own* copy */ dcArgAggr(pc, NULL, &nt1_); /* use *own* copy */ @@ -470,7 +427,7 @@ b = ntr.i == 13*14 && ntr.j == 37*38 && !istriv; r = r && b; - printf("r:{ii} (this/nontrivial/retval_copy_elision): %d\n", b); + printf("r:{ii} (%s/nontrivial/retval_copy_elision): %d\n", str, b); } dcFree(pc); @@ -490,12 +447,22 @@ bool r = true; - r = testCallThisC() && r; + r = testCallThis<ValueThisDef>(DC_CALL_C_DEFAULT_THIS, "thisDef") && r; +#if defined(DC__Arch_Intel_x86) + r = testCallThis<ValueThisCdecl>(DC_CALL_C_X86_CDECL, "thisCdecl") && r; #if defined(DC__OS_Win32) && defined(DC__C_MSVC) - r = testCallThisMS() && r; + r = testCallThis<ValueThisMS>(DC_CALL_C_X86_WIN32_THIS_MS, "thisMS") && r; +#endif #endif + #if defined(DC__Feature_AggrByVal) - r = testCallThisAggr() && r; + r = testCallThisAggr<ValueAggrThisDef>(DC_CALL_C_DEFAULT_THIS, "thisDef") && r; +#if defined(DC__Arch_Intel_x86) + r = testCallThisAggr<ValueAggrThisCdecl>(DC_CALL_C_X86_CDECL, "thisCdecl") && r; +#if defined(DC__OS_Win32) && defined(DC__C_MSVC) + r = testCallThisAggr<ValueAggrThisMS>(DC_CALL_C_X86_WIN32_THIS_MS, "thisMS") && r; +#endif +#endif #endif printf("result: plain_c++: %d\n", r);