$search
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 /* call-seq: 00028 * lib.find(function_name) => function object 00029 * 00030 * Searches for the function named +function_name+ in +lib+ and returns 00031 * the corresponding Function object. If the symbol is not available in 00032 * the library, raises ArgumentError. 00033 */ 00034 static VALUE 00035 library_find(VALUE self, VALUE name) 00036 { 00037 //void* libhandle; 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 // Build directly a DL::Ptr object, no need to build a Ruby Value wrapper 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 // /void == /nil, so that if expected_type is null, then 00278 // it is because the argument can hold any kind of pointers 00279 if (expected_pointed_type.isNull() || expected_pointed_type == arg_type) 00280 return rb_funcall(arg, rb_intern("to_memory_ptr"), 0); 00281 00282 // There is only array <-> pointer conversion left to handle 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 // if it was OK, then arg_type == expected_type which is already handled 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 // check sizes 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