diff ruby/rbdc/rbdc.c @ 0:0cfcc391201f

initial from svn dyncall-1745
author Daniel Adler
date Thu, 19 Mar 2015 22:26:28 +0100
parents
children 5e159be89d73
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ruby/rbdc/rbdc.c	Thu Mar 19 22:26:28 2015 +0100
@@ -0,0 +1,306 @@
+/*
+
+ rbdc.c
+ Copyright (c) 2007-2014 Daniel Adler <dadler@uni-goettingen.de>, 
+                         Tassilo Philipp <tphilipp@potion-studios.com>
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ Ruby/dyncall extension implementation.
+
+*/
+
+
+#include <ruby.h>
+#include "../../../dyncall/dyncall/dyncall.h"
+#include "../../../dyncall/dyncallback/dyncall_callback.h"
+#include "../../../dyncall/dynload/dynload.h"
+#include "../../../dyncall/dyncall/dyncall_signature.h"
+
+/* Our ruby module and its classes. */
+static VALUE rb_dcModule;
+static VALUE rb_dcExtLib;
+
+
+typedef struct {
+	void*     lib;
+	void*     syms;
+	DCCallVM* cvm;
+} rb_dcLibHandle;
+
+
+/* Allocator for handle and mark-and-sweep GC handlers. */
+static void GCMark_ExtLib(rb_dcLibHandle* h)
+{
+}
+
+static void GCSweep_ExtLib(rb_dcLibHandle* h)
+{
+	if(h->lib  != NULL) dlFreeLibrary(h->lib);
+	if(h->syms != NULL) dlSymsCleanup(h->syms);
+
+	dcFree(h->cvm);
+	free(h);
+}
+
+static VALUE AllocExtLib(VALUE cl)
+{
+	rb_dcLibHandle* h = malloc(sizeof(rb_dcLibHandle));
+	h->lib  = NULL;
+	h->syms = NULL;
+	h->cvm  = dcNewCallVM(4096/*@@@*/);
+	return Data_Wrap_Struct(cl, GCMark_ExtLib, GCSweep_ExtLib, h);
+}
+
+
+/* Helpers */
+static void ExtLib_SecCheckLib(rb_dcLibHandle* h)
+{
+	if(h->lib == NULL)
+		rb_raise(rb_eRuntimeError, "no library loaded - use ExtLib#load");
+}
+
+static void ExtLib_SecCheckSyms(rb_dcLibHandle* h)
+{
+	if(h->syms == NULL)
+		rb_raise(rb_eRuntimeError, "no symbol table initialized - use ExtLib#symsInit");
+}
+
+
+
+/* Methods for lib access */
+static VALUE ExtLib_Load(VALUE self, VALUE path)
+{
+	void* newLib;
+	rb_dcLibHandle* h;
+
+	if(TYPE(path) != T_STRING)
+		rb_raise(rb_eRuntimeError, "argument must be of type 'String'");/*@@@ respond to to_s*/
+
+	Data_Get_Struct(self, rb_dcLibHandle, h);
+	newLib = dlLoadLibrary(RSTRING_PTR(path));
+	if(newLib) {
+		dlFreeLibrary(h->lib);
+		h->lib = newLib;
+
+		return self;
+	}
+
+	return Qnil;
+}
+
+static VALUE ExtLib_ExistsQ(VALUE self, VALUE sym)
+{
+	rb_dcLibHandle* h;
+
+	Data_Get_Struct(self, rb_dcLibHandle, h);
+	ExtLib_SecCheckLib(h);
+
+	return dlFindSymbol(h->lib, rb_id2name(SYM2ID(sym))) ? Qtrue : Qfalse;
+}
+
+
+/* Methods for syms parsing */
+static VALUE ExtLib_SymsInit(VALUE self, VALUE path)
+{
+	void* newSyms;
+	rb_dcLibHandle* h;
+
+	if(TYPE(path) != T_STRING)
+		rb_raise(rb_eRuntimeError, "argument must be of type 'String'");/*@@@ respond to to_s*/
+
+	Data_Get_Struct(self, rb_dcLibHandle, h);
+	newSyms = dlSymsInit(RSTRING_PTR(path));
+
+	if(newSyms) {
+		dlSymsCleanup(h->syms);
+		h->syms = newSyms;
+
+		return self;
+	}
+
+	return Qnil;
+}
+
+
+static VALUE ExtLib_SymsCount(VALUE self)
+{
+	rb_dcLibHandle* h;
+
+	Data_Get_Struct(self, rb_dcLibHandle, h);
+	ExtLib_SecCheckSyms(h);
+
+	return LONG2NUM(dlSymsCount(h->syms));
+}
+
+
+static VALUE ExtLib_SymsEach(int argc, VALUE* argv, VALUE self)
+{
+	rb_dcLibHandle* h;
+	size_t i, c;
+
+	if(!rb_block_given_p())
+		rb_raise(rb_eRuntimeError, "no block given");
+
+	Data_Get_Struct(self, rb_dcLibHandle, h);
+	ExtLib_SecCheckSyms(h);
+
+	c = dlSymsCount(h->syms);
+	for(i=0; i<c; ++i)
+		rb_yield(ID2SYM(rb_intern(dlSymsName(h->syms, i))));
+
+	return self;
+}
+
+/* expose dlSymsName @@@ */
+
+
+/* Methods interfacing with dyncall */
+static VALUE ExtLib_Call(int argc, VALUE* argv, VALUE self)
+{
+	/* argv[0] - symbol to call  *
+	 * argv[1] - signature       *
+	 * argv[2] - first parameter *
+	 * argv[x] - parameter x-2   */
+
+	rb_dcLibHandle* h;
+	DCpointer       fptr;
+	int             i, t, b;
+	VALUE           r;
+	DCCallVM*       cvm;
+	const char*     sig;
+
+
+	/* Security checks. */
+	if(argc < 2)
+		rb_raise(rb_eRuntimeError, "wrong number of arguments for function call");
+
+	if(TYPE(argv[0]) != T_SYMBOL)
+		rb_raise(rb_eRuntimeError, "syntax error - argument 0 must be of type 'Symbol'");
+
+	if(TYPE(argv[1]) != T_STRING)
+		rb_raise(rb_eRuntimeError, "syntax error - argument 1 must be of type 'String'");
+
+	Data_Get_Struct(self, rb_dcLibHandle, h);
+	cvm = h->cvm;
+
+	if(argc != RSTRING_LEN(argv[1]))	/* Don't count the return value in the signature @@@ write something more secure */
+		rb_raise(rb_eRuntimeError, "number of provided arguments doesn't match signature");
+
+	ExtLib_SecCheckLib(h);
+
+
+	/* Flush old arguments. */
+	dcReset(cvm);
+
+
+	/* Get a pointer to the function and start pushing. */
+	fptr = (DCpointer)dlFindSymbol(h->lib, rb_id2name(SYM2ID(argv[0])));
+	sig = RSTRING_PTR(argv[1]);
+
+	for(i=2; i<argc; ++i) {
+		t = TYPE(argv[i]);
+
+		//@@@ add support for calling convention mode(s)
+
+		switch(sig[i-2]) {
+			case DC_SIGCHAR_BOOL:
+				b = 1;
+				switch(t) {
+					case T_TRUE:   dcArgBool(cvm, DC_TRUE);                break;  /* TrueClass.  */
+					case T_FALSE:                                                  /* FalseClass. */
+					case T_NIL:    dcArgBool(cvm, DC_FALSE);               break;  /* NilClass.   */
+					case T_FIXNUM: dcArgBool(cvm, FIX2LONG(argv[i]) != 0); break;  /* Fixnum.     */
+					default:       b = 0;                                  break;
+				}
+				break;
+
+			case DC_SIGCHAR_CHAR:
+			case DC_SIGCHAR_UCHAR:     if(b = (t == T_FIXNUM)) dcArgChar    (cvm, (DCchar)    FIX2LONG(argv[i]));      break;
+			case DC_SIGCHAR_SHORT:
+			case DC_SIGCHAR_USHORT:    if(b = (t == T_FIXNUM)) dcArgShort   (cvm, (DCshort)   FIX2LONG(argv[i]));      break;
+			case DC_SIGCHAR_INT:
+			case DC_SIGCHAR_UINT:      if(b = (t == T_FIXNUM)) dcArgInt     (cvm, (DCint)     FIX2LONG(argv[i]));      break;
+			case DC_SIGCHAR_LONG:
+			case DC_SIGCHAR_ULONG:     if(b = (t == T_FIXNUM)) dcArgLong    (cvm, (DClong)    FIX2LONG(argv[i]));      break;
+			case DC_SIGCHAR_LONGLONG:
+			case DC_SIGCHAR_ULONGLONG: if(b = (t == T_FIXNUM)) dcArgLongLong(cvm, (DClonglong)FIX2LONG(argv[i]));      break;
+			case DC_SIGCHAR_FLOAT:     if(b = (t == T_FLOAT))  dcArgFloat   (cvm, (DCfloat)   RFLOAT_VALUE(argv[i]));  break;
+			case DC_SIGCHAR_DOUBLE:    if(b = (t == T_FLOAT))  dcArgDouble  (cvm, (DCdouble)  RFLOAT_VALUE(argv[i]));  break;
+
+			case DC_SIGCHAR_POINTER:
+			case DC_SIGCHAR_STRING:
+				b = 1;	
+				switch(t) {
+					case T_STRING: dcArgPointer(cvm, RSTRING_PTR(argv[i])); break;  /* String. */
+					default:       b = 0;                                   break;
+				}
+				break;
+
+			default:
+				b = 0;
+				break;
+		}
+
+
+		if(!b)
+			rb_raise(rb_eRuntimeError, "syntax error in signature or type mismatch at argument %d", i-2);
+	}
+
+
+	/* Get the return type and call the function. */
+	switch(sig[i-1]) {
+		case DC_SIGCHAR_VOID:      r = Qnil;        dcCallVoid    (cvm, fptr);                  break;
+		case DC_SIGCHAR_BOOL:      r =              dcCallBool    (cvm, fptr) ? Qtrue : Qfalse; break;
+		case DC_SIGCHAR_CHAR:
+		case DC_SIGCHAR_UCHAR:     r = CHR2FIX(     dcCallChar    (cvm, fptr));                 break;
+		case DC_SIGCHAR_SHORT:
+		case DC_SIGCHAR_USHORT:    r = INT2FIX(     dcCallShort   (cvm, fptr));                 break;
+		case DC_SIGCHAR_INT:
+		case DC_SIGCHAR_UINT:      r = INT2FIX(     dcCallInt     (cvm, fptr));                 break;
+		case DC_SIGCHAR_LONG:
+		case DC_SIGCHAR_ULONG:     r = INT2FIX(     dcCallLong    (cvm, fptr));                 break;
+		case DC_SIGCHAR_LONGLONG:
+		case DC_SIGCHAR_ULONGLONG: r = INT2FIX(     dcCallLongLong(cvm, fptr));                 break;
+		case DC_SIGCHAR_FLOAT:     r = rb_float_new(dcCallFloat   (cvm, fptr));                 break;
+		case DC_SIGCHAR_DOUBLE:    r = rb_float_new(dcCallDouble  (cvm, fptr));                 break;
+		case DC_SIGCHAR_STRING:    r = rb_str_new2( dcCallPointer (cvm, fptr));                 break;
+		case DC_SIGCHAR_POINTER:
+		default:
+			rb_raise(rb_eRuntimeError, "unsupported return type or syntax error in signature");
+	}
+
+	return r;
+}
+
+
+/* Main initialization. */
+void Init_rbdc()
+{
+	rb_dcModule = rb_define_module("Dyncall");
+
+	/* Handle to the external dynamic library. */
+	rb_dcExtLib = rb_define_class_under(rb_dcModule, "ExtLib", rb_cObject);
+
+	/* Class allocators. */
+	rb_define_alloc_func(rb_dcExtLib, AllocExtLib);
+
+	/* Methods. */
+	rb_define_method(rb_dcExtLib, "load",       &ExtLib_Load,      1);
+	rb_define_method(rb_dcExtLib, "exists?",    &ExtLib_ExistsQ,   1);
+	rb_define_method(rb_dcExtLib, "syms_init",  &ExtLib_SymsInit,  1);
+	rb_define_method(rb_dcExtLib, "syms_count", &ExtLib_SymsCount, 0);
+	rb_define_method(rb_dcExtLib, "syms_each",  &ExtLib_SymsEach, -1);
+	rb_define_method(rb_dcExtLib, "call",       &ExtLib_Call,     -1);
+}
+