00001 #include "typelib.hh"
00002
00003 #include <dyncall.h>
00004 #include <dynload.h>
00005 #include <boost/static_assert.hpp>
00006
00007 static VALUE cCallVM;
00008 static VALUE cLibrary;
00009 static VALUE cFunction;
00010
00011 using namespace Typelib;
00012 using namespace typelib_ruby;
00013
00014 static VALUE
00015 library_wrap(VALUE self, VALUE name, VALUE auto_unload)
00016 {
00017 void* libhandle = dlLoadLibrary(StringValuePtr(name));
00018 if (!libhandle)
00019 rb_raise(rb_eArgError, "cannot load library %s", StringValuePtr(name));
00020
00021 if (RTEST(auto_unload))
00022 return Data_Wrap_Struct(cLibrary, 0, dlFreeLibrary, libhandle);
00023 else
00024 return Data_Wrap_Struct(cLibrary, 0, 0, libhandle);
00025 }
00026
00027
00028
00029
00030
00031
00032
00033
00034 static VALUE
00035 library_find(VALUE self, VALUE name)
00036 {
00037
00038 DLLib* libhandle;
00039 Data_Get_Struct(self, DLLib, libhandle);
00040
00041 void* symhandle = dlFindSymbol(libhandle, StringValuePtr(name));
00042 if (!symhandle)
00043 {
00044 VALUE libname = rb_iv_get(self, "@name");
00045 rb_raise(rb_eArgError, "cannot find symbol '%s' in library '%s'",
00046 StringValuePtr(name), StringValuePtr(libname));
00047 }
00048
00049 VALUE function = Data_Wrap_Struct(cFunction, 0, 0, symhandle);
00050 rb_funcall(function, rb_intern("initialize"), 2, self, name);
00051 return function;
00052 }
00053
00054 class PrepareVM : public TypeVisitor
00055 {
00056 DCCallVM* m_vm;
00057 VALUE m_data;
00058
00059 virtual bool visit_ (Numeric const& type)
00060 {
00061 if (type.getNumericCategory() == Numeric::Float)
00062 {
00063 double value = NUM2DBL(m_data);
00064 switch(type.getSize())
00065 {
00066 case 4: dcArgFloat(m_vm, static_cast<float>(value)); break;
00067 case 8: dcArgDouble(m_vm, static_cast<double>(value)); break;
00068 }
00069 }
00070 else
00071 {
00072 switch(type.getSize())
00073 {
00074 case 1: dcArgChar (m_vm, static_cast<char>(NUM2INT(m_data))); break;
00075 case 2: dcArgShort (m_vm, static_cast<short>(NUM2INT(m_data))); break;
00076 case 4: dcArgInt (m_vm, NUM2INT(m_data)); break;
00077 case 8: dcArgLongLong(m_vm, NUM2LL(m_data)); break;
00078 }
00079 }
00080 return false;
00081 }
00082 virtual bool visit_ (Enum const& type)
00083 {
00084 BOOST_STATIC_ASSERT(( sizeof(Enum::integral_type) == sizeof(int) ));
00085 dcArgInt(m_vm, NUM2INT(m_data));
00086 return false;
00087 }
00088
00089 virtual bool visit_ (Pointer const& type)
00090 {
00091 dcArgPointer(m_vm, memory_cptr(m_data));
00092 return false;
00093 }
00094 virtual bool visit_ (Array const& type)
00095 {
00096 dcArgPointer(m_vm, memory_cptr(m_data));
00097 return false;
00098 }
00099
00100 virtual bool visit_ (Compound const& type)
00101 { throw UnsupportedType(type); }
00102
00103 public:
00104 PrepareVM(DCCallVM* vm, VALUE data)
00105 : m_vm(vm), m_data(data) {}
00106
00107 static void append(DCCallVM* vm, Type const& type, VALUE data)
00108 {
00109 PrepareVM visitor(vm, data);
00110 visitor.apply(type);
00111 }
00112 };
00113
00114 static VALUE
00115 function_compile(VALUE self, VALUE filtered_args)
00116 {
00117 DCCallVM* vm = dcNewCallVM(4096);
00118 VALUE rb_vm = Data_Wrap_Struct(cCallVM, 0, dcFree, vm);
00119 rb_iv_set(rb_vm, "@arguments", filtered_args);
00120
00121 VALUE argument_types = rb_iv_get(self, "@arguments");
00122 size_t argc = RARRAY_LEN(argument_types);
00123 VALUE* argv = RARRAY_PTR(argument_types);
00124
00125 for (size_t i = 0; i < argc; ++i)
00126 PrepareVM::append(vm, rb2cxx::object<Type>(argv[i]), RARRAY_PTR(filtered_args)[i]);
00127
00128 return rb_vm;
00129 }
00130
00131 class VMCall : public TypeVisitor
00132 {
00133 DCCallVM* m_vm;
00134 void* m_handle;
00135 VALUE m_return_type;
00136 VALUE m_return;
00137
00138 virtual bool visit_ (NullType const& type)
00139 {
00140 dcCallVoid(m_vm, m_handle);
00141 return true;
00142 }
00143
00144 virtual bool visit_ (Numeric const& type)
00145 {
00146 if (type.getNumericCategory() == Numeric::Float)
00147 {
00148 switch(type.getSize())
00149 {
00150 case 4: m_return = rb_float_new(dcCallFloat(m_vm, m_handle)); break;
00151 case 8: m_return = rb_float_new(dcCallDouble(m_vm, m_handle)); break;
00152 }
00153 }
00154 else
00155 {
00156 switch(type.getSize())
00157 {
00158 case 1: m_return = INT2NUM (dcCallChar (m_vm, m_handle)); break;
00159 case 2: m_return = INT2NUM (dcCallShort (m_vm, m_handle)); break;
00160 case 4: m_return = INT2NUM (dcCallInt (m_vm, m_handle)); break;
00161 case 8: m_return = LL2NUM (dcCallLongLong(m_vm, m_handle)); break;
00162 }
00163 }
00164 return false;
00165 }
00166 virtual bool visit_ (Enum const& type)
00167 {
00168 BOOST_STATIC_ASSERT(( sizeof(Enum::integral_type) == sizeof(int) ));
00169 m_return = INT2NUM(dcCallInt(m_vm, m_handle));
00170 return false;
00171 }
00172
00173 virtual bool visit_ (Pointer const& type)
00174 {
00175 m_return = memory_wrap(dcCallPointer(m_vm, m_handle));
00176 return false;
00177 }
00178 virtual bool visit_ (Array const& type)
00179 {
00180 m_return = memory_wrap(dcCallPointer(m_vm, m_handle));
00181 return false;
00182 }
00183
00184 virtual bool visit_ (Compound const& type)
00185 { throw UnsupportedType(type); }
00186
00187 public:
00188 VMCall(DCCallVM* vm, void* handle, VALUE return_type)
00189 : m_vm(vm), m_handle(handle), m_return_type(return_type),
00190 m_return(Qnil) {}
00191
00192 VALUE getReturnedValue() const { return m_return; }
00193
00194 static VALUE call(DCCallVM* vm, void* handle, VALUE return_type)
00195 {
00196 if (NIL_P(return_type))
00197 {
00198 dcCallVoid(vm, handle);
00199 return Qnil;
00200 }
00201
00202 VMCall visitor(vm, handle, return_type);
00203 visitor.apply(rb2cxx::object<Type>(return_type));
00204 return visitor.getReturnedValue();
00205 }
00206 };
00207
00208 static VALUE
00209 vm_call(VALUE self, VALUE function)
00210 {
00211 DCCallVM* vm;
00212 Data_Get_Struct(self, DCCallVM, vm);
00213
00214 void* symhandle;
00215 Data_Get_Struct(function, void, symhandle);
00216
00217 return VMCall::call(vm, symhandle, rb_iv_get(function, "@return_type"));
00218 }
00219
00220 static
00221 VALUE filter_numeric_arg(VALUE self, VALUE arg, VALUE rb_expected_type)
00222 {
00223 Type const& expected_type = rb2cxx::object<Type>(rb_expected_type);
00224
00225 if (expected_type.getCategory() == Type::Enum)
00226 return INT2FIX( rb2cxx::enum_value(arg, static_cast<Enum const&>(expected_type)) );
00227 else if (expected_type.getCategory() == Type::Pointer)
00228 {
00229
00230 Pointer const& ptr_type = static_cast<Pointer const&>(expected_type);
00231 Type const& pointed_type = ptr_type.getIndirection();
00232 VALUE ptr = memory_allocate(pointed_type.getSize());
00233
00234 Value typelib_value(memory_cptr(ptr), pointed_type);
00235 typelib_from_ruby(typelib_value, arg);
00236 return ptr;
00237 }
00238 return arg;
00239 }
00240
00241 static
00242 VALUE filter_value_arg(VALUE self, VALUE arg, VALUE rb_expected_type)
00243 {
00244 Type const& expected_type = rb2cxx::object<Type>(rb_expected_type);
00245 Value const& arg_value = rb2cxx::object<Value>(arg);
00246 Type const& arg_type = arg_value.getType();
00247
00248 if (arg_type == expected_type)
00249 {
00250 if (arg_type.getCategory() == Type::Pointer)
00251 return memory_wrap(*reinterpret_cast<void**>(arg_value.getData()));
00252 else if (arg_type.getCategory() == Type::Array)
00253 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00254 else if (arg_type.getCategory() == Type::Numeric)
00255 return rb_funcall(arg, rb_intern("to_ruby"), 0);
00256 else
00257 return Qnil;
00258 }
00259
00260 if (expected_type.getCategory() != Type::Pointer && expected_type.getCategory() != Type::Array)
00261 return Qnil;
00262
00263 Indirect const& expected_indirect_type = static_cast<Indirect const&>(expected_type);
00264 Type const& expected_pointed_type = expected_indirect_type.getIndirection();
00265
00266
00267
00268 if (expected_pointed_type.isNull() || expected_pointed_type == arg_type)
00269 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00270
00271
00272 Indirect const& arg_indirect = static_cast<Indirect const&>(arg_type);
00273 if (arg_indirect.getIndirection() != expected_pointed_type)
00274 return Qnil;
00275
00276 if (expected_type.getCategory() == Type::Pointer)
00277 {
00278 if (arg_type.getCategory() == Type::Pointer)
00279 {
00280
00281 return Qnil;
00282 }
00283 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00284 }
00285 else
00286 {
00287 if (arg_type.getCategory() == Type::Pointer)
00288 return memory_wrap(*reinterpret_cast<void**>(arg_value.getData()));
00289
00290 Array const& expected_array = static_cast<Array const&>(expected_type);
00291 Array const& arg_array = static_cast<Array const&>(arg_type);
00292 if (expected_array.getDimension() > arg_array.getDimension())
00293 return Qnil;
00294 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00295 }
00296 }
00297
00298 void typelib_ruby::Typelib_init_functions()
00299 {
00300 VALUE mTypelib = rb_define_module("Typelib");
00301 rb_define_singleton_method(mTypelib, "filter_numeric_arg", RUBY_METHOD_FUNC(filter_numeric_arg), 2);
00302 rb_define_singleton_method(mTypelib, "filter_value_arg", RUBY_METHOD_FUNC(filter_value_arg), 2);
00303
00304 cLibrary = rb_define_class_under(mTypelib, "Library", rb_cObject);
00305 rb_define_singleton_method(cLibrary, "wrap", RUBY_METHOD_FUNC(library_wrap), 2);
00306 rb_define_method(cLibrary, "find", RUBY_METHOD_FUNC(library_find), 1);
00307
00308 cFunction = rb_define_class_under(mTypelib, "Function", rb_cObject);
00309 rb_define_private_method(cFunction, "prepare_vm", RUBY_METHOD_FUNC(function_compile), 1);
00310
00311 cCallVM = rb_define_class_under(mTypelib, "CallVM", rb_cObject);
00312 rb_define_method(cCallVM, "call", RUBY_METHOD_FUNC(vm_call), 1);
00313 }
00314