comparison test/plain_c++/test_main.cc @ 533:71c884e610f0

- integration of patches from Raphael Luba, Thekla, Inc.: * integration of aggregate-by-value (struct, union) support patch for x64 (win and sysv) * windows/x64 asm additions to specify how stack unwinds (help for debuggers, exception handling, etc.) * see Changelog for details - new calling convention modes for thiscalls (platform agnostic, was specific before) * new signature character for platform agnostic thiscalls ('*' / DC_SIGCHAR_CC_THISCALL) - dcCallF(), dcVCallF(), dcArgF() and dcVArgF(): * added support for aggregates-by-value (wasn't part of patch) * change that those functions don't implicitly call dcReset() anymore, which was unflexible (breaking change) - added macros to feature test implementation for aggregate-by-value and syscall support - changed libdyncall_s.lib and libdyncallback_s.lib order in callback test makefiles, as some toolchains are picky about order - doc: * man page updates to describe aggregate interface * manual overview changes to highlight platforms with aggregate-by-value support - test/plain: replaced tests w/ old/stale sctruct interface with new aggregate one
author Tassilo Philipp
date Thu, 21 Apr 2022 13:35:47 +0200
parents ddfb9577a00e
children 0c3f5355769d
comparison
equal deleted inserted replaced
532:d4bf63ab9164 533:71c884e610f0
4 Library: test 4 Library: test
5 File: test/plain_c++/test_main.cc 5 File: test/plain_c++/test_main.cc
6 Description: 6 Description:
7 License: 7 License:
8 8
9 Copyright (c) 2007-2019 Daniel Adler <dadler@uni-goettingen.de>, 9 Copyright (c) 2007-2022 Daniel Adler <dadler@uni-goettingen.de>,
10 Tassilo Philipp <tphilipp@potion-studios.com> 10 Tassilo Philipp <tphilipp@potion-studios.com>
11 11
12 Permission to use, copy, modify, and distribute this software for any 12 Permission to use, copy, modify, and distribute this software for any
13 purpose with or without fee is hereby granted, provided that the above 13 purpose with or without fee is hereby granted, provided that the above
14 copyright notice and this permission notice appear in all copies. 14 copyright notice and this permission notice appear in all copies.
28 28
29 #include "../../dyncall/dyncall.h" 29 #include "../../dyncall/dyncall.h"
30 #include "../common/platformInit.h" 30 #include "../common/platformInit.h"
31 #include "../common/platformInit.c" /* Impl. for functions only used in this translation unit */ 31 #include "../common/platformInit.c" /* Impl. for functions only used in this translation unit */
32 32
33 #include "../../dyncall/dyncall_aggregate.h"
33 34
34 #include <signal.h> 35 #include <signal.h>
35 #include <setjmp.h> 36 #include <setjmp.h>
37 #include <stdarg.h>
36 38
37 jmp_buf jbuf; 39 jmp_buf jbuf;
38 40
39 41
40 void segv_handler(int sig) 42 void segv_handler(int sig)
108 #define VTBI_GET_FLOAT VTBI_BASE+9 110 #define VTBI_GET_FLOAT VTBI_BASE+9
109 #define VTBI_SET_DOUBLE VTBI_BASE+10 111 #define VTBI_SET_DOUBLE VTBI_BASE+10
110 #define VTBI_GET_DOUBLE VTBI_BASE+11 112 #define VTBI_GET_DOUBLE VTBI_BASE+11
111 #define VTBI_SET_POINTER VTBI_BASE+12 113 #define VTBI_SET_POINTER VTBI_BASE+12
112 #define VTBI_GET_POINTER VTBI_BASE+13 114 #define VTBI_GET_POINTER VTBI_BASE+13
115 #define VTBI_SUM_3_INTS VTBI_BASE+14
113 116
114 class Value 117 class Value
115 { 118 {
116 public: 119 public:
117 virtual ~Value() {} 120 virtual ~Value() {}
128 virtual DCfloat __cdecl getFloat() { return mValue.f; } 131 virtual DCfloat __cdecl getFloat() { return mValue.f; }
129 virtual void __cdecl setDouble(DCdouble x) { mValue.d = x; } 132 virtual void __cdecl setDouble(DCdouble x) { mValue.d = x; }
130 virtual DCdouble __cdecl getDouble() { return mValue.d; } 133 virtual DCdouble __cdecl getDouble() { return mValue.d; }
131 virtual void __cdecl setPtr(DCpointer x) { mValue.p = x; } 134 virtual void __cdecl setPtr(DCpointer x) { mValue.p = x; }
132 virtual DCpointer __cdecl getPtr() { return mValue.p; } 135 virtual DCpointer __cdecl getPtr() { return mValue.p; }
136
137 /* ellipsis test w/ this ptr */
138 virtual int __cdecl sum3Ints(DCint x, ...) { va_list va; va_start(va,x); x += va_arg(va,int) + va_arg(va,int); va_end(va); return x; }
139
133 private: 140 private:
134 ValueUnion mValue; 141 ValueUnion mValue;
135 }; 142 };
143
144 template<typename T>
145 bool testCallValue(DCCallVM* pc, const char* name)
146 {
147 bool r = true, b;
148 T o;
149 T* pThis = &o;
150 DCpointer* vtbl = *( (DCpointer**) pThis ); /* vtbl is located at beginning of class */
151
152 /* set/get bool (TRUE) */
153
154 dcReset(pc);
155 dcArgPointer(pc, pThis);
156 dcArgBool(pc,DC_TRUE);
157 dcCallVoid(pc, vtbl[VTBI_SET_BOOL] );
158 dcReset(pc);
159 dcArgPointer(pc, pThis);
160 b = ( dcCallBool(pc, vtbl[VTBI_GET_BOOL] ) == DC_TRUE );
161 printf("bt (%s): %d\n", name, b);
162 r = r && b;
163
164 /* set/get bool (FALSE) */
165
166 dcReset(pc);
167 dcArgPointer(pc, pThis);
168 dcArgBool(pc,DC_FALSE);
169 dcCallVoid(pc, vtbl[VTBI_SET_BOOL] );
170 dcReset(pc);
171 dcArgPointer(pc, pThis);
172 b = ( dcCallBool(pc, vtbl[VTBI_GET_BOOL] ) == DC_FALSE );
173 printf("bf (%s): %d\n", name, b);
174 r = r && b;
175
176 /* set/get int */
177
178 dcReset(pc);
179 dcArgPointer(pc, pThis);
180 dcArgInt(pc,1234);
181 dcCallVoid(pc, vtbl[VTBI_SET_INT] );
182 dcReset(pc);
183 dcArgPointer(pc, pThis);
184 b = ( dcCallInt(pc, vtbl[VTBI_GET_INT] ) == 1234 );
185 printf("i (%s): %d\n", name, b);
186 r = r && b;
187
188 /* set/get long */
189
190 dcReset(pc);
191 dcArgPointer(pc, pThis);
192 dcArgLong(pc,0xCAFEBABEUL);
193 dcCallVoid(pc, vtbl[VTBI_SET_LONG] );
194 dcReset(pc);
195 dcArgPointer(pc, pThis);
196 b = ( dcCallLong(pc, vtbl[VTBI_GET_LONG] ) == (DClong)0xCAFEBABEUL );
197 printf("l (%s): %d\n", name, b);
198 r = r && b;
199
200 /* set/get long long */
201
202 dcReset(pc);
203 dcArgPointer(pc, pThis);
204 dcArgLongLong(pc,0xCAFEBABEDEADC0DELL);
205 dcCallVoid(pc, vtbl[VTBI_SET_LONG_LONG] );
206 dcReset(pc);
207 dcArgPointer(pc, pThis);
208 b = ( dcCallLongLong(pc, vtbl[VTBI_GET_LONG_LONG] ) == (DClonglong)0xCAFEBABEDEADC0DELL );
209 printf("ll (%s): %d\n", name, b);
210 r = r && b;
211
212 /* set/get float */
213
214 dcReset(pc);
215 dcArgPointer(pc, pThis);
216 dcArgFloat(pc,1.2345f);
217 dcCallVoid(pc, vtbl[VTBI_SET_FLOAT] );
218 dcReset(pc);
219 dcArgPointer(pc, pThis);
220 b = ( dcCallFloat(pc, vtbl[VTBI_GET_FLOAT] ) == 1.2345f );
221 printf("f (%s): %d\n", name, b);
222 r = r && b;
223
224 /* set/get double */
225
226 dcReset(pc);
227 dcArgPointer(pc, pThis);
228 dcArgDouble(pc,1.23456789);
229 dcCallVoid(pc, vtbl[VTBI_SET_DOUBLE] );
230 dcReset(pc);
231 dcArgPointer(pc, pThis);
232 b = ( dcCallDouble(pc, vtbl[VTBI_GET_DOUBLE] ) == 1.23456789 );
233 printf("d (%s): %d\n", name, b);
234 r = r && b;
235
236 /* set/get pointer */
237
238 dcReset(pc);
239 dcArgPointer(pc, pThis);
240 dcArgPointer(pc, (DCpointer) 0xCAFEBABE );
241 dcCallVoid(pc, vtbl[VTBI_SET_POINTER] );
242 dcReset(pc);
243 dcArgPointer(pc, pThis);
244 b = ( dcCallPointer(pc, vtbl[VTBI_GET_POINTER] ) == ( (DCpointer) 0xCAFEBABE ) );
245 printf("p (%s): %d\n", name, b);
246 r = r && b;
247
248 /* ellipsis test w/ this pointer */
249
250 dcReset(pc);
251 dcMode(pc, DC_CALL_C_ELLIPSIS);
252 dcArgPointer(pc, pThis);
253 dcArgInt(pc, 23);
254 dcMode(pc, DC_CALL_C_ELLIPSIS_VARARGS);
255 dcArgInt(pc, -223);
256 dcArgInt(pc, 888);
257 int r_ = dcCallInt(pc, vtbl[VTBI_SUM_3_INTS]);
258 b = (r_ == 688);
259 printf("... (%s): %d\n", name, b);
260 r = r && b;
261
262 return r;
263 }
264
265
266 #if defined(DC__OS_Win32) && defined(DC__C_MSVC)
136 267
137 /* C++ class using (on win32: microsoft) this call */ 268 /* C++ class using (on win32: microsoft) this call */
138 269
139 class ValueMS 270 class ValueMS
140 { 271 {
157 virtual DCpointer getPtr() { return mValue.p; } 288 virtual DCpointer getPtr() { return mValue.p; }
158 private: 289 private:
159 ValueUnion mValue; 290 ValueUnion mValue;
160 }; 291 };
161 292
162 template<typename T> 293 static bool testCallThisMS()
163 bool testCallValue(DCCallVM* pc, const char* name)
164 {
165 bool r = true, b;
166 T o;
167 T* pThis = &o;
168 DCpointer* vtbl = *( (DCpointer**) pThis ); /* vtbl is located at beginning of class */
169
170 /* set/get bool (TRUE) */
171
172 dcReset(pc);
173 dcArgPointer(pc, pThis);
174 dcArgBool(pc,DC_TRUE);
175 dcCallVoid(pc, vtbl[VTBI_SET_BOOL] );
176 dcReset(pc);
177 dcArgPointer(pc, pThis);
178 b = ( dcCallBool(pc, vtbl[VTBI_GET_BOOL] ) == DC_TRUE );
179 printf("bt (%s): %d\n", name, b);
180 r = r && b;
181
182 /* set/get bool (FALSE) */
183
184 dcReset(pc);
185 dcArgPointer(pc, pThis);
186 dcArgBool(pc,DC_FALSE);
187 dcCallVoid(pc, vtbl[VTBI_SET_BOOL] );
188 dcReset(pc);
189 dcArgPointer(pc, pThis);
190 b = ( dcCallBool(pc, vtbl[VTBI_GET_BOOL] ) == DC_FALSE );
191 printf("bf (%s): %d\n", name, b);
192 r = r && b;
193
194 /* set/get int */
195
196 dcReset(pc);
197 dcArgPointer(pc, pThis);
198 dcArgInt(pc,1234);
199 dcCallVoid(pc, vtbl[VTBI_SET_INT] );
200 dcReset(pc);
201 dcArgPointer(pc, pThis);
202 b = ( dcCallInt(pc, vtbl[VTBI_GET_INT] ) == 1234 );
203 printf("i (%s): %d\n", name, b);
204 r = r && b;
205
206 /* set/get long */
207
208 dcReset(pc);
209 dcArgPointer(pc, pThis);
210 dcArgLong(pc,0xCAFEBABEUL);
211 dcCallVoid(pc, vtbl[VTBI_SET_LONG] );
212 dcReset(pc);
213 dcArgPointer(pc, pThis);
214 b = ( dcCallLong(pc, vtbl[VTBI_GET_LONG] ) == (DClong)0xCAFEBABEUL );
215 printf("l (%s): %d\n", name, b);
216 r = r && b;
217
218 /* set/get long long */
219
220 dcReset(pc);
221 dcArgPointer(pc, pThis);
222 dcArgLongLong(pc,0xCAFEBABEDEADC0DELL);
223 dcCallVoid(pc, vtbl[VTBI_SET_LONG_LONG] );
224 dcReset(pc);
225 dcArgPointer(pc, pThis);
226 b = ( dcCallLongLong(pc, vtbl[VTBI_GET_LONG_LONG] ) == (DClonglong)0xCAFEBABEDEADC0DELL );
227 printf("ll (%s): %d\n", name, b);
228 r = r && b;
229
230 /* set/get float */
231
232 dcReset(pc);
233 dcArgPointer(pc, pThis);
234 dcArgFloat(pc,1.2345f);
235 dcCallVoid(pc, vtbl[VTBI_SET_FLOAT] );
236 dcReset(pc);
237 dcArgPointer(pc, pThis);
238 b = ( dcCallFloat(pc, vtbl[VTBI_GET_FLOAT] ) == 1.2345f );
239 printf("f (%s): %d\n", name, b);
240 r = r && b;
241
242 /* set/get double */
243
244 dcReset(pc);
245 dcArgPointer(pc, pThis);
246 dcArgDouble(pc,1.23456789);
247 dcCallVoid(pc, vtbl[VTBI_SET_DOUBLE] );
248 dcReset(pc);
249 dcArgPointer(pc, pThis);
250 b = ( dcCallDouble(pc, vtbl[VTBI_GET_DOUBLE] ) == 1.23456789 );
251 printf("d (%s): %d\n", name, b);
252 r = r && b;
253
254 /* set/get pointer */
255
256 dcReset(pc);
257 dcArgPointer(pc, pThis);
258 dcArgPointer(pc, (DCpointer) 0xCAFEBABE );
259 dcCallVoid(pc, vtbl[VTBI_SET_POINTER] );
260 dcReset(pc);
261 dcArgPointer(pc, pThis);
262 b = ( dcCallPointer(pc, vtbl[VTBI_GET_POINTER] ) == ( (DCpointer) 0xCAFEBABE ) );
263 printf("p (%s): %d\n", name, b);
264 r = r && b;
265
266 return r;
267 }
268
269
270 #if defined(DC__OS_Win32)
271
272 int testCallThisMS()
273 { 294 {
274 bool r = false; 295 bool r = false;
275 DCCallVM* pc = dcNewCallVM(4096); 296 DCCallVM* pc = dcNewCallVM(4096);
276 dcMode(pc, DC_CALL_C_X86_WIN32_THIS_MS); 297 dcMode(pc, DC_CALL_C_X86_WIN32_THIS_MS);
277 dcReset(pc); 298 dcReset(pc);
278 if(setjmp(jbuf) != 0) 299 if(setjmp(jbuf) != 0)
279 printf("sigsegv\n"); 300 printf("sigsegv\n"), r=false;
280 else 301 else
281 r = testCallValue<ValueMS>(pc, "MS"); 302 r = testCallValue<ValueMS>(pc, "MS");
282 dcFree(pc); 303 dcFree(pc);
283 return r; 304 return r;
284 } 305 }
285 306
286 #endif 307 #endif
287 308
288 309
289 int testCallThisC() 310 static bool testCallThisC()
290 { 311 {
291 bool r = false; 312 bool r = false;
292 DCCallVM* pc = dcNewCallVM(4096); 313 DCCallVM* pc = dcNewCallVM(4096);
293 dcMode(pc, DC_CALL_C_DEFAULT_THIS); 314 dcMode(pc, DC_CALL_C_DEFAULT_THIS);
294 dcReset(pc); 315 dcReset(pc);
295 if(setjmp(jbuf) != 0) 316 if(setjmp(jbuf) != 0)
296 printf("sigsegv\n"); 317 printf("sigsegv\n"), r=false;
297 else 318 else
298 r = testCallValue<Value>(pc, "c"); 319 r = testCallValue<Value>(pc, "c");
299 dcFree(pc); 320 dcFree(pc);
300 return r; 321 return r;
301 } 322 }
302 323
303 324
325 #if defined(DC__Feature_AggrByVal)
326
327 class ValueAggr
328 {
329 public:
330 struct S { int i, j, k, l, m; };
331
332 virtual ~ValueAggr() {}
333
334 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; }
335 virtual S __cdecl getAggr() { return mS; }
336
337 /* ellipsis test w/ this ptr and big (!) aggregate return */
338 struct Big { int sum; long long dummy[50]; /*dummy to make it not fit in any regs*/ };
339 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; }
340
341 /* non-trivial aggregate */
342 struct NonTriv {
343 int i, j;
344 NonTriv(int a, int b) : i(a),j(b) { }
345 NonTriv(const NonTriv& rhs) { static int a=13, b=37; i = a++; j = b++; }
346 };
347 /* 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 */
348 /* NOTE: copy of return value is subject to C++ "copy elision", so it is *not* calling the copy ctor for the return value */
349 virtual struct NonTriv __cdecl squareFields(NonTriv a, NonTriv b) { return NonTriv(a.i*b.i, a.j*b.j); }
350
351 private:
352 struct S mS;
353 };
354
355 #if (__cplusplus >= 201103L)
356 # include <type_traits>
357 #endif
358
359 /* special case w/ e.g. MS x64 C++ calling cconf: struct return ptr is passed as *2nd* arg */
360 static bool testCallThisAggr()
361 {
362 bool r = false;
363 DCCallVM* pc = dcNewCallVM(4096);
364 dcMode(pc, DC_CALL_C_DEFAULT_THIS);
365
366 if(setjmp(jbuf) != 0)
367 printf("sigsegv\n"), r=false;
368 else
369 {
370 ValueAggr o;
371
372 DCpointer* vtbl = *( (DCpointer**) &o ); /* vtbl is located at beginning of class */
373 ValueAggr::S st = { 124, -12, 434, 20202, -99999 }, returned;
374
375 #if (__cplusplus >= 201103L)
376 bool istriv = std::is_trivial<ValueAggr::S>::value;
377 #else
378 bool istriv = true; /* own deduction as no type trait */
379 #endif
380 DCaggr *s = dcNewAggr(5, sizeof(ValueAggr::S));
381 dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, i), 1);
382 dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, j), 1);
383 dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, k), 1);
384 dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, l), 1);
385 dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, m), 1);
386 dcCloseAggr(s);
387
388 // set S::mS
389 dcReset(pc);
390 dcArgPointer(pc, &o); // this ptr
391 dcArgAggr(pc, s, &st);
392 dcCallVoid(pc, vtbl[VTBI_BASE+0]);
393
394 // get it back
395 dcReset(pc);
396 dcBeginCallAggr(pc, s);
397 dcArgPointer(pc, &o); // this ptr
398 dcCallAggr(pc, vtbl[VTBI_BASE+1], s, &returned);
399
400 dcFreeAggr(s);
401
402 r = returned.i == st.i && returned.j == st.j && returned.k == st.k && returned.l == st.l && returned.m == st.m && istriv;
403 printf("r:{iiiii} (this/trivial): %d\n", r);
404
405
406
407 /* ellipsis test w/ this pointer returning big aggregate (quite an edge
408 * case) by value (won't fit in regs, so hidden pointer is is used to write
409 * return values to), showing the need to use the DC_CALL_C_DEFAULT_THIS
410 * mode first, for the this ptr alone, then DC_CALL_C_ELLIPSIS, then
411 * DC_CALL_C_ELLIPSIS_VARARGS (test is useful on win64 where thisptr is
412 * passed *after* return aggregate's hidden ptr) */
413 #if (__cplusplus >= 201103L)
414 istriv = std::is_trivial<ValueAggr::Big>::value;
415 #else
416 istriv = true; /* own deduction as no type trait */
417 #endif
418 s = dcNewAggr(2, sizeof(struct ValueAggr::Big));
419 dcAggrField(s, DC_SIGCHAR_INT, offsetof(struct ValueAggr::Big, sum), 1);
420 dcAggrField(s, DC_SIGCHAR_LONGLONG, offsetof(struct ValueAggr::Big, dummy), 50);
421 dcCloseAggr(s);
422 dcReset(pc);
423 dcMode(pc, DC_CALL_C_DEFAULT_THIS); /* <-- needed on x64/win64 */
424
425 dcBeginCallAggr(pc, s);
426 dcMode(pc, DC_CALL_C_ELLIPSIS);
427 dcArgPointer(pc, &o);
428 dcArgInt(pc, 89);
429 dcMode(pc, DC_CALL_C_ELLIPSIS_VARARGS);
430 dcArgInt(pc, -157);
431 dcArgInt(pc, 888);
432 struct ValueAggr::Big big;
433 dcCallAggr(pc, vtbl[VTBI_BASE+2], s, &big);
434
435 dcFreeAggr(s);
436
437 bool b = (big.sum == 820) && istriv;
438 r = r && b;
439 printf("r:{il[50]} (this/trivial/ellipsis): %d\n", b);
440
441
442
443 /* non-trivial test ----------------------------------------------------------- */
444
445 #if (__cplusplus >= 201103L)
446 istriv = std::is_trivial<ValueAggr::NonTriv>::value;
447 #else
448 istriv = false; /* own deduction as no type trait */
449 #endif
450 dcReset(pc);
451 dcMode(pc, DC_CALL_C_DEFAULT_THIS);
452
453 /* non trivial aggregates: pass NULL for DCaggr* and do copy on our own (see doc) */
454 dcBeginCallAggr(pc, NULL);
455
456 ValueAggr::NonTriv nt0(5, 6), nt1(7, 8), ntr(0, 0);
457 dcArgAggr(pc, NULL, &o); // this ptr
458 /* make *own* copies, as dyncall cannot know how to call copy ctor */ //@@@ put into doc
459 ValueAggr::NonTriv nt0_ = nt0, nt1_ = nt1;
460 dcArgAggr(pc, NULL, &nt0_); /* use *own* copy */
461 dcArgAggr(pc, NULL, &nt1_); /* use *own* copy */
462
463 dcCallAggr(pc, vtbl[VTBI_BASE+3], NULL, &ntr); /* note: "copy elision", so retval might *not* call copy ctor */
464
465
466 b = ntr.i == 13*14 && ntr.j == 37*38 && !istriv;
467 r = r && b;
468 printf("r:{ii} (this/nontrivial/retval_copy_elision): %d\n", b);
469 }
470
471 dcFree(pc);
472 return r;
473 }
474
475 #endif
476
477
304 extern "C" { 478 extern "C" {
305 479
306 int main(int argc, char* argv[]) 480 int main(int argc, char* argv[])
307 { 481 {
308 dcTest_initPlatform(); 482 dcTest_initPlatform();
310 signal(SIGSEGV, segv_handler); 484 signal(SIGSEGV, segv_handler);
311 485
312 bool r = true; 486 bool r = true;
313 487
314 r = testCallThisC() && r; 488 r = testCallThisC() && r;
315 #if defined(DC__OS_Win32) 489 #if defined(DC__OS_Win32) && defined(DC__C_MSVC)
316 r = testCallThisMS() && r; 490 r = testCallThisMS() && r;
491 #endif
492 #if defined(DC__Feature_AggrByVal)
493 r = testCallThisAggr() && r;
317 #endif 494 #endif
318 495
319 printf("result: plain_cpp: %d\n", r); 496 printf("result: plain_cpp: %d\n", r);
320 497
321 dcTest_deInitPlatform(); 498 dcTest_deInitPlatform();