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 #ifdef VERBOSE
00176 fprintf(stderr, "wrapping dcCallPointer with type=%s\n", type.getName().c_str());
00177 #endif
00178 m_return = memory_wrap(new void*(dcCallPointer(m_vm, m_handle)), true, 0);
00179 return false;
00180 }
00181 virtual bool visit_ (Array const& type)
00182 {
00183 #ifdef VERBOSE
00184 fprintf(stderr, "wrapping dcCallPointer with type=%s\n", type.getName().c_str());
00185 #endif
00186 m_return = memory_wrap(new void*(dcCallPointer(m_vm, m_handle)), true, 0);
00187 return false;
00188 }
00189
00190 virtual bool visit_ (Compound const& type)
00191 { throw UnsupportedType(type); }
00192
00193 public:
00194 VMCall(DCCallVM* vm, void* handle, VALUE return_type)
00195 : m_vm(vm), m_handle(handle), m_return_type(return_type),
00196 m_return(Qnil) {}
00197
00198 VALUE getReturnedValue() const { return m_return; }
00199
00200 static VALUE call(DCCallVM* vm, void* handle, VALUE return_type)
00201 {
00202 if (NIL_P(return_type))
00203 {
00204 dcCallVoid(vm, handle);
00205 return Qnil;
00206 }
00207
00208 VMCall visitor(vm, handle, return_type);
00209 visitor.apply(rb2cxx::object<Type>(return_type));
00210 return visitor.getReturnedValue();
00211 }
00212 };
00213
00214 static VALUE
00215 vm_call(VALUE self, VALUE function)
00216 {
00217 DCCallVM* vm;
00218 Data_Get_Struct(self, DCCallVM, vm);
00219
00220 void* symhandle;
00221 Data_Get_Struct(function, void, symhandle);
00222
00223 return VMCall::call(vm, symhandle, rb_iv_get(function, "@return_type"));
00224 }
00225
00226 static
00227 VALUE filter_numeric_arg(VALUE self, VALUE arg, VALUE rb_expected_type)
00228 {
00229 Type const& expected_type = rb2cxx::object<Type>(rb_expected_type);
00230
00231 if (expected_type.getCategory() == Type::Enum)
00232 return INT2FIX( rb2cxx::enum_value(arg, static_cast<Enum const&>(expected_type)) );
00233 else if (expected_type.getCategory() == Type::Pointer)
00234 {
00235
00236 Pointer const& ptr_type = static_cast<Pointer const&>(expected_type);
00237 Type const& pointed_type = ptr_type.getIndirection();
00238 VALUE ptr = memory_allocate(pointed_type.getSize());
00239
00240 Value typelib_value(memory_cptr(ptr), pointed_type);
00241 typelib_from_ruby(typelib_value, arg);
00242 return ptr;
00243 }
00244 return arg;
00245 }
00246
00247 static
00248 VALUE filter_value_arg(VALUE self, VALUE arg, VALUE rb_expected_type)
00249 {
00250 Type const& expected_type = rb2cxx::object<Type>(rb_expected_type);
00251 Value const& arg_value = rb2cxx::object<Value>(arg);
00252 Type const& arg_type = arg_value.getType();
00253
00254 if (arg_type == expected_type)
00255 {
00256 if (arg_type.getCategory() == Type::Pointer)
00257 {
00258 #ifdef VERBOSE
00259 fprintf(stderr, "wrapping filtered argument with arg_type=%s\n", arg_type.getName().c_str());
00260 #endif
00261 return memory_wrap(*reinterpret_cast<void**>(arg_value.getData()), false, 0);
00262 }
00263 else if (arg_type.getCategory() == Type::Array)
00264 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00265 else if (arg_type.getCategory() == Type::Numeric)
00266 return rb_funcall(arg, rb_intern("to_ruby"), 0);
00267 else
00268 return Qnil;
00269 }
00270
00271 if (expected_type.getCategory() != Type::Pointer && expected_type.getCategory() != Type::Array)
00272 return Qnil;
00273
00274 Indirect const& expected_indirect_type = static_cast<Indirect const&>(expected_type);
00275 Type const& expected_pointed_type = expected_indirect_type.getIndirection();
00276
00277
00278
00279 if (expected_pointed_type.isNull() || expected_pointed_type == arg_type)
00280 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00281
00282
00283 Indirect const& arg_indirect = static_cast<Indirect const&>(arg_type);
00284 if (arg_indirect.getIndirection() != expected_pointed_type)
00285 return Qnil;
00286
00287 if (expected_type.getCategory() == Type::Pointer)
00288 {
00289 if (arg_type.getCategory() == Type::Pointer)
00290 {
00291
00292 return Qnil;
00293 }
00294 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00295 }
00296 else
00297 {
00298 if (arg_type.getCategory() == Type::Pointer)
00299 return memory_wrap(*reinterpret_cast<void**>(arg_value.getData()), false, 0);
00300
00301 Array const& expected_array = static_cast<Array const&>(expected_type);
00302 Array const& arg_array = static_cast<Array const&>(arg_type);
00303 if (expected_array.getDimension() > arg_array.getDimension())
00304 return Qnil;
00305 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00306 }
00307 }
00308
00309 void typelib_ruby::Typelib_init_functions()
00310 {
00311 VALUE mTypelib = rb_define_module("Typelib");
00312 rb_define_singleton_method(mTypelib, "filter_numeric_arg", RUBY_METHOD_FUNC(filter_numeric_arg), 2);
00313 rb_define_singleton_method(mTypelib, "filter_value_arg", RUBY_METHOD_FUNC(filter_value_arg), 2);
00314
00315 cLibrary = rb_define_class_under(mTypelib, "Library", rb_cObject);
00316 rb_define_singleton_method(cLibrary, "wrap", RUBY_METHOD_FUNC(library_wrap), 2);
00317 rb_define_method(cLibrary, "find", RUBY_METHOD_FUNC(library_find), 1);
00318
00319 cFunction = rb_define_class_under(mTypelib, "Function", rb_cObject);
00320 rb_define_private_method(cFunction, "prepare_vm", RUBY_METHOD_FUNC(function_compile), 1);
00321
00322 cCallVM = rb_define_class_under(mTypelib, "CallVM", rb_cObject);
00323 rb_define_method(cCallVM, "call", RUBY_METHOD_FUNC(vm_call), 1);
00324 }
00325