Index: dl.c
===================================================================
RCS file: /src/ruby/ext/dl/dl.c,v
retrieving revision 1.16
diff -u -p -r1.16 dl.c
--- dl.c	16 Jan 2003 07:38:40 -0000	1.16
+++ dl.c	18 Feb 2003 23:50:52 -0000
@@ -6,15 +6,22 @@
 #include <rubyio.h>
 #include <ctype.h>
 #include "dl.h"
+#ifdef USE_FFCALL
+#include <callback.h>
+#endif
 
 VALUE rb_mDL;
 VALUE rb_eDLError;
 VALUE rb_eDLTypeError;
 
 static VALUE DLFuncTable;
+#ifndef USE_FFCALL
 static void *rb_dl_callback_table[CALLBACK_TYPES][MAX_CALLBACK];
+#endif
 static ID id_call;
 
+#ifndef USE_FFCALL
+
 static int
 rb_dl_scan_callback_args(long stack[], const char *proto,
 			 int *argc, VALUE argv[])
@@ -108,6 +115,8 @@ init_dl_func_table(){
 #include "cbtable.func"
 }
 
+#endif
+
 void *
 dlmalloc(size_t size)
 {
@@ -564,6 +573,164 @@ rb_dl_sizeof(VALUE self, VALUE str)
   return INT2NUM(dlsizeof(StringValuePtr(str)));
 }
 
+#ifdef USE_FFCALL
+
+struct callback_info{
+  VALUE proc;
+  VALUE type;
+  void* func;
+};
+
+static void
+callback_info_mark(struct callback_info *p)
+{
+  rb_gc_mark(p->type);
+  rb_gc_mark(p->proc);
+}
+
+static void
+callback_info_free(struct callback_info *p)
+{
+  free_callback(p->func);
+  free(p);
+}
+
+static void
+rb_dl_callback_func(struct callback_info *info, va_alist ap)
+{
+  const char *proto = RSTRING(info->type)->ptr;
+  VALUE proc = info->proc;
+  VALUE args = rb_ary_new();
+  VALUE val, retval;
+  int i;
+
+  switch( proto[0] ){
+  case '0':
+    va_start_void(ap);
+    break;
+  case 'C':
+    va_start_char(ap);
+    break;
+  case 'H':
+    va_start_short(ap);
+    break;
+  case 'I':
+    va_start_int(ap);
+    break;
+  case 'L':
+    va_start_long(ap);
+    break;
+  case 'F':
+    va_start_float(ap);
+    break;
+  case 'D':
+    va_start_double(ap);
+    break;
+  case 'P':
+    va_start_ptr(ap, void*);
+    break;
+  default:
+  }
+
+  for( i=1; proto[i]; i++ ){
+    switch( proto[i] ){
+    case 'C':
+      {
+	char v;
+	v = va_arg_char(ap);
+	val = INT2NUM(v);
+      }
+      break;
+    case 'H':
+      {
+	short v;
+	v = va_arg_short(ap);
+	val = INT2NUM(v);
+      }
+      break;
+    case 'I':
+      {
+	int v;
+	v = va_arg_int(ap);
+	val = INT2NUM(v);
+      }
+      break;
+    case 'L':
+      {
+	long v;
+	v = va_arg_long(ap);
+	val = INT2NUM(v);
+      }
+      break;
+    case 'F':
+      {
+	float v;
+	v = va_arg_float(ap);
+	val = rb_float_new(v);
+      }
+      break;
+    case 'D':
+      {
+	double v;
+	v = va_arg_double(ap);
+	val = rb_float_new(v);
+      }
+      break;
+    case 'P':
+      {
+	void *v;
+	v = va_arg_ptr(ap, void*);
+	val = rb_dlptr_new(v, 0, 0);
+      }
+      break;
+    case 'S':
+      {
+	char *v;
+	v = va_arg_ptr(ap, char*);
+	val = rb_tainted_str_new2(v);
+      }
+      break;
+    default:
+      rb_raise(rb_eDLTypeError, "unsupported type `%c'", proto[i]);
+      break;
+    }
+    rb_ary_push(args, val);
+  }
+
+  retval = rb_apply(proc, id_call, args);
+
+  switch( proto[0] ){
+  case '0':
+    va_return_void(ap);
+    break;
+  case 'C':
+    va_return_char(ap, NUM2CHR(retval));
+    break;
+  case 'H':
+    va_return_short(ap, FIX2INT(retval));
+    break;
+  case 'I':
+    va_return_int(ap, NUM2INT(retval));
+    break;
+  case 'L':
+    va_return_long(ap, NUM2INT(retval));
+    break;
+  case 'F':
+    va_return_float(ap, (float)(RFLOAT(retval)->value));
+    break;
+  case 'D':
+    va_return_double(ap, RFLOAT(retval)->value);
+    break;
+  case 'P':
+    va_return_ptr(ap, void*, rb_dlptr2cptr(retval));
+    break;
+  default:
+    rb_raise(rb_eDLTypeError, "unsupported type `%c'", RSTRING(info->type)->ptr[0]);
+  }
+}
+
+#endif
+
 static VALUE
 rb_dl_callback(int argc, VALUE argv[], VALUE self)
 {
@@ -614,6 +781,7 @@ rb_dl_callback(int argc, VALUE argv[], V
     rb_raise(rb_eDLTypeError, "unsupported type `%c'", RSTRING(type)->ptr[0]);
   }
 
+#ifndef USE_FFCALL
   entry = -1;
   for( i=0; i < MAX_CALLBACK; i++ ){
     if( rb_hash_aref(DLFuncTable, rb_assoc_new(INT2NUM(rettype), INT2NUM(i))) == Qnil ){
@@ -631,11 +799,31 @@ rb_dl_callback(int argc, VALUE argv[], V
   sprintf(fname, "rb_dl_callback_func_%d_%d", rettype, entry);
   return rb_dlsym_new((void (*)())rb_dl_callback_table[rettype][entry],
 		      fname, RSTRING(type)->ptr);
+#else
+  {
+    struct callback_info *p;
+    VALUE sym, info;
+
+    info = Data_Make_Struct(rb_cData, struct callback_info,
+                            callback_info_mark, callback_info_free, p);
+    p->type = type;
+    p->proc = proc;
+    p->func = alloc_callback(&rb_dl_callback_func, p);
+
+    sym = rb_dlsym_new(p->func, NULL, RSTRING(type)->ptr);
+    rb_iv_set(sym, "ffcall_callback_info", info);
+
+    rb_hash_aset(DLFuncTable, sym, Qtrue);
+
+    return sym;
+  }
+#endif
 }
 
 static VALUE
 rb_dl_remove_callback(VALUE mod, VALUE sym)
 {
+#ifndef USE_FFCALL
   freefunc_t f = rb_dlsym2csym(sym);
   int i, j;
 
@@ -647,6 +835,9 @@ rb_dl_remove_callback(VALUE mod, VALUE s
       }
     }
   }
+#else
+  rb_hash_delete(DLFuncTable, sym);
+#endif
   return Qnil;
 }
 
@@ -665,8 +856,10 @@ Init_dl()
   rb_eDLTypeError = rb_define_class_under(rb_mDL, "DLTypeError", rb_eDLError);
 
   DLFuncTable = rb_hash_new();
+  rb_global_variable(&DLFuncTable);
+#ifndef USE_FFCALL
   init_dl_func_table();
-  rb_define_const(rb_mDL, "FuncTable", DLFuncTable);
+#endif
 
   rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
   rb_define_const(rb_mDL, "RTLD_LAZY",   INT2NUM(RTLD_LAZY));
Index: extconf.rb
===================================================================
RCS file: /src/ruby/ext/dl/extconf.rb,v
retrieving revision 1.9
diff -u -p -r1.9 extconf.rb
--- extconf.rb	21 Oct 2002 14:03:46 -0000	1.9
+++ extconf.rb	18 Feb 2003 23:50:52 -0000
@@ -16,6 +16,7 @@
   --enable-asm       use the embedded assembler for passing arguments.
                      (this option is available for i386 machine now.)
   --enable-dlstack   use a stack emulation for constructing function call.
+  --enable-ffcall    use ffcall library.
 EOF
   exit(0)
 end
@@ -68,6 +69,7 @@
 
 $with_asm        = enable_config("asm", $with_asm)
 $with_dlstack    = enable_config("dlstack", $with_dlstack)
+$with_ffcall     = enable_config("ffcall", $with_ffcall) 
 
 args = with_config("args")
 max_arg = nil
@@ -89,6 +91,9 @@
 max_callback = with_config("callback","10").to_i
 callback_types = DLTYPE.keys.length
 
+if $with_ffcall
+  $with_ffcall = have_header('callback.h') && have_library('callback', 'alloc_trampoline_r')
+end
 
 $dlconfig_h = <<EOF
 #define MAX_ARG           #{max_arg}
@@ -108,6 +113,9 @@ else
   if( $with_asm )
     $dlconfig_h << "#define USE_INLINE_ASM\n"
   end
+end
+if( $with_ffcall )
+  $dlconfig_h << "#define USE_FFCALL\n"
 end
 if( $with_type_char )
   $dlconfig_h << "#define WITH_TYPE_CHAR\n"
