diff dyncall/dyncall_callf.c @ 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 78dfa2f9783a
children
line wrap: on
line diff
--- a/dyncall/dyncall_callf.c	Sat Apr 16 15:00:58 2022 +0200
+++ b/dyncall/dyncall_callf.c	Thu Apr 21 13:35:47 2022 +0200
@@ -6,7 +6,7 @@
  Description: formatted call C interface (extension module)
  License:
 
-   Copyright (c) 2007-2018 Daniel Adler <dadler@uni-goettingen.de>, 
+   Copyright (c) 2007-2022 Daniel Adler <dadler@uni-goettingen.de>, 
                            Tassilo Philipp <tphilipp@potion-studios.com>
 
    Permission to use, copy, modify, and distribute this software for any
@@ -28,13 +28,24 @@
 #include "dyncall_callf.h"
 
 
+static void handle_mode(DCCallVM* vm, const DCsigchar** sigptr)
+{
+  if(*((*sigptr)+1) != '\0') {
+    DCint mode = dcGetModeFromCCSigChar(*(*sigptr)++);
+    if(mode != DC_ERROR_UNSUPPORTED_MODE)
+      dcMode(vm, mode);
+  }
+}
+
+
 /* Shareable implementation for argument binding used in ArgF and CallF below. */
 static void dcArgF_impl(DCCallVM* vm, const DCsigchar** sigptr, va_list args)
 {
   DCsigchar ch;
-  dcReset(vm);
   while((ch=*(*sigptr)++) != '\0' && ch != DC_SIGCHAR_ENDARG) {
     switch(ch) {
+      /* calling convention modes */
+      case DC_SIGCHAR_CC_PREFIX: handle_mode(vm, sigptr); break;
       /* types */
       case DC_SIGCHAR_BOOL:      dcArgBool    (vm, (DCbool)           va_arg(args, DCint     )); break;
       case DC_SIGCHAR_CHAR:      dcArgChar    (vm, (DCchar)           va_arg(args, DCint     )); break;
@@ -51,14 +62,12 @@
       case DC_SIGCHAR_DOUBLE:    dcArgDouble  (vm, (DCdouble)         va_arg(args, DCdouble  )); break;
       case DC_SIGCHAR_POINTER:   dcArgPointer (vm, (DCpointer)        va_arg(args, DCpointer )); break;
       case DC_SIGCHAR_STRING:    dcArgPointer (vm, (DCpointer)        va_arg(args, DCpointer )); break;
-      /* calling convention modes */
-      case DC_SIGCHAR_CC_PREFIX:
-        if(*((*sigptr)+1) != '\0') {
-          DCint mode = dcGetModeFromCCSigChar(*(*sigptr)++);
-          if(mode != DC_ERROR_UNSUPPORTED_MODE)
-            dcMode(vm, mode);
-        }
+      case DC_SIGCHAR_AGGREGATE: {
+        /* aggregates expect 2 va args, a DCaggr*, then a ptr to the aggregate */
+        DCaggr* ag = va_arg(args, DCaggr*);
+        dcArgAggr(vm, ag, va_arg(args, DCpointer));
         break;
+      }
     }
   }
 }
@@ -76,11 +85,66 @@
   va_end(va);
 }
 
+
+/* msvc introduced C99'w va_copy() late (in 2013 w/ msvc 18.00); plan9 APE does
+ * not have it either; luckily given their va_list being only a ptr in both
+ * cases, work around the issue for older versions */
+#if (defined(DC__C_MSVC) || defined(DC__OS_Plan9)) && !defined(va_copy)
+	#define va_copy(dst, src) ((dst)=(src))
+#endif
+
+
 void dcVCallF(DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, va_list args)
 {
+  DCaggr* ret_ag = NULL; /* only needed for when func returns an aggregate      */
   const DCsigchar* ptr = signature;
+
+  /* need preparatory call if return type is an aggregate, so check end of sig */
+  /* @@@ugly */
+  while(*ptr && ptr[1]) ++ptr;
+  if(*ptr == DC_SIGCHAR_AGGREGATE) {
+    va_list args_;
+    va_copy(args_, args);
+
+    /* iterate va_list to get return type related args*/
+    ptr = signature;
+    while(*ptr) {
+      switch(*ptr++) {
+        /* calling convention modes */
+        case DC_SIGCHAR_CC_PREFIX: handle_mode(vm, &ptr); break; /* needs handling before dcBeginCallAggr */
+        /* types */
+        case DC_SIGCHAR_BOOL:
+        case DC_SIGCHAR_CHAR:
+        case DC_SIGCHAR_UCHAR:
+        case DC_SIGCHAR_SHORT:
+        case DC_SIGCHAR_USHORT:
+        case DC_SIGCHAR_INT:
+        case DC_SIGCHAR_UINT:      va_arg(args_, DCint     ); break;
+        case DC_SIGCHAR_LONG:
+        case DC_SIGCHAR_ULONG:     va_arg(args_, DClong    ); break;
+        case DC_SIGCHAR_LONGLONG:
+        case DC_SIGCHAR_ULONGLONG: va_arg(args_, DClonglong); break;
+        case DC_SIGCHAR_FLOAT:
+        case DC_SIGCHAR_DOUBLE:    va_arg(args_, DCdouble  ); break;
+        case DC_SIGCHAR_POINTER:
+        case DC_SIGCHAR_STRING:    va_arg(args_, DCpointer ); break;
+        case DC_SIGCHAR_AGGREGATE:
+          /* aggregate as retval expects 2 more va args, a DCaggr*, then a ptr to the aggregate */
+          ret_ag = va_arg(args_, DCaggr*);
+          result->p = va_arg(args_, DCpointer);
+          break;
+      }
+    }
+    dcBeginCallAggr(vm, ret_ag);
+
+	va_end(args_);
+  }
+
+  /* push args */
+  ptr = signature;
   dcArgF_impl(vm, &ptr, args);
 
+  /* call */
   switch(*ptr) {
     case DC_SIGCHAR_VOID:                   dcCallVoid             (vm,funcptr); break;
     case DC_SIGCHAR_BOOL:       result->B = dcCallBool             (vm,funcptr); break;
@@ -98,6 +162,10 @@
     case DC_SIGCHAR_DOUBLE:     result->d = dcCallDouble           (vm,funcptr); break;
     case DC_SIGCHAR_POINTER:    result->p = dcCallPointer          (vm,funcptr); break;
     case DC_SIGCHAR_STRING:     result->Z = (DCstring)dcCallPointer(vm,funcptr); break;
+    case DC_SIGCHAR_AGGREGATE: {
+      result->p = dcCallAggr(vm, funcptr, ret_ag, result->p);
+      break;
+    }
   }
 }