diff options
author | ttate <ttate@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2002-04-02 10:56:13 +0000 |
---|---|---|
committer | ttate <ttate@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2002-04-02 10:56:13 +0000 |
commit | d235ceb328813138a4910c9798e9d5959405de4b (patch) | |
tree | b1b54ab76217775071de4e3669674aa7887aa6b9 | |
parent | b7389bf5d46b0434b940ed78e650efc16ffc8bc6 (diff) | |
download | ruby-d235ceb328813138a4910c9798e9d5959405de4b.tar.gz ruby-d235ceb328813138a4910c9798e9d5959405de4b.tar.xz ruby-d235ceb328813138a4910c9798e9d5959405de4b.zip |
Add ruby-dl
git-svn-id: http://svn.ruby-lang.org/repos/ruby/trunk@2324 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ext/Setup | 1 | ||||
-rw-r--r-- | ext/dl/Changes | 166 | ||||
-rw-r--r-- | ext/dl/MANIFEST | 24 | ||||
-rw-r--r-- | ext/dl/README | 186 | ||||
-rw-r--r-- | ext/dl/README.html | 247 | ||||
-rw-r--r-- | ext/dl/depend | 45 | ||||
-rw-r--r-- | ext/dl/dl.c | 655 | ||||
-rw-r--r-- | ext/dl/dl.def | 70 | ||||
-rw-r--r-- | ext/dl/dl.h | 282 | ||||
-rw-r--r-- | ext/dl/extconf.rb | 197 | ||||
-rw-r--r-- | ext/dl/h2rb | 500 | ||||
-rw-r--r-- | ext/dl/handle.c | 207 | ||||
-rw-r--r-- | ext/dl/install.rb | 49 | ||||
-rw-r--r-- | ext/dl/lib/dl/import.rb | 228 | ||||
-rw-r--r-- | ext/dl/lib/dl/win32.rb | 26 | ||||
-rw-r--r-- | ext/dl/mkcall.rb | 68 | ||||
-rw-r--r-- | ext/dl/mkcallback.rb | 83 | ||||
-rw-r--r-- | ext/dl/mkcbtable.rb | 42 | ||||
-rw-r--r-- | ext/dl/ptr.c | 1075 | ||||
-rw-r--r-- | ext/dl/sample/drives.rb | 70 | ||||
-rw-r--r-- | ext/dl/sample/getch.rb | 5 | ||||
-rw-r--r-- | ext/dl/sample/libc.rb | 84 | ||||
-rw-r--r-- | ext/dl/sample/msgbox.rb | 19 | ||||
-rw-r--r-- | ext/dl/sample/msgbox2.rb | 18 | ||||
-rw-r--r-- | ext/dl/sample/stream.rb | 87 | ||||
-rw-r--r-- | ext/dl/sym.c | 771 | ||||
-rw-r--r-- | ext/dl/test/libtest.def | 29 | ||||
-rw-r--r-- | ext/dl/test/test.c | 251 | ||||
-rw-r--r-- | ext/dl/test/test.rb | 272 | ||||
-rw-r--r-- | ext/dl/type.rb | 115 |
30 files changed, 5872 insertions, 0 deletions
@@ -9,6 +9,7 @@ #digest/rmd160 #digest/sha1 #digest/sha2 +#dl #etc #fcntl #gdbm diff --git a/ext/dl/Changes b/ext/dl/Changes new file mode 100644 index 000000000..d02dd9ed4 --- /dev/null +++ b/ext/dl/Changes @@ -0,0 +1,166 @@ +1.0.2 -> 1.1.0 +--------------- +* Use inline assembler for gcc on ix86 as a default. +* Add DL::Importable module (defined in "dl/import.rb"). + +1.0.1 -> 1.0.2 +-------------- +* Fix an installation problem, thanks to Nakada. +* Handle#to_i, to_ptr is added. +* lib/dl/win32.rb is added for Win32API compatibility. + +1.0 -> 1.0.1 +------------- +* Fix PtrData#to_s. +* Add PtrData#to_str(size). + +0.9 -> 1.0 (stable) +------------------- +* PtrData object keeps size data if possible. +* PtrData#size is added. +* LoadLibary(), FreeLibrary() and GetProcAddress() are used on + windows instead of dlopen(),dlclose() and dlsym(). + +0.8.1 -> 0.9 +------------ +* You can use inline assembler to constracting the function calls by + building the library using --with-asm options for extconf.rb. + But now this can work with GNU assembler on i386 machines. If you try + this mechanism, you can get rid of the limit of number of parameters. + +0.8 -> 0.8.1 +------------- +* rb_dlsym_call() calls xmalloc() or dlmalloc() instread of + alloca() because of portability. + +* 'h2rb' get to parse the enumeration type. + +0.7.1 -> 0.8 +------------- +* If <type> of Symbol.new(addr, name = nil, type = nil) is nil, + it returns a DataPtr object. + +* PtrData#[] returns the memory image, and PtrData#[]= is used + to copy the memory image to specified area. + +* Handle#sym(symbol_name) returns a PtrData object, and + Handle#sym(symbol_name, type_spec) returns a Symbol object. + +* We can use the number following a type specifier to represent the + length of an array. For example, if you'd like to represent the + following structure, we use 'C1024I' when using PtrData#struct! or + PtrData#union!. + + struct { + char name[1024]; + int age; + } + +0.6 -> 0.7 +----------- +* type `ANY_FUNC' is removed, it is because the method for + calling the function obtained by dlsym() was changed. + +* `char', `short' and `float' types are supported, if you'd + like to use those type exactly and run extconf.rb with the + option `--with-type-{char,short,float}'. + +* `DL.sizeof(type)' returns the size of the <type> with the + alignment. so `DL.sizeof("C") + DL.sizeof("L")' is not equal + to `DL.sizeof("CL")'. it is assumed that the latter returns + the enough size for the following structure: + struct foo { char x; long y; } + but it may not equal to `sizeof(struct foo)' of C. + +* new methods: + - PtrData#define_data_type + - PtrData#struct! + - PtrData#union! + - PtrData#data_type + - PtrData#[] + - PtrData#[]= + - Symbol.new + +0.5 -> 0.6 +----------- +* DL.set_callback is changed. + - set_callback(type,num,proc) [old style] + - set_callback(type,num){...} [new style] + +* the handle object don't call dlclose() at the time of GC. + if you need to call dlclose(), use Handle#enable_close. + +* new methods: + - PtrData#{+,-} + - PtrData#{+@,-@} (= PtrData#{ptr,ref}) + - DL.malloc + - DL.strdup + - MemorySpace.each + +* some memory leaks are fixed. + +0.4 -> 0.5 +---------- +* new methods: + - DL.dlopen (= DL::Handle.new) + - PtrData#ref + - PtrData#== + - PtrData#eql? + - String#to_ptr + - Handle#enable_close + - Handle#disable_close + +* PtrData#ptr returns the pointed value (*ptr). + +* PtrData#ref returns the reference (&ptr). + `ptr.ref.ptr == ptr' must be true. + +(experiment) +* the callback function is supported. + +* new methods: + - DL.set_callback + - DL.get_callback + + +0.3 -> 0.4 +---------- +* Symbol#call supports the mechanism for converting any object + to PtrData object automatically using 'to_ptr' method, if the + argument type is 'P' or 'p'. + +* new methods are added. + - Array#to_ptr + - IO#to_ptr + - Symbol#to_ptr + - Symbol#[] + +* new constant is added. + - DL::FREE is a symbol object for representing the function 'free'. + +* the specification of PtrData#free was changed. + +* new internal functions are added. + - rb_dlptr2cptr() + - rb_dlsym2csym() + - rb_dlsym_new() + +* 'dl.h' is new file. + +* 'extconf.rb' and 'depend' ware modified so that we can build + the library in a directory except 'srcdir' (by N. Nakada). + +* (experimental) 'h2rb' is a new script which converts C header + files into ruby scripts. + +0.2 -> 0.3 +---------- +* many useful functions ware added (by Akinori Musha). + +* the type of 'long' was supported (by Akinori Musha). + +* some methods of the PtrData ware added for handling pointer data. + +* bug fix for mutable int and long. + +* the type of array was supported. diff --git a/ext/dl/MANIFEST b/ext/dl/MANIFEST new file mode 100644 index 000000000..a98d6f5b3 --- /dev/null +++ b/ext/dl/MANIFEST @@ -0,0 +1,24 @@ +.cvsignore +Changes +MANIFEST +README +README.html +depend +dl.c +dl.h +dl.def +extconf.rb +h2rb +handle.c +mkcall.rb +mkcallback.rb +mkcbtable.rb +ptr.c +sym.c +type.rb +test/test.c +test/test.rb +sample/drives.rb +sample/getch.rb +sample/libc.rb +sample/msgbox.rb diff --git a/ext/dl/README b/ext/dl/README new file mode 100644 index 000000000..2463e48bb --- /dev/null +++ b/ext/dl/README @@ -0,0 +1,186 @@ + Ruby/DL + + an interface to dynamic linking loader +------------------------------------------------------------------------------- + +Ruby/DL + +`Ruby/DL' provides an interface to the dynamic linking loader. +------------------------------------------------------------------------------- + +Installing + + $ ruby extconf.rb # to create the Makefile + $ make # to build the library 'dl.so' + $ make libtest.so # to build the C library 'libtest.so' for the test script + $ make test # to run the test script + $ make install # to install the library + $ make clean # to remove the created files without Makefile + $ make distclean # to remove the all created files + +------------------------------------------------------------------------------- + +Functions and Classes + +after loading the `dl' library, we get access to the module called `DL'. the DL +module has the following constants, functions and classes. + +Constants + +VERSION +MAJOR_VERSION +MINOR_VERSION +PATCH_VERSION +RTLD_GLOBAL +RTLD_LAZY +RTLD_NOW +MAX_ARG +MAX_CBARG +MAX_CBENT + +Functions + +handle = dlopen(lib){|handle| ... } + is quite equal to `Handle.new(lib)' +sym = set_callback(cbtype, entry){|args| ... } +sym = set_callback(cbtype, entry, proc) + makes entry-th pre-defined function to call the proc or given block. the + entry-th pre-defined function is specified by cbtype and entry. cbtype is a + prototype of the callback. see also the section `Type specifiers' about + cbtype. +sym = get_callback(cbtype, entry) + returns the Proc object which is given by the above function + `set_callback'. +ptr = malloc(size, [free = nil]) + allocates the size bytes, and returns the pointer as a PtrData object ptr. +ptr = strdup(str) + returns a PtrData object ptr which represents the pointer to a new string + which is a duplicate of the string str. +size = sizeof(type) + returns the size of type. `sizeof("C") + sizeof("L")' is not equal to + `sizeof("CL")'. the latter is assumed to returns the enough size of the + structure `struct foo { char c; long l; }', but the size may not equal to + `sizeof(foo)' of C. + +class Handle + +handle = Handle.new(lib){|handle| ... } + opens a library lib and returns a Handle object handle. if a block is + given, the handle is automatically closed as the block ends. +Handle#close + closes the handle opened by the above Handle.new(lib). +sym = Handle#sym(func, prototype = "0") +sym = Handle#[func, prototype = nil] + obtains the pointer to a function called func and returns a Symbol object + or a DataPtr object. prototype is a string which consists of type + specifiers, it indicates the function's prototype. see also the section + `Type specifiers'. + +class Symbol + +sym = Symbol.new(addr, type = nil, name = nil) + creates the Symbol object sym with the type type if type is not nil. addr + is the address where the function is allocated. If type is nil, it returns + a DataPtr object. +Symbol::char2type(char) + takes a character char that represents a type and returns the type + specifier of the C language. +str = Symbol#proto() + returns the function prototype. +str = Symbol#name() + Returns the function name. +str = Symbol#cproto() +str = Symbol#to_s() + returns the prototype of the C language. +str = Symbol#inspect() + returns the inspectable string. +r,rs = Symbol#call(arg1,arg2,...,argN) +r,rs = Symbol#[](arg1,arg2,...,argN) + calls the function with parameters arg1, arg2, ..., argN. and the result + consists of the return value r and parameters rs. rs is an array. +ptr = Symbol#to_ptr + returns the corresponding PtrData object ptr. + +class PtrData + +ptr = PtrData.new(addr, [free = nil]) + returns the PtrData object representing the pointer which indicates the + address addr. GC frees the memory using the free function. +PtrData#free=(sym) + if you specify a symbol object sym, GC frees the memory using the function + represented by sym. +sym = PtrData#free + returns a symbol object sym which is used when GC frees the memory. it + usually configured by `PtrData#free=' or `PtrData.new'. +size = PtrData#size, PtrData#size=(size) + gets and sets allocated size of the memory. +ary = PtrData#to_a(type, [size]) + returns an array of the type which specified with type. type must be one of + 'S','P','I','L','D' and 'F'. +str = PtrData#to_s([len]) + returns a string which length is len. if len is omitted, the end of the + string is '\0'. +ptr = PtrData#ptr,+@ + returns the pointed value as a PtrData object ptr. +ptr = PtrData#ref,-@ + returns the reference as a PtrData object ptr. +ptr = PtrData#+ + returns the PtrData object +ptr = PtrData#- + returns the PtrData object +PtrData#struct!(type, *members) + defines the data type to get access to a structure member with a symbol. + (see also PtrData#[]) +PtrData#union!(type, *members) + defines the data type to get access to a union member with a symbol. (see + also PtrData#[]) +val = PtrData#[key], PtrData#[key, num = 0] + if the key is a string or symbol, this method returns the value of the + structure/union member which has the type defined by PtrData# + {struct!,union!}. if the key is a integer value and this object represents + the pointer ptr, it returns the value of `(ptr + key).to_s(num)' +PtrData#[key,num]=val, PtrData#[key]=val + if the key is a string or symbol, this method substitute the value of the + structure/union member with val. if the key is a integer value and val is a + string, this method copies num bytes of val to the memory area ptr using + memcpy(3). + +------------------------------------------------------------------------------- + +Type specifiers + +the prototype consists of the following type specifiers, first element of +prototype represents the type of return value, and remaining elements represent +the type of each argument. + + C : a character (char) + c : a pointer to a character (char *) + H : a short integer (short) + h : a pointer to a short integer (short *) + I : an integer (char, short, int) + i : a pointer to an integer (char *, short *, int *) + L : a long integer (long) + l : a pointer to a long integer (long *) + F : a real (float) + f : a pointer to a real (float *) + D : a real (double) + d : a pointer to a real (double *) + S : an immutable string (const char *) + s : a mutable string (char *) + A : an array (const type[]) + a : a mutable array (type[]) + P : a pointer (void *) + p : a mutable object (void *) + 0 : void function (this must be a first character of the prototype) + +the cbtype consists of type specifiers 0, I, L, D and P. +for example: + + DL.set_callback('IPP',0){|ptr1,ptr2| + str1 = ptr1.ptr.to_s + str2 = ptr2.ptr.to_s + return str1 <=> str2 + } + +------------------------------------------------------------------------------- +ttate@kt.jaist.ac.jp diff --git a/ext/dl/README.html b/ext/dl/README.html new file mode 100644 index 000000000..b248cce1f --- /dev/null +++ b/ext/dl/README.html @@ -0,0 +1,247 @@ +<html> +<head><title>Ruby/DL</title></head> +<body> +<center> + <h2>Ruby/DL</h2> + an interface to dynamic linking loader +</center> + +<hr> +<h2>Ruby/DL</h2> + +`Ruby/DL' provides an interface to the dynamic linking loader. + +<hr> +<h2>Installing</h2> + +<blockquote> +<pre> +$ ruby extconf.rb # to create the Makefile +$ make # to build the library 'dl.so' +$ make libtest.so # to build the C library 'libtest.so' for the test script +$ make test # to run the test script +$ make install # to install the library +$ make clean # to remove the created files without Makefile +$ make distclean # to remove the all created files +</pre> +</blockquote> + +<hr> +<h2>Functions and Classes</h2> + +after loading the `dl' library, we get access to the module called `DL'. +the DL module has the following constants, functions and classes. + +<h2>Constants</h2> + +VERSION<br> +MAJOR_VERSION<br> +MINOR_VERSION<br> +PATCH_VERSION<br> +RTLD_GLOBAL<br> +RTLD_LAZY<br> +RTLD_NOW<br> +MAX_ARG<br> +MAX_CBARG<br> +MAX_CBENT<br> + +<h2>Functions</h2> + +<dl> + <dt>handle = dlopen(lib){|handle| ... }</dt> + <dd>is quite equal to `Handle.new(lib)' + + <dt>sym = set_callback(cbtype, entry){|args| ... } + <dt>sym = set_callback(cbtype, entry, proc) + <dd>makes <u>entry</u>-th pre-defined function to call the <u>proc</u> + or given block. + the <u>entry</u>-th pre-defined function is specified by + <u>cbtype</u> and <u>entry</u>. + <u>cbtype</u> is a prototype of the callback. + see also the section `Type specifiers' about <u>cbtype</u>. + + <dt>sym = get_callback(cbtype, entry) + <dd>returns the Proc object which is given by the above function `set_callback'. + + <dt>ptr = malloc(size, [free = nil]) + <dd>allocates the <u>size</u> bytes, and returns the pointer as a + PtrData object <u>ptr</u>. + + <dt>ptr = strdup(str) + <dd>returns a PtrData object <u>ptr</u> which represents the pointer to + a new string which is a duplicate of the string <u>str</u>. + + <dt>size = sizeof(type) + <dd>returns the size of <u>type</u>. `sizeof("C") + sizeof("L")' is not + equal to `sizeof("CL")'. the latter is assumed to returns the + enough size of the structure `struct foo { char c; long l; }', + but the size may not equal to `sizeof(foo)' of C. +</dl> + +<h2>class Handle</h2> + +<dl> + <dt>handle = Handle.new(lib){|handle| ... }</dt> + <dd>opens a library <u>lib</u> and returns a Handle object + <u>handle</u>. if a block is given, the handle is + automatically closed as the block ends. + + <dt>Handle#close + <dd>closes the handle opened by the above Handle.new(lib). + + <dt>sym = Handle#sym(func, prototype = "0") + <dt>sym = Handle#[func, prototype = nil] + <dd>obtains the pointer to a function called <u>func</u> and returns + a Symbol object or a DataPtr object. + <u>prototype</u> is a string which consists of type specifiers, + it indicates the function's prototype. + see also the section `Type specifiers'. +</dl> + +<h2>class Symbol</h2> + +<dl> + <dt>sym = Symbol.new(addr, type = nil, name = nil) + <dd>creates the Symbol object <u>sym</u> with the type <u>type</u> + if <u>type</u> is not nil. <u>addr</u> is the address where the + function is allocated. If <u>type</u> is nil, it returns a DataPtr + object. + + <dt>Symbol::char2type(char) + <dd>takes a character <u>char</u> that represents a type and returns + the type specifier of the C language. + + <dt>str = Symbol#proto() + <dd>returns the function prototype. + + <dt>str = Symbol#name() + <dd>Returns the function name. + + <dt>str = Symbol#cproto() + <dt>str = Symbol#to_s() + <dd>returns the prototype of the C language. + + <dt>str = Symbol#inspect() + <dd>returns the inspectable string. + + <dt>r,rs = Symbol#call(arg1,arg2,...,argN) + <dt>r,rs = Symbol#[](arg1,arg2,...,argN) + <dd>calls the function with parameters arg1, arg2, ..., argN. + and the result consists of the return value <u>r</u> and + parameters <u>rs</u>. <u>rs</u> is an array. + + <dt>ptr = Symbol#to_ptr + <dd>returns the corresponding PtrData object <u>ptr</u>. +</dl> + +<h2>class PtrData</h2> + +<dl> + <dt>ptr = PtrData.new(addr, [free = nil]) + <dd>returns the PtrData object representing the pointer which + indicates the address <u>addr</u>. + GC frees the memory using the <u>free</u> function. + + <dt>PtrData#free=(sym) + <dd>if you specify a symbol object <u>sym</u>, GC frees the memory + using the function represented by <u>sym</u>. + + <dt>sym = PtrData#free + <dd>returns a symbol object <u>sym</u> which is used when GC frees + the memory. it usually configured by `PtrData#free=' or `PtrData.new'. + + <dt>size = PtrData#size, PtrData#size=(size) + <dd>gets and sets allocated size of the memory. + + <dt>ary = PtrData#to_a(type, [size]) + <dd>returns an array of the type which specified with <u>type</u>. + <u>type</u> must be one of 'S','P','I','L','D' and 'F'. + + <dt>str = PtrData#to_s([len]) + <dd>returns a string which length is <u>len</u>. if <u>len</u> + is omitted, the end of the string is '\0'. + + <dt>ptr = PtrData#ptr,+@ + <dd>returns the pointed value as a PtrData object <u>ptr</u>. + + <dt>ptr = PtrData#ref,-@ + <dd>returns the reference as a PtrData object <u>ptr</u>. + + <dt>ptr = PtrData#+ + <dd>returns the PtrData object + + <dt>ptr = PtrData#- + <dd>returns the PtrData object + + <dt>PtrData#struct!(type, *members) + <dd>defines the data type to get access to a structure member with a symbol. + (see also PtrData#[]) + + <dt>PtrData#union!(type, *members) + <dd>defines the data type to get access to a union member with a symbol. + (see also PtrData#[]) + + <dt>val = PtrData#[key], PtrData#[key, num = 0] + <dd>if the <u>key</u> is a string or symbol, this method returns the + value of the structure/union member which has the type defined by + PtrData#{struct!,union!}. + if the <u>key</u> is a integer value and this object represents + the pointer <u>ptr</u>, it returns the value of + `(<u>ptr</u> + <u>key</u>).to_s(num)' + + <dt>PtrData#[key,num]=val, PtrData#[key]=val + <dd>if the <u>key</u> is a string or symbol, this method substitute + the value of the structure/union member with <u>val</u>. + if the <u>key</u> is a integer value and <u>val</u> is a string, + this method copies <u>num</u> bytes of <u>val</u> to the memory + area <u>ptr</u> using memcpy(3). +</dl> + +<hr> +<h2>Type specifiers</h2> + +the <u>prototype</u> consists of the following type specifiers, +first element of <u>prototype</u> represents the type of return value, +and remaining elements represent the type of each argument. + +<blockquote> + C : a character (char)<br> + c : a pointer to a character (char *)<br> + H : a short integer (short)<br> + h : a pointer to a short integer (short *)<br> + I : an integer (char, short, int)<br> + i : a pointer to an integer (char *, short *, int *)<br> + L : a long integer (long)<br> + l : a pointer to a long integer (long *)<br> + F : a real (float)<br> + f : a pointer to a real (float *)<br> + D : a real (double)<br> + d : a pointer to a real (double *)<br> + S : an immutable string (const char *)<br> + s : a mutable string (char *)<br> + A : an array (const type[])<br> + a : a mutable array (type[])<br> + P : a pointer (void *)<br> + p : a mutable object (void *)<br> + 0 : void function + (this must be a first character of the <u>prototype</u>)<br> +</blockquote> + +the <u>cbtype</u> consists of type specifiers 0, I, L, D and P. +<br> +for example: +<blockquote> +<pre> +DL.set_callback('IPP',0){|ptr1,ptr2| + str1 = ptr1.ptr.to_s + str2 = ptr2.ptr.to_s + return str1 <=> str2 +} +</pre> +</blockquote> + +<hr> +<i>ttate@kt.jaist.ac.jp</i> + +</body> +</html>
\ No newline at end of file diff --git a/ext/dl/depend b/ext/dl/depend new file mode 100644 index 000000000..50ef5cebb --- /dev/null +++ b/ext/dl/depend @@ -0,0 +1,45 @@ +RUBY = $(RUBY_INSTALL_NAME)$(EXEEXT) +CLEANFILES = test/test.o +DISTCLEANFILES = call.func callback.func cbtable.func dlconfig.rb dlconfig.h \ + test/libtest.so test/*~ *~ mkmf.log + +libtest.so: test/libtest.so + +test/libtest.so: test/test.o test/libtest.def + $(RUBY) -rftools -e 'ARGV.each{|d|File.mkpath(File.dirname(d))}' $@ + `$(RUBY) -e 'print ARGV.join(" ").gsub(/dl\\.def/,"test/libtest.def")' $(LDSHARED)` $(LDFLAGS) test/test.o -o test/libtest.so + +test/test.o: test/test.c + @$(RUBY) -rftools -e 'File.mkpath(*ARGV)' test + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +test:: dl.so libtest.so .force + $(RUBY) -I. -I$(srcdir)/lib $(srcdir)/test/test.rb + +.force: + +.PHONY: .force test + +allclean: distclean + @rm -f $(CLEANFILES) $(DISTCLEANFILES) + +$(OBJS): dlconfig.h + +sym.o: call.func + +dl.o: callback.func cbtable.func + +call.func: mkcall.rb dlconfig.rb + @echo "Generating call.func" + @$(RUBY) $< > $@ + +callback.func: mkcallback.rb dlconfig.rb + @echo "Generating callback.func" + @$(RUBY) $< > $@ + +cbtable.func: mkcbtable.rb dlconfig.rb + @echo "Generating cbtable.func" + @$(RUBY) $< > $@ + +debug: + $(MAKE) CFLAGS+=-DDEBUG diff --git a/ext/dl/dl.c b/ext/dl/dl.c new file mode 100644 index 000000000..b8e7381f8 --- /dev/null +++ b/ext/dl/dl.c @@ -0,0 +1,655 @@ +/* + * $Id$ + */ + +#include <ruby.h> +#include <rubyio.h> +#include "dl.h" + +VALUE rb_mDL; +VALUE rb_eDLError; +VALUE rb_eDLTypeError; + +static VALUE DLFuncTable; +static void *rb_dl_func_table[MAX_CALLBACK_TYPE][MAX_CALLBACK]; +static ID id_call; + +#include "callback.func" + +static void +init_dl_func_table(){ +#include "cbtable.func" +}; + +void * +dlmalloc(size_t size) +{ + DEBUG_CODE2({ + void *ptr; + + printf("dlmalloc(%d)",size); + ptr = xmalloc(size); + printf(":0x%x\n",ptr); + return ptr; + }, + { + return xmalloc(size); + }); +}; + +void * +dlrealloc(void *ptr, size_t size) +{ + DEBUG_CODE({ + printf("dlrealloc(0x%x,%d)\n",ptr,size); + }); + return xrealloc(ptr, size); +}; + +void +dlfree(void *ptr) +{ + DEBUG_CODE({ + printf("dlfree(0x%x)\n",ptr); + }); + xfree(ptr); +}; + +char* +dlstrdup(const char *str) +{ + char *newstr; + + newstr = (char*)dlmalloc(strlen(str)); + strcpy(newstr,str); + + return newstr; +}; + +size_t +dlsizeof(const char *cstr) +{ + size_t size; + int i, len, n, dlen; + char *d; + + len = strlen(cstr); + size = 0; + for( i=0; i<len; i++ ){ + n = 1; + if( isdigit(cstr[i+1]) ){ + dlen = 1; + while( isdigit(cstr[i+dlen]) ){ dlen ++; }; + dlen --; + d = ALLOCA_N(char, dlen + 1); + strncpy(d, cstr + i + 1, dlen); + d[dlen] = '\0'; + n = atoi(d); + } + else{ + dlen = 0; + }; + + switch( cstr[i] ){ + case 'I': + DLALIGN(0,size,INT_ALIGN); + case 'i': + size += sizeof(int) * n; + break; + case 'L': + DLALIGN(0,size,LONG_ALIGN); + case 'l': + size += sizeof(long) * n; + break; + case 'F': + DLALIGN(0,size,FLOAT_ALIGN); + case 'f': + size += sizeof(float) * n; + break; + case 'D': + DLALIGN(0,size,DOUBLE_ALIGN); + case 'd': + size += sizeof(double) * n; + break; + case 'C': + case 'c': + size += sizeof(char) * n; + break; + case 'H': + DLALIGN(0,size,SHORT_ALIGN); + case 'h': + size += sizeof(short) * n; + break; + case 'P': + DLALIGN(0,size,VOIDP_ALIGN); + case 'p': + size += sizeof(void*) * n; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type '%c'", cstr[i]); + break; + }; + i += dlen; + }; + + return size; +}; + +static float * +c_farray(VALUE v, long *size) +{ + int i, len; + float *ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(float) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_FLOAT: + ary[i] = (float)(RFLOAT(e)->value); + break; + case T_NIL: + ary[i] = 0.0; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +static double * +c_darray(VALUE v, long *size) +{ + int i, len; + double *ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(double) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_FLOAT: + ary[i] = (double)(RFLOAT(e)->value); + break; + case T_NIL: + ary[i] = 0.0; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +static long * +c_larray(VALUE v, long *size) +{ + int i, len; + long *ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(long) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_FIXNUM: + case T_BIGNUM: + ary[i] = (long)(NUM2INT(e)); + break; + case T_NIL: + ary[i] = 0; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +static int * +c_iarray(VALUE v, long *size) +{ + int i, len; + int *ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(int) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_FIXNUM: + case T_BIGNUM: + ary[i] = (int)(NUM2INT(e)); + break; + case T_NIL: + ary[i] = 0; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +static short * +c_harray(VALUE v, long *size) +{ + int i, len; + short *ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(short) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_FIXNUM: + case T_BIGNUM: + ary[i] = (short)(NUM2INT(e)); + break; + case T_NIL: + ary[i] = 0; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +static char * +c_carray(VALUE v, long *size) +{ + int i, len; + char *ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(char) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_FIXNUM: + case T_BIGNUM: + ary[i] = (char)(NUM2INT(e)); + break; + case T_NIL: + ary[i] = 0; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +static void * +c_parray(VALUE v, long *size) +{ + int i, len; + void **ary; + VALUE e; + + len = RARRAY(v)->len; + *size = sizeof(void*) * len; + ary = dlmalloc(*size); + for( i=0; i < len; i++ ){ + e = rb_ary_entry(v, i); + switch( TYPE(e) ){ + case T_STRING: + { + char *str, *src; + src = STR2CSTR(e); + str = dlstrdup(src); + ary[i] = (void*)str; + }; + break; + case T_NIL: + ary[i] = NULL; + break; + case T_DATA: + if( rb_obj_is_kind_of(e, rb_cDLPtrData) ){ + struct ptr_data *pdata; + Data_Get_Struct(e, struct ptr_data, pdata); + ary[i] = (void*)(pdata->ptr); + } + else{ + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + }; + break; + default: + rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i); + break; + }; + }; + + return ary; +}; + +void * +rb_ary2cary(char t, VALUE v, long *size) +{ + int len; + VALUE val0; + + if( TYPE(v) != T_ARRAY ){ + rb_raise(rb_eDLTypeError, "an array is expected."); + }; + + len = RARRAY(v)->len; + if( len == 0 ){ + return NULL; + }; + + if( !size ){ + size = ALLOCA_N(long,1); + }; + + val0 = rb_ary_entry(v,0); + switch( TYPE(val0) ){ + case T_FIXNUM: + case T_BIGNUM: + switch( t ){ + case 'C': case 'c': + return (void*)c_carray(v,size); + case 'H': case 'h': + return (void*)c_harray(v,size); + case 'I': case 'i': + return (void*)c_iarray(v,size); + case 'L': case 'l': case 0: + return (void*)c_larray(v,size); + default: + rb_raise(rb_eDLTypeError, "type mismatch"); + }; + case T_STRING: + return (void*)c_parray(v,size); + case T_FLOAT: + switch( t ){ + case 'F': case 'f': + return (void*)c_farray(v,size); + case 'D': case 'd': case 0: + return (void*)c_darray(v,size); + }; + rb_raise(rb_eDLTypeError, "type mismatch"); + case T_DATA: + if( rb_obj_is_kind_of(val0, rb_cDLPtrData) ){ + return (void*)c_parray(v,size); + }; + rb_raise(rb_eDLTypeError, "type mismatch"); + default: + rb_raise(rb_eDLTypeError, "unsupported type"); + }; +}; + +VALUE +rb_str_to_ptr(VALUE self) +{ + char *ptr; + int len; + + len = RSTRING(self)->len; + ptr = (char*)dlmalloc(len + 1); + memcpy(ptr, STR2CSTR(self), len); + ptr[len] = '\0'; + return rb_dlptr_new((void*)ptr,len,dlfree); +}; + +VALUE +rb_ary_to_ptr(int argc, VALUE argv[], VALUE self) +{ + void *ptr; + VALUE t; + long size; + + switch( rb_scan_args(argc, argv, "01", &t) ){ + case 1: + ptr = rb_ary2cary(STR2CSTR(t)[0], self, &size); + break; + case 0: + ptr = rb_ary2cary(0, self, &size); + break; + }; + return ptr ? rb_dlptr_new(ptr, size, dlfree) : Qnil; +}; + +VALUE +rb_io_to_ptr(VALUE self) +{ + OpenFile *fptr; + FILE *fp; + + GetOpenFile(self, fptr); + fp = fptr->f; + + return fp ? rb_dlptr_new(fp, sizeof(FILE), 0) : Qnil; +}; + +VALUE +rb_dl_dlopen(int argc, VALUE argv[], VALUE self) +{ + return rb_dlhandle_s_new(argc, argv, rb_cDLHandle); +}; + +VALUE +rb_dl_malloc(VALUE self, VALUE size) +{ + void *ptr; + long s; + + s = DLNUM2LONG(size); + ptr = dlmalloc((size_t)s); + memset(ptr,0,(size_t)s); + return rb_dlptr_new(ptr, s, dlfree); +}; + +VALUE +rb_dl_strdup(VALUE self, VALUE str) +{ + void *p; + + str = rb_String(str); + return rb_dlptr_new(strdup(STR2CSTR(str)), RSTRING(str)->len, dlfree); +}; + +static VALUE +rb_dl_sizeof(VALUE self, VALUE str) +{ + return INT2NUM(dlsizeof(STR2CSTR(str))); +}; + +static VALUE +rb_dl_callback_type(VALUE str) +{ + char *type; + int len; + int i; + long ftype; + + ftype = 0; + type = STR2CSTR(str); + len = RSTRING(str)->len; + + if( len - 1 > MAX_CBARG ){ + rb_raise(rb_eDLError, "maximum number of the argument is %d.", MAX_CBARG); + }; + + for( i = len - 1; i > 0; i-- ){ + switch( type[i] ){ + case 'P': + CBPUSH_P(ftype); + break; + case 'I': + CBPUSH_I(ftype); + break; + case 'L': + CBPUSH_L(ftype); + break; + case 'F': + CBPUSH_F(ftype); + break; + case 'D': + CBPUSH_D(ftype); + default: + rb_raise(rb_eDLError, "unsupported type `%c'", type[i]); + break; + }; + }; + + switch( type[0] ){ + case '0': + CBPUSH_0(ftype); + break; + case 'P': + CBPUSH_P(ftype); + break; + case 'I': + CBPUSH_I(ftype); + break; + case 'L': + CBPUSH_L(ftype); + break; + case 'F': + CBPUSH_F(ftype); + break; + case 'D': + CBPUSH_D(ftype); + break; + default: + rb_raise(rb_eDLError, "unsupported type `%c'", type[i]); + break; + }; + + return INT2NUM(ftype); +}; + +VALUE +rb_dl_set_callback(int argc, VALUE argv[], VALUE self) +{ + VALUE types, num, proc; + VALUE key; + VALUE entry; + void *func; + + char func_name[1024]; + extern dln_sym(); + + switch( rb_scan_args(argc, argv, "21", &types, &num, &proc) ){ + case 2: + proc = rb_f_lambda(); + break; + case 3: + break; + default: + rb_bug("rb_dl_set_callback"); + }; + + key = rb_dl_callback_type(types); + entry = rb_hash_aref(DLFuncTable, key); + if( entry == Qnil ){ + entry = rb_hash_new(); + rb_hash_aset(DLFuncTable, key, entry); + }; + + func = rb_dl_func_table[NUM2INT(key)][NUM2INT(num)]; + if( func ){ + rb_hash_aset(entry, num, proc); + snprintf(func_name, 1023, "rb_dl_func%d_%d", NUM2INT(key), NUM2INT(num)); + return rb_dlsym_new(func, func_name, STR2CSTR(types)); + } + else{ + return Qnil; + }; +}; + +VALUE +rb_dl_get_callback(VALUE self, VALUE types, VALUE num) +{ + VALUE key; + VALUE entry; + + key = rb_dl_callback_type(types); + entry = rb_hash_aref(DLFuncTable, key); + if( entry == Qnil ){ + return Qnil; + }; + return rb_hash_aref(entry, num); +}; + +void +Init_dl() +{ + void Init_dlptr(); + void Init_dlsym(); + void Init_dlhandle(); + + id_call = rb_intern("call"); + + rb_mDL = rb_define_module("DL"); + + rb_eDLError = rb_define_class_under(rb_mDL, "DLError", rb_eStandardError); + rb_eDLTypeError = rb_define_class_under(rb_mDL, "DLTypeError", rb_eDLError); + + DLFuncTable = rb_hash_new(); + init_dl_func_table(); + rb_define_const(rb_mDL, "FuncTable", DLFuncTable); + + rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL)); + rb_define_const(rb_mDL, "RTLD_LAZY", INT2NUM(RTLD_LAZY)); + rb_define_const(rb_mDL, "RTLD_NOW", INT2NUM(RTLD_NOW)); + + rb_define_const(rb_mDL, "ALIGN_INT", INT2NUM(ALIGN_INT)); + rb_define_const(rb_mDL, "ALIGN_LONG", INT2NUM(ALIGN_LONG)); + rb_define_const(rb_mDL, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT)); + rb_define_const(rb_mDL, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT)); + rb_define_const(rb_mDL, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE)); + rb_define_const(rb_mDL, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP)); + + rb_define_const(rb_mDL, "VERSION", rb_tainted_str_new2(DL_VERSION)); + rb_define_const(rb_mDL, "MAJOR_VERSION", INT2NUM(DL_MAJOR_VERSION)); + rb_define_const(rb_mDL, "MINOR_VERSION", INT2NUM(DL_MINOR_VERSION)); + rb_define_const(rb_mDL, "PATCH_VERSION", INT2NUM(DL_PATCH_VERSION)); + rb_define_const(rb_mDL, "MAX_ARG", INT2NUM(MAX_ARG)); + rb_define_const(rb_mDL, "MAX_CBARG", INT2NUM(MAX_CBARG)); + rb_define_const(rb_mDL, "MAX_CBENT", INT2NUM(MAX_CBENT)); + + rb_define_module_function(rb_mDL, "dlopen", rb_dl_dlopen, -1); + rb_define_module_function(rb_mDL, "set_callback", rb_dl_set_callback, -1); + rb_define_module_function(rb_mDL, "get_callback", rb_dl_get_callback, 2); + rb_define_module_function(rb_mDL, "malloc", rb_dl_malloc, 1); + rb_define_module_function(rb_mDL, "strdup", rb_dl_strdup, 1); + rb_define_module_function(rb_mDL, "sizeof", rb_dl_sizeof, 1); + + Init_dlptr(); + Init_dlsym(); + Init_dlhandle(); + + rb_define_const(rb_mDL, "FREE", rb_dlsym_new(dlfree, "free", "0P")); + + rb_define_method(rb_cString, "to_ptr", rb_str_to_ptr, 0); + rb_define_method(rb_cArray, "to_ptr", rb_ary_to_ptr, -1); + rb_define_method(rb_cIO, "to_ptr", rb_io_to_ptr, 0); +}; diff --git a/ext/dl/dl.def b/ext/dl/dl.def new file mode 100644 index 000000000..75da180a7 --- /dev/null +++ b/ext/dl/dl.def @@ -0,0 +1,70 @@ +EXPORTS +Init_dl +dlfree +dlmalloc +dlrealloc +dlstrdup +rb_ary2cary +rb_ary_to_ptr +rb_dl_dlopen +rb_dl_get_callback +rb_dl_malloc +rb_dl_set_callback +rb_dl_strdup +rb_eDLError +rb_eDLTypeError +rb_io_to_ptr +rb_mDL +rb_str_to_ptr +Init_dlhandle +dlhandle_free +rb_cDLHandle +rb_dlhandle_close +rb_dlhandle_disable_close +rb_dlhandle_enable_close +rb_dlhandle_init +rb_dlhandle_s_new +rb_dlhandle_sym +Init_dlptr +dlptr_free +dlptr_init +rb_cDLPtrData +rb_dlmem_each +rb_dlptr2cptr +rb_dlptr_alloc +rb_dlptr_aref +rb_dlptr_aset +rb_dlptr_cmp +rb_dlptr_define_data_type +rb_dlptr_define_struct +rb_dlptr_define_union +rb_dlptr_eql +rb_dlptr_free_get +rb_dlptr_free_set +rb_dlptr_get_data_type +rb_dlptr_inspect +rb_dlptr_minus +rb_dlptr_new +rb_dlptr_null_p +rb_dlptr_plus +rb_dlptr_ptr +rb_dlptr_ref +rb_dlptr_to_array +rb_dlptr_to_i +rb_dlptr_to_s +rb_dlptr_to_str +rb_mDLMemorySpace +Init_dlsym +dlsym_free +rb_cDLSymbol +rb_dlsym2csym +rb_dlsym_call +rb_dlsym_cproto +rb_dlsym_initialize +rb_dlsym_inspect +rb_dlsym_name +rb_dlsym_new +rb_dlsym_proto +rb_dlsym_s_new +rb_dlsym_to_ptr +rb_s_dlsym_char2type diff --git a/ext/dl/dl.h b/ext/dl/dl.h new file mode 100644 index 000000000..27290fc12 --- /dev/null +++ b/ext/dl/dl.h @@ -0,0 +1,282 @@ +/* -*- C -*- + * $Id$ + */ + +#ifndef RUBY_DL_H +#define RUBY_DL_H + +#include <ruby.h> +#include <dlconfig.h> + +#if defined(HAVE_DLFCN_H) +# include <dlfcn.h> +#else +# if defined(HAVE_WINDOWS_H) +# include <windows.h> +# define dlclose(ptr) FreeLibrary((HINSTANCE)ptr) +# define dlopen(name,flag) ((void*)LoadLibrary(name)) +# define dlerror() "unknown error" +# define dlsym(handle,name) ((void*)GetProcAddress(handle,name)) +# define RTLD_LAZY -1 +# define RTLD_NOW -1 +# define RTLD_GLOBAL -1 +# endif +#endif + +#ifdef DEBUG +#define DEBUG_CODE(b) {printf("DEBUG:%d\n",__LINE__);b;} +#define DEBUG_CODE2(b1,b2) {printf("DEBUG:%d\n",__LINE__);b1;} +#else +#define DEBUG_CODE(b) +#define DEBUG_CODE2(b1,b2) b2 +#endif + +#define DL_VERSION "1.1.0" +#define DL_MAJOR_VERSION 1 +#define DL_MINOR_VERSION 1 +#define DL_PATCH_VERSION 0 + +#define VOID_DLTYPE 0x00 +#define CHAR_DLTYPE 0x01 +#define SHORT_DLTYPE 0x02 +#define INT_DLTYPE 0x03 +#define LONG_DLTYPE 0x04 +#define FLOAT_DLTYPE 0x05 +#define DOUBLE_DLTYPE 0x06 +#define VOIDP_DLTYPE 0x07 + +#define ARG_TYPE(x,i) (((x) & (0x07 << ((i)*3))) >> ((i)*3)) +#define PUSH_ARG(x,t) do{x <<= 3; x |= t;}while(0) +#define PUSH_0(x) PUSH_ARG(x,VOID_DLTYPE) + +#if SIZEOF_INT == SIZEOF_LONG +# define PUSH_I(x) PUSH_ARG(x,LONG_DLTYPE) +# define ANY2I(x) x.l +# define DLINT(x) (long)x +#else +# define PUSH_I(x) PUSH_ARG(x,INT_DLTYPE) +# define ANY2I(x) x.i +# define DLINT(x) (int)x +#endif +#define PUSH_L(x) PUSH_ARG(x,LONG_DLTYPE) +#define ANY2L(x) x.l +#define DLLONG(x) (long)x + +#if defined(WITH_TYPE_FLOAT) +# if SIZEOF_FLOAT == SIZEOF_DOUBLE +# define PUSH_F(x) PUSH_ARG(x,DOUBLE_DLTYPE) +# define ANY2F(x) (x.d) +# define DLFLOAT(x) ((double)x) +# else +# define PUSH_F(x) PUSH_ARG(x,FLOAT_DLTYPE) +# define ANY2F(x) (x.f) +# define DLFLOAT(x) ((float)x) +# endif +#else +# define PUSH_F(x) PUSH_ARG(x,DOUBLE_DLTYPE) +# define ANY2F(x) (x.d) +# define DLFLOAT(x) ((double)x) +#endif +#define PUSH_D(x) PUSH_ARG(x,DOUBLE_DLTYPE) +#define ANY2D(x) (x.d) +#define DLDOUBLE(x) ((double)x) + +#if SIZEOF_INT == SIZEOF_VOIDP && SIZEOF_INT != SIZEOF_LONG +# define PUSH_P(x) PUSH_ARG(x,INT_DLTYPE) +# define ANY2P(x) (x.i) +# define DLVOIDP(x) ((int)x) +#elif SIZEOF_LONG == SIZEOF_VOIDP +# define PUSH_P(x) PUSH_ARG(x,LONG_DLTYPE) +# define ANY2P(x) (x.l) +# define DLVOIDP(x) ((long)x) +#else +# define PUSH_P(x) PUSH_ARG(x,VOIDP_DLTYPE) +# define ANY2P(x) (x.p) +# define DLVOIDP(x) ((void*)p) +#endif + +#if defined(WITH_TYPE_CHAR) +# define PUSH_C(x) PUSH_ARG(x,CHAR_DLTYPE) +# define ANY2C(x) (x.c) +# define DLCHAR(x) ((char)x) +#else +# define PUSH_C(x) PUSH_I(x) +# define ANY2C(x) ANY2I(x) +# define DLCHAR(x) DLINT(x) +#endif + +#if defined(WITH_TYPE_SHORT) +# define PUSH_H(x) PUSH_ARG(x,SHORT_DLTYPE) +# define ANY2H(x) (x.h) +# define DLSHORT(x) ((short)x) +#else +# define PUSH_H(x) PUSH_I(x) +# define ANY2H(x) ANY2I(x) +# define DLSHORT(x) DLINT(x) +#endif + +#define PUSH_S(x) PUSH_P(x) +#define ANY2S(x) ANY2P(x) +#define DLSTR(x) DLVOIDP(x) + +#define CBPUSH_0(x) PUSH_0(x) +#define CBPUSH_C(x) PUSH_C(x) +#define CBPUSH_H(x) PUSH_H(x) +#define CBPUSH_I(x) PUSH_I(x) +#define CBPUSH_L(x) PUSH_L(x) +#define CBPUSH_F(x) PUSH_F(x) +#define CBPUSH_D(x) PUSH_D(x) +#if defined(WITH_CBTYPE_VOIDP) +# define CBPUSH_P(x) PUSH_ARG(x,VOIDP_DLTYPE) +#else +# define CBPUSH_P(x) PUSH_P(x) +#endif + + +#if defined(USE_INLINE_ASM) +# if defined(__i386__) && defined(__GNUC__) +# define ASM_START(type) +# define ASM_END(type) +# define ASM_PUSH_C(x) asm volatile ("pushl %0" :: "g" (x)); +# define ASM_PUSH_H(x) asm volatile ("pushl %0" :: "g" (x)); +# define ASM_PUSH_I(x) asm volatile ("pushl %0" :: "g" (x)); +# define ASM_PUSH_L(x) asm volatile ("pushl %0" :: "g" (x)); +# define ASM_PUSH_P(x) asm volatile ("pushl %0" :: "g" (x)); +# define ASM_PUSH_F(x) asm volatile ("flds %0"::"g"(x));\ + asm volatile ("subl $4,%esp");\ + asm volatile ("fstps (%esp)"); +# define ASM_PUSH_D(x) asm volatile ("fldl %0"::"g"(x));\ + asm volatile ("subl $8,%esp");\ + asm volatile ("fstpl (%esp)") +# else +# error --with-asm is not supported on this machine +# endif +#else +# define ASM_START(type) +# define ASM_END(type) +# define ASM_PUSH_C(x) +# define ASM_PUSH_I(x) +# define ASM_PUSH_L(x) +# define ASM_PUSH_P(x) +# define ASM_PUSH_F(x) +# define ASM_PUSH_D(x) +#endif + +extern VALUE rb_mDL; +extern VALUE rb_mDLMemorySpace; +extern VALUE rb_cDLHandle; +extern VALUE rb_cDLSymbol; +extern VALUE rb_cDLPtrData; +extern VALUE rb_cDLStructData; + +extern VALUE rb_eDLError; +extern VALUE rb_eDLTypeError; + +#if defined(LONG2NUM) && (SIZEOF_LONG == SIZEOF_VOIDP) +# define DLLONG2NUM(x) LONG2NUM((long)x) +# define DLNUM2LONG(x) (long)(NUM2LONG(x)) +#else +# define DLLONG2NUM(x) INT2NUM((long)x) +# define DLNUM2LONG(x) (long)(NUM2INT(x)) +#endif + +typedef struct { char c; void *x; } s_voidp; +typedef struct { char c; short x; } s_short; +typedef struct { char c; int x; } s_int; +typedef struct { char c; long x; } s_long; +typedef struct { char c; float x; } s_float; +typedef struct { char c; double x; } s_double; + +#define ALIGN_VOIDP (sizeof(s_voidp) - sizeof(void *)) +#define ALIGN_SHORT (sizeof(s_short) - sizeof(short)) +#define ALIGN_INT (sizeof(s_int) - sizeof(int)) +#define ALIGN_LONG (sizeof(s_long) - sizeof(long)) +#define ALIGN_FLOAT (sizeof(s_float) - sizeof(float)) +#define ALIGN_DOUBLE (sizeof(s_double) - sizeof(double)) + +/* for compatibility */ +#define VOIDP_ALIGN ALIGN_VOIDP +#define SHORT_ALIGN ALIGN_SHORT +#define INT_ALIGN ALIGN_INT +#define LONG_ALIGN ALIGN_LONG +#define FLOAT_ALIGN ALIGN_FLOAT +#define DOUBLE_ALIGN ALIGN_DOUBLE + +#define DLALIGN(ptr,offset,align) {\ + while( (((unsigned long)(ptr + offset)) % align) != 0 ) offset++;\ +} + +typedef void (*freefunc_t)(void *); +#define DLFREEFUNC(func) ((freefunc_t)(func)) + +typedef union { + void* p; + char c; + short h; + int i; + long l; + float f; + double d; + char *s; +} ANY_TYPE; + +struct dl_handle { + void *ptr; + int open; + int enable_close; +}; + +struct sym_data { + void *func; + char *name; + char *type; + int len; +}; + +enum DLPTR_CTYPE { + DLPTR_CTYPE_UNKNOWN, + DLPTR_CTYPE_STRUCT, + DLPTR_CTYPE_UNION +}; + +struct ptr_data { + void *ptr; /* a pointer to the data */ + freefunc_t free; /* free() */ + char *stype; /* array of type specifiers */ + int *ssize; /* size[i] = sizeof(type[i]) > 0 */ + int slen; /* the number of type specifiers */ + ID *ids; + int ids_num; + int ctype; /* DLPTR_CTYPE_UNKNOWN, DLPTR_CTYPE_STRUCT, DLPTR_CTYPE_UNION */ + long size; +}; + +#define RDLPTR(obj) ((struct ptr_data *)(DATA_PTR(obj))) +#define RDLSYM(obj) ((struct sym_data *)(DATA_PTR(obj))) + +void dlfree(void*); +void *dlmalloc(size_t); +void *dlrealloc(void*,size_t); +char *dlstrdup(const char *); +size_t dlsizeof(const char *); + +void *rb_ary2cary(char t, VALUE ary, long *size); + +/* +void rb_dlmem_delete(void *ptr); +void rb_dlmem_aset(void *ptr, VALUE obj); +VALUE rb_dlmem_aref(void *ptr); +*/ + +void dlptr_free(struct ptr_data *data); +void dlptr_init(VALUE val); + +VALUE rb_dlptr_new(void *ptr, long size, freefunc_t func); +VALUE rb_dlptr_alloc(long size, freefunc_t func); +void *rb_dlptr2cptr(VALUE val); + +VALUE rb_dlsym_new(void (*func)(), const char *name, const char *type); +freefunc_t rb_dlsym2csym(VALUE val); + + +#endif /* RUBY_DL_H */ diff --git a/ext/dl/extconf.rb b/ext/dl/extconf.rb new file mode 100644 index 000000000..372f1b138 --- /dev/null +++ b/ext/dl/extconf.rb @@ -0,0 +1,197 @@ +require 'mkmf' +$:.unshift File.dirname(__FILE__) +require 'type' + +if( ARGV.include?("--help") ) + print <<EOF + --help print this messages + --with-type-char strictly use type 'char' + --with-type-short strictly use type 'short' + --with-type-float strictly use type 'float' + --with-asm use the embedded assembler for passing arguments. + (this option is available for i386 machine now.) + --with-args=<max_arg>,<max_cbarg>,<max_cbent> + <max_arg>: maximum number of arguments of the function + <max_cbarg>: maximum number of arguments of the callback + <max_cbent>: maximum number of callback entries +EOF + exit(0) +end + +($CPPFLAGS || $CFLAGS) << " -I." + +case RUBY_PLATFORM # from Win32API +when /cygwin/,/mingw/ + $CFLAGS << " -fno-defer-pop -fno-omit-frame-pointer" +end + +if (Config::CONFIG['CC'] =~ /gcc/) && (Config::CONFIG['arch'] =~ /i.86/) + $with_asm = true +else + $with_asm = false +end + +$with_type_int = try_run(<<EOF) +int main(){ return sizeof(int) == sizeof(long); } +EOF + +$with_type_float = try_run(<<EOF) +int main(){ return sizeof(float) == sizeof(double); } +EOF + +$with_type_voidp = try_run(<<EOF) +int main(){ + return (sizeof(void *) == sizeof(long)) + || (sizeof(void *) == sizeof(int)); +} +EOF + +$with_type_char = DLTYPE[CHAR][:sym] +$with_type_short = DLTYPE[SHORT][:sym] +$with_type_long = DLTYPE[LONG][:sym] +$with_type_double= DLTYPE[DOUBLE][:sym] +$with_type_int &= DLTYPE[INT][:sym] +$with_type_float &= DLTYPE[FLOAT][:sym] +$with_type_voidp &= DLTYPE[VOIDP][:sym] + +$with_cbtype_voidp = DLTYPE[VOIDP][:cb] + +$with_type_char = with_config("type-char") ? true : false +$with_type_short = with_config("type-short") ? true : false +$with_type_float = with_config("type-float") ? true : false + +$with_asm = with_config("asm") ? true : $with_asm + +args = with_config("args") +max_arg = max_cbarg = max_cbent = nil +if( $with_asm ) + $with_type_char = true + $with_type_short = true + $with_type_float = true + max_arg = 0 +end +if( args ) + max_arg,max_cbarg,max_cbent = args.split(",").collect{|c| c.to_i} + if( !(max_arg && max_cbarg && max_cbent) ) + print("--with-args=<max_arg>,<max_cbarg>,<max_cbent>\n") + exit(1) + end +end +max_arg ||= 6 +max_cbarg ||= 3 +max_cbent ||= 3 + +max_callback_type = types2num(DLTYPE.keys.sort[-1,1] * (max_cbarg + 1)) + 1 +max_callback = max_cbent + +#m = [1].pack("i") +#c,cs = m.unpack("c") +#bigendian = (c == 0) +#print("bigendian ... #{bigendian ? 'true' : 'false'}\n") + + +$dlconfig_h = <<EOF +#define MAX_ARG #{max_arg} +#define MAX_CBARG #{max_cbarg} +#define MAX_CBENT #{max_cbent} +#define MAX_CALLBACK_TYPE #{max_callback_type} +#define MAX_CALLBACK #{max_callback} +EOF + +def dlc_define(const) + $dlconfig_h << "#if !defined(#{const})\n" + + "# define #{const}\n" + + "#endif\n" +end + +if( $with_asm ) + $dlconfig_h << "#define USE_INLINE_ASM\n" +end +if( $with_type_char ) + $dlconfig_h << "#define WITH_TYPE_CHAR\n" +end +if( $with_type_short ) + $dlconfig_h << "#define WITH_TYPE_SHORT\n" +end +if( $with_type_long ) + $dlconfig_h << "#define WITH_TYPE_LONG\n" +end +if( $with_type_double ) + $dlconfig_h << "#define WITH_TYPE_DOUBLE\n" +end +if( $with_type_float ) + $dlconfig_h << "#define WITH_TYPE_FLOAT\n" +end +if( $with_type_int ) + $dlconfig_h << "#define WITH_TYPE_INT\n" +end +if( $with_type_voidp ) + $dlconfig_h << "#define WITH_TYPE_VOIDP\n" +end +if( $with_cbtype_voidp ) + $dlconfig_h << "#define WITH_CBTYPE_VOIDP\n" +end +#if( bigendian ) +# $dlconfig_h << "#define BIGENDIAN" +#else +# $dlconfig_h << "#define LITTLEENDIAN" +#end + + +if( have_header("dlfcn.h") ) + dlc_define("HAVE_DLFCN_H") + have_library("dl") + have_func("dlopen") + have_func("dlclose") + have_func("dlsym") + if( have_func("dlerror") ) + dlc_define("HAVE_DLERROR") + end +elsif( have_header("windows.h") ) + dlc_define("HAVE_WINDOWS_H") + have_func("LoadLibrary") + have_func("FreeLibrary") + have_func("GetProcAddress") +else + exit(0) +end + +method(:have_func).arity == 1 or have_func("rb_str_cat2", "ruby.h") +if method(:have_func).arity == 1 or !have_func("rb_block_given_p", "ruby.h") + $dlconfig_h << "#define rb_block_given_p rb_iterator_p\n" +end + +def File.update(file, str) + begin + open(file){|f|f.read} == str + rescue Errno::ENOENT + false + end or open(file, "w"){|f|f.print(str)} +end + +File.update("dlconfig.h", <<EOF) +#ifndef DLCONFIG_H +#define DLCONFIG_H +#{$dlconfig_h} +#endif /* DLCONFIG_H */ +EOF + +File.update("dlconfig.rb", <<EOF) +MAX_ARG = #{max_arg} +MAX_CBARG = #{max_cbarg} +MAX_CBENT = #{max_cbent} +DLTYPE[CHAR][:sym] = #{$with_type_char} +DLTYPE[SHORT][:sym] = #{$with_type_short} +DLTYPE[INT][:sym] = #{$with_type_int} +DLTYPE[LONG][:sym] = #{$with_type_long} +DLTYPE[FLOAT][:sym] = #{$with_type_float} +DLTYPE[DOUBLE][:sym]= #{$with_type_double} +DLTYPE[VOIDP][:sym] = #{$with_type_voidp} +EOF + +$INSTALLFILES = [ + ["./dlconfig.h", "$(archdir)$(target_prefix)", "."], + ["dl.h", "$(archdir)$(target_prefix)", ""], +] + +create_makefile('dl') diff --git a/ext/dl/h2rb b/ext/dl/h2rb new file mode 100644 index 000000000..00fbd60c8 --- /dev/null +++ b/ext/dl/h2rb @@ -0,0 +1,500 @@ +#!/usr/bin/env ruby +# -*- ruby -*- +# $Id$ + +require 'mkmf' +require 'ftools' + +$recursive = false +$force = false +$conly = true +$inc_path = [] +$infilename= nil +$insert_require = true + +def valid_ruby_code?(code) + begin + eval("BEGIN {return true}; #{code}") + rescue SyntaxError + return false + end + return false +end + +def print_usage + print <<EOF +h2rb [-r] [-I <path>] [-d] [<filename>] +EOF +end + +while( ARGV[0] ) + case( ARGV[0] ) + when "-r" + ARGV.shift + $recursive = true + when "-R" + ARGV.shift + $recursive = false + when "-l" + ARGV.shift + $insert_require = true + when "-L" + ARGV.shift + $insert_require = false + when "-c" + ARGV.shift + $conly = true + when "-C" + ARGV.shift + $conly = false + when "-f" + ARGV.shift + $force = true + when "-F" + ARGV.shift + $force = false + when "-I" + ARGV.shift + $inc_path << ARGV.shift + when "-d" + ARGV.shift + $DEBUG = true + when "-h","--help" + print_usage() + exit 0 + when /-.*/ + $stderr.print("unknown option '#{ARGV[0]}'.\n") + print_usage() + exit 0 + else + $infilename = ARGV.shift + end +end + +$inc_dir = File.join(CONFIG["prefix"], "lib", "ruby", + CONFIG["MAJOR"] + "." + CONFIG["MINOR"], + "dl") + +class H2RBError < StandardError; end + + +class H2RB + def initialize(inc_dir = nil, inc_path = nil, insert_require = nil) + @inc_path = inc_path || [] + @inc_dir = inc_dir || '.' + @indent = 0 + @parsed_files = [] + @insert_require = insert_require || false + end + + def find_path(file) + if( ! file ) + return nil + end + if( File.exist?(file) ) + if( file[0] == ?/ ) + return file + else + return file + end + end + @inc_path.each{|path| + full = File.join(path, file) + if( File.exist?(full) ) + return full + end + } + return nil + end + + def strip_comment(line) + if( @commented ) + if( e = line.index("*/") ) + line[0..(e+1)] = "" + @commented = false + else + line = "" + end + else + if( s = line.index("/*") ) + if( e = line.index("*/") ) + line[s..(e+1)] = "" + else + line[s..-1] = "" + @commented = true + end + elsif( s = line.index("//") ) + line[s..(-1)] = "" + end + end + + line.gsub!(/\s+$/,"") + return line + end + + def up_indent + @indent += 1 + end + + def down_indent + @indent -= 1 + if( @indent < 0 ) + raise + end + end + + def indent + " " * @indent + end + + def rescue_begin + line = "#{indent}begin" + up_indent + return line + end + + def rescue_nameerror + down_indent + line = [ + "#{indent}rescue NameError => e", + "#{indent} raise e if( $DEBUG )", + "#{indent}end"].join($/) + return line + end + + def parse_enum(line) + if( line =~ /enum\s+(\S+\s+)?\{(.+)\}/ ) + enum_name = $1 + enum_block = $2 + if( enum_name ) + line = "#{indent}# -- enum #{enum_name}\n" + else + line = "#{indent}# -- enum\n" + end + enums = enum_block.split(/,/).collect{|e| e.strip} + i = 0 + enums.each{|elem| + var,val = elem.split(/=/).collect{|e| e.strip} + if( val ) + i = val.to_i + end + line += "#{indent}#{var} = #{i.to_s}\n" + i += 1 + } + line += "#{indent}# -- end of enum" + return line + else + return nil + end + end + + def parse_define(line) + case line + when /^#\s*define\s+(\S+)\(\)/ + line = nil + when /^#\s*define\s+(\S+)\((.+)\)\s+(.+)$/ + if( @conly ) + line = nil + else + defname = $1 + defargs = $2 + defval = $3 + if( !valid_ruby_code?(defval) ) + defval = "nil # #{defval}" + end + if( defname[0,1] =~ /^[A-Z]$/ ) + line = "#{indent}#{defname} = proc{|#{defargs}| #{defval}}" + else + line = [ + "#{indent}def #{defname}(#{defargs})", + "#{indent} #{defval}", + "#{indent}end" + ].join("\n") + end + end + when /^#\s*define\s+(\S+)\((.+)\)$/ + if( @conly ) + line = nil + else + defname = $1 + defargs = $2 + defval = nil + if( !valid_ruby_code?(defval) ) + defval = "nil # #{defval}" + end + if( defname[0,1] =~ /^[A-Z]$/ ) + line = "#{indent}#{defname} = proc{|#{defargs}| #{defval}}" + else + line = [ + "#{indent}def #{defname}(#{defargs})", + "#{indent} #{defval}", + "#{indent}end" + ].join("\n") + end + end + when /^#\s*define\s+(\S+)\s+(.+)$/ + defname = $1 + defval = $2 + if( !valid_ruby_code?(defval) ) + defval = "nil # #{defval}" + end + line = [rescue_begin, "#{indent}#{defname} = #{defval}", rescue_nameerror].join($/) + when /^#\s*define\s+(\S+)$/ + defname = $1 + line = "#{indent}#{defname} = nil" + else + line = nil + end + return line + end + + def parse_undef(line) + case line + when /^#\s*undef\s+([A-Z]\S+)$/ + defname = $1 + line = "#{indent}remove_const(:#{defname})" + when /^#\s*undef\s+(\S+)$/ + defname = $1 + line = "#{indent}#{defname} = nil" + else + line = nil + end + return line + end + + def parse_ifdef(line) + case line + when /^#\s*ifdef\s+(\S+)$/ + defname = $1 + line = [ + rescue_begin, + "#{indent}if( defined?(#{defname}) && ! #{defname}.nil? )"].join($/) + else + line = nil + end + return line + end + + def parse_ifndef(line) + case line + when /^#\s*ifndef\s+(\S+)$/ + defname = $1 + line = [ + rescue_begin, + "#{indent}if( ! defined?(#{defname}) || #{defname}.nil? )"].join($/) + else + line = nil + end + return line + end + + def parse_if(line) + case line + when /^#\s*if\s+(.+)$/ + cond = $1 + cond.gsub!(/defined(.+)/){ "defined?(#{$1}) && ! #{$1}.nil?" } + if( valid_ruby_code?(cond) ) + line = "#{indent}if( #{cond} )" + else + line = "#{indent}if( false ) # #{cond}" + end + line = [rescue_begin, line].join($/) + else + line = nil + end + return line + end + + def parse_elif(line) + case line + when /^#\s*elif\s+(.+)$/ + cond = $1 + cond.gsub!("defined","defined?") + line = "#{indent}elsif( #{cond} )" + else + line = nil + end + return line + end + + def parse_else(line) + case line + when /^#\s*else\s*/ + line = "#{indent}else" + else + line = nil + end + return line + end + + def parse_endif(line) + case line + when /^#\s*endif\s*$/ + line = ["#{indent}end", rescue_nameerror].join($/) + else + line = nil + end + return line + end + + def parse_include(line) + if( ! @insert_require ) + return nil + end + + file = nil + case line + when /^#\s*include "(.+)"$/ + file = $1 + line = "#{indent}require '#{file}'" + when /^#\s*include \<(.+)\>$/ + file = $1 + line = "#{indent}require '#{file}'" + else + line = nil + end + if( @recursive && file && (!@parsed_files.include?(file)) ) + parse(file, @recursive, @force, @conly) + end + return line + end + + + def open_files(infilename) + if( ! infilename ) + return [$stdin, $stdout] + end + + old_infilename = infilename + infilename = find_path(infilename) + if( ! infilename ) + $stderr.print("'#{old_infilename}' was not found.\n") + return [nil,nil] + end + + if( infilename ) + if( infilename[0,1] == '/' ) + outfilename = File.join(@inc_dir, infilename[1..-1] + ".rb") + else + outfilename = infilename + ".rb" + end + File.mkpath(File.dirname(outfilename)) + else + outfilename = nil + end + + if( infilename ) + fin = File.open(infilename,"r") + else + fin = $stdin + end + if( outfilename ) + if( File.exist?(outfilename) && (!@force) ) + $stderr.print("'#{outfilename}' have already existed.\n") + return [fin, nil] + end + fout = File.open(outfilename,"w") + else + fout = $stdout + end + + $stderr.print("#{infilename} -> #{outfilename}\n") + if( fout ) + dir = File.dirname(outfilename) + if( dir[0,1] != "." && dir != "" ) + fout.print("if( ! $LOAD_PATH.include?('#{dir}') )\n", + " $LOAD_PATH.push('#{dir}')\n", + "end\n") + end + end + return [fin,fout] + end + + def parse(infilename = nil, recursive = false, force = false, conly = false) + @commented = false + @recursive = recursive + @force = force + @conly = conly + @parsed_files << infilename + + fin,fout = open_files(infilename) + if( !fin ) + return + end + + begin + line_number = 0 + pre_line = nil + fin.each_line{|line| + line_number += 1 + line.chop! + if( $DEBUG ) + $stderr.print("#{line_number}:(#{@indent}):", line, "\n") + end + + if( pre_line ) + line = pre_line + line + pre_line = nil + end + + if( line[-1,1] == "\\" ) + pre_line = line[0..-2] + next + end + + if( eidx = line.index("enum ") ) + pre_line = line[eidx .. -1] + if( i = line.index("{") && j = line.index("}") ) + line = line[0..j] + pre_line = nil + else + next + end + end + + line = strip_comment(line) + case line + when /^enum\s/ + line = parse_enum(line) + when /^#\s*define\s/ + line = parse_define(line) + when /^#\s*undef\s/ + line = parse_undef(line) + when /^#\s*ifdef\s/ + line = parse_ifdef(line) + up_indent + when /^#\s*ifndef\s/ + line = parse_ifndef(line) + up_indent + when /^#\s*if\s/ + line = parse_if(line) + up_indent + when /^#\s*elif\s/ + down_indent + line = parse_elif(line) + up_indent + when /^#\s*else/ + down_indent + line = parse_else(line) + up_indent + when /^#\s*endif/ + down_indent + line = parse_endif(line) + when /^#\s*include\s/ + line = parse_include(line) + else + line = nil + end + if( line && fout ) + fout.print(line, " # #{line_number}",$/) + end + } + ensure + fin.close if fin + fout.close if fout + end + end +end + +h2rb = H2RB.new($inc_dir, $inc_path, $insert_require) +h2rb.parse($infilename, $recursive, $force, $conly) diff --git a/ext/dl/handle.c b/ext/dl/handle.c new file mode 100644 index 000000000..d75cb11a8 --- /dev/null +++ b/ext/dl/handle.c @@ -0,0 +1,207 @@ +/* -*- C -*- + * $Id$ + */ + +#include <ruby.h> +#include "dl.h" + +VALUE rb_cDLHandle; + +void +dlhandle_free(struct dl_handle *dlhandle) +{ + if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){ + dlclose(dlhandle->ptr); + }; +}; + +VALUE +rb_dlhandle_close(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + dlhandle->open = 0; + return INT2NUM(dlclose(dlhandle->ptr)); +}; + +VALUE +rb_dlhandle_s_new(int argc, VALUE argv[], VALUE self) +{ + void *ptr; + VALUE val; + struct dl_handle *dlhandle; + VALUE lib, flag; + char *clib; + int cflag; + const char *err; + + switch( rb_scan_args(argc, argv, "11", &lib, &flag) ){ + case 1: + clib = STR2CSTR(lib); + cflag = RTLD_LAZY | RTLD_GLOBAL; + break; + case 2: + clib = STR2CSTR(lib); + cflag = NUM2INT(flag); + break; + default: + rb_bug("rb_dlhandle_new"); + }; + + ptr = dlopen(clib, cflag); +#if defined(HAVE_DLERROR) + if( (err = dlerror()) ){ + rb_raise(rb_eRuntimeError, err); + }; +#else + if( !ptr ){ + err = dlerror(); + rb_raise(rb_eRuntimeError, err); + }; +#endif + val = Data_Make_Struct(rb_cDLHandle, struct dl_handle, 0, + dlhandle_free, dlhandle); + dlhandle->ptr = ptr; + dlhandle->open = 1; + dlhandle->enable_close = 0; + + rb_obj_call_init(val, argc, argv); + + if( rb_block_given_p() ){ + rb_ensure(rb_yield, val, rb_dlhandle_close, val); + }; + + return val; +}; + +VALUE +rb_dlhandle_init(int argc, VALUE argv[], VALUE self) +{ + return Qnil; +}; + +VALUE +rb_dlhandle_enable_close(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + dlhandle->enable_close = 1; + return Qnil; +}; + +VALUE +rb_dlhandle_disable_close(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + dlhandle->enable_close = 0; + return Qnil; +}; + +VALUE +rb_dlhandle_to_i(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + return DLLONG2NUM(dlhandle); +}; + +VALUE +rb_dlhandle_to_ptr(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + return rb_dlptr_new(dlhandle, sizeof(dlhandle), 0); +}; + +VALUE +rb_dlhandle_sym(int argc, VALUE argv[], VALUE self) +{ + VALUE sym, type; + void (*func)(); + VALUE val; + struct sym_data *data; + int *ctypes; + int i, ctypes_len; + struct dl_handle *dlhandle; + void *handle; + const char *name, *stype; + const char *err; + + if( rb_scan_args(argc, argv, "11", &sym, &type) == 2 ){ + Check_Type(type, T_STRING); + stype = STR2CSTR(type); + } + else{ + stype = NULL; + }; + + if( sym == Qnil ){ +#if defined(RTLD_NEXT) + name = RTLD_NEXT; +#else + name = NULL; +#endif + } + else{ + Check_Type(sym, T_STRING); + name = STR2CSTR(sym); + }; + + + Data_Get_Struct(self, struct dl_handle, dlhandle); + handle = dlhandle->ptr; + + func = dlsym(handle, name); +#if defined(HAVE_DLERROR) + if( (err = dlerror()) && (!func) ) +#else + if( !func ) +#endif + { +#if defined(__CYGWIN__) || defined(WIN32) || defined(__MINGW32__) + { + int len = strlen(name); + char *name_a = (char*)dlmalloc(len+2); + strcpy(name_a, name); + name_a[len] = 'A'; + name_a[len+1] = '\0'; + func = dlsym(handle, name_a); + dlfree(name_a); +#if defined(HAVE_DLERROR) + if( (err = dlerror()) && (!func) ) +#else + if( !func ) +#endif + { + rb_raise(rb_eRuntimeError, "Unknown symbol \"%sA\".", name); + }; + } +#else + rb_raise(rb_eRuntimeError, "Unknown symbol \"%s\".", name); +#endif + }; + val = rb_dlsym_new(func, name, stype); + + return val; +}; + +void +Init_dlhandle() +{ + rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cData); + rb_define_singleton_method(rb_cDLHandle, "new", rb_dlhandle_s_new, -1); + rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_init, -1); + rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0); + rb_define_method(rb_cDLHandle, "to_ptr", rb_dlhandle_to_ptr, 0); + rb_define_method(rb_cDLHandle, "close", rb_dlhandle_close, 0); + rb_define_method(rb_cDLHandle, "sym", rb_dlhandle_sym, -1); + rb_define_method(rb_cDLHandle, "[]", rb_dlhandle_sym, -1); + rb_define_method(rb_cDLHandle, "disable_close", rb_dlhandle_disable_close, 0); + rb_define_method(rb_cDLHandle, "enable_close", rb_dlhandle_enable_close, 0); +}; diff --git a/ext/dl/install.rb b/ext/dl/install.rb new file mode 100644 index 000000000..69b183430 --- /dev/null +++ b/ext/dl/install.rb @@ -0,0 +1,49 @@ +require 'mkmf' +require 'ftools' + +SO_LIBS = ["dl.so"] + +$ruby_version = CONFIG['MAJOR'] + "." + CONFIG['MINOR'] +$prefix = CONFIG['prefix'] +$libdir = File.join($prefix,'lib') +$rubylibdir = File.join($libdir, 'ruby', $ruby_version) +$arch = CONFIG['arch'] +$archdir = File.join($rubylibdir, $arch) + +def find(dir, match = /./) + Dir.chdir(dir) + files = [] + Dir.new(".").each{|file| + if( file != "." && file != ".." ) + case File.ftype(file) + when "file" + if( file =~ match ) + files.push(File.join(dir,file)) + end + when "directory" + files += find(file, match).collect{|f| File.join(dir,f)} + end + end + } + Dir.chdir("..") + return files +end + +def install() + rb_files = find(File.join(".","lib"), /.rb$/) + + SO_LIBS.each{|f| + File.makedirs($rubylibdir, "#{$archdir}") + File.install(f, File.join($archdir,f), 0555, true) + } + + rb_files.each{|f| + origfile = f + instfile = File.join($rubylibdir, origfile.sub("./lib/","")) + instdir = File.dirname(instfile) + File.makedirs(instdir) + File.install(origfile, instfile, 0644, true) + } +end + +install() diff --git a/ext/dl/lib/dl/import.rb b/ext/dl/lib/dl/import.rb new file mode 100644 index 000000000..78f6a2b78 --- /dev/null +++ b/ext/dl/lib/dl/import.rb @@ -0,0 +1,228 @@ +# -*- ruby -*- + +require 'dl' + +module DL + module Importable + LIB_MAP = {} + + module Internal + def dlload(*libnames) + if( !defined?(@LIBS) ) + @LIBS = [] + end + libnames.each{|libname| + if( !LIB_MAP[libname] ) + LIB_MAP[libname] = DL.dlopen(libname) + end + @LIBS.push(LIB_MAP[libname]) + } + end + alias dllink :dlload + + # example: + # extern "int strlen(char*)" + # + def extern(proto) + proto = proto.gsub(/\s+/, " ").strip + case proto + when /^([\d\w\*_\s]+)\(([\d\w\*_\s\,\[\]]*)\)$/ + ret = $1 + args = $2 + ret = ret.split(/\s+/) + args = args.split(/\s*,\s*/) + func = ret.pop + ret = ret.join(" ") + return import(func, ret, args) + else + raise(RuntimeError,"can't parse the function prototype: #{proto}") + end + end + + # example: + # import("get_length", "int", ["void*", "int"]) + # + def import(name, rettype, argtypes = nil) + if( !defined?(@SYM) ) + @SYM = {} + end + @LIBS.each{|lib| + rty,_,rdec = encode_type(rettype) + ty,enc,dec = encode_types(argtypes) + symty = rty + ty + + begin + sym = lib[name, symty] + rescue + next + end + + mname = name.dup + if( ?A <= mname[0] && mname[0] <= ?Z ) + mname[0,1] = mname[0,1].downcase + end + @SYM[mname] = [sym,rdec,enc,dec] + + module_eval [ + "def #{mname}(*args)", + " sym,rdec,enc,dec = @SYM['#{mname}']", + " args = enc.call(args)", + if( $DEBUG ) + " p \"[DL] call #{mname} with \#{args.inspect}\"" + else + "" + end, + " r,rs = sym.call(*args)", + if( $DEBUG ) + " p \"[DL] retval=\#{r.inspect} args=\#{rs.inspect}\"" + else + "" + end, + " @retval = rdec.call(r)", + " @args = dec.call(rs)", + " return @retval", + "end", + "module_function :#{mname}", + ].join("\n") + + return @SYM[mname] + } + raise(RuntimeError, "can't find #{name}.") + end + + def _args_ + return @args + end + + def _retval_ + return @retval + end + + def typealias(ty1, ty2, enc=nil, dec=nil) + check_type + @TYDEFS.unshift([ty1,ty2, enc,dec]) + end + + def encode_type(ty) + check_type + orig_ty = ty + enc = proc{|v| v} + dec = proc{|v| v} + @TYDEFS.each{|t1,t2,c1,c2| + if( t1.is_a?(String) ) + t1 = Regexp.new("^" + t1 + "$") + end + if( ty =~ t1 ) + ty = ty.gsub(t1,t2) + if( c1 ) + conv1 = enc + enc = proc{|v| c1.call(conv1.call(v))} + end + if( c2 ) + conv2 = dec + dec = proc{|v| c2.call(conv2.call(v))} + end + end + } + ty = ty.strip + if( ty.length != 1 ) + raise(TypeError, "unknown type: #{orig_ty}.") + end + return [ty,enc,dec] + end + + def encode_types(tys) + encty = [] + enc = proc{|v| v} + dec = proc{|v| v} + tys.each_with_index{|ty,idx| + ty,c1,c2 = encode_type(ty) + encty.push(ty) + conv1 = enc + enc = proc{|v| v = conv1.call(v); v[idx] = c1.call(v[idx]); v} + conv2 = dec + dec = proc{|v| v = conv2.call(v); v[idx] = c2.call(v[idx]); v} + } + return [encty.join, enc, dec] + end + + def check_type + if( !defined?(@TYDEFS) ) + init_type + end + end + + def init_type + @TYDEFS = [ + # for Windows + ["DWORD", "unsigned long", nil, nil], + ["PDWORD", "unsigned long *", nil, nil], + ["WORD", "unsigned int", nil, nil], + ["PWORD", "unsigned int *", nil, nil], + ["BOOL", "ibool", nil, nil], + ["ATOM", "int", nil, nil], + ["BYTE", "unsigned char", nil, nil], + ["PBYTE", "unsigned char *", nil, nil], + ["UINT", "unsigned int", nil, nil], + ["ULONG", "unsigned long", nil, nil], + ["UCHAR", "unsigned char", nil, nil], + ["HANDLE", "unsigned long", nil, nil], + ["PHANDLE","void*", nil, nil], + ["PVOID", "void*", nil, nil], + ["LPCSTR", "char*", nil, nil], + + # Others + ["uint", "unsigned int", nil, nil], + ["u_int", "unsigned int", nil, nil], + ["ulong", "unsigned long", nil, nil], + ["u_long", "unsigned long", nil, nil], + + # DL::Importable primitive types + ["ibool", "I", + proc{|v| v ? 1 : 0}, + proc{|v| (v != 0) ? true : false}], + ["cbool", "C", + proc{|v| v ? 1 : 0}, + proc{|v| (v != 0) ? true : false}], + ["lbool", "L", + proc{|v| v ? 1 : 0}, + proc{|v| (v != 0) ? true : false}], + ["unsigned char", "I", + proc{|v| [v].pack("C").unpack("c")[0]}, + proc{|v| [v].pack("c").unpack("C")[0]}], + ["unsigned int", "I", + proc{|v| [v].pack("I").unpack("i")[0]}, + proc{|v| [v].pack("i").unpack("I")[0]}], + ["unsigned long", "L", + proc{|v| [v].pack("L").unpack("l")[0]}, + proc{|v| [v].pack("l").unpack("L")[0]}], + ["unsigned char ref", "i", + proc{|v| [v].pack("C").unpack("c")[0]}, + proc{|v| [v].pack("c").unpack("C")[0]}], + ["unsigned int ref", "i", + proc{|v| [v].pack("I").unpack("i")[0]}, + proc{|v| [v].pack("i").unpack("I")[0]}], + ["unsigned long ref", "l", + proc{|v| [v].pack("L").unpack("l")[0]}, + proc{|v| [v].pack("l").unpack("L")[0]}], + ["char ref", "c", nil, nil], + ["short ref", "h", nil, nil], + ["int ref", "i", nil, nil], + ["long ref", "l", nil, nil], + ["float ref", "f", nil, nil], + ["double ref","d", nil, nil], + ["char", "C", nil, nil], + ["short", "H", nil, nil], + ["int", "I", nil, nil], + ["long", "L", nil, nil], + ["float", "F", nil, nil], + ["double", "D", nil, nil], + [/.+\*/, "P", nil, nil], + [/.+\[\]/, "a", nil, nil], + ["void", "0", nil, nil], + ] + end + end # end of Internal + include Internal + end # end of Importable +end diff --git a/ext/dl/lib/dl/win32.rb b/ext/dl/lib/dl/win32.rb new file mode 100644 index 000000000..b507be5fd --- /dev/null +++ b/ext/dl/lib/dl/win32.rb @@ -0,0 +1,26 @@ +# -*- ruby -*- + +require 'dl' + +class Win32API + LIBRARY = {} + + attr_reader :val, :args + + def initialize(lib, func, args, ret) + LIBRARY[lib] ||= DL.dlopen(lib) + ty = (ret + args).tr('V','0') + @sym = LIBRARY[lib].sym(func, ty) + @__dll__ = LIBRARY[lib].to_i + @__dllname__ = lib + @__proc__ = @sym.to_i + @val = nil + @args = [] + end + + def call(*args) + @val,@args = @sym.call(*args) + return @val + end + alias Call call +end diff --git a/ext/dl/mkcall.rb b/ext/dl/mkcall.rb new file mode 100644 index 000000000..dad101744 --- /dev/null +++ b/ext/dl/mkcall.rb @@ -0,0 +1,68 @@ +# -*- ruby -*- + +require 'mkmf' +$:.unshift File.dirname(__FILE__) +require 'type' +require 'dlconfig' + +$int_eq_long = try_run(<<EOF) +int main() { + return sizeof(int) == sizeof(long) ? 0 : 1; +} +EOF + +def output_arg(x,i) + "args[#{i}].#{DLTYPE[x][:stmem]}" +end + +def output_args(types) + t = [] + types[1..-1].each_with_index{|x,i| t.push(output_arg(x,i))} + t.join(",") +end + +def output_callfunc(types) + t = types[0] + stmem = DLTYPE[t][:stmem] + ctypes = types2ctypes(types) + if( t == VOID ) + callstm = "(*f)(#{output_args(types)})" + else + callstm = "ret.#{stmem} = (*f)(#{output_args(types)})" + end + [ "{", + "#{ctypes[0]} (*f)(#{ctypes[1..-1].join(',')}) = func;", + "#{callstm};", + "}"].join(" ") +end + +def output_case(types) + num = types2num(types) + callfunc_stm = output_callfunc(types) +<<EOF + case #{num}: +#ifdef DEBUG + printf("#{callfunc_stm}\\n"); +#endif + #{callfunc_stm}; + break; +EOF +end + +def rec_output(types = [VOID]) + print output_case(types) + if( types.length <= MAX_ARG ) + DLTYPE.keys.sort.each{|t| + if( t != VOID && DLTYPE[t][:sym] ) + rec_output(types + [t]) + end + } + end +end + +DLTYPE.keys.sort.each{|t| + if( DLTYPE[t][:sym] ) + $stderr.printf(" #{DLTYPE[t][:ctype]}\n") + rec_output([t]) + end +} diff --git a/ext/dl/mkcallback.rb b/ext/dl/mkcallback.rb new file mode 100644 index 000000000..32c7b451d --- /dev/null +++ b/ext/dl/mkcallback.rb @@ -0,0 +1,83 @@ +# -*- ruby -*- + +require 'mkmf' +$:.unshift File.dirname(__FILE__) +require 'type' +require 'dlconfig' + +$int_eq_long = try_run(<<EOF) +int main() { + return sizeof(int) == sizeof(long) ? 0 : 1; +} +EOF + +def func_arg(x,i) + ctype = DLTYPE[x][:ctype] + "#{ctype} arg#{i}" +end + +def func_args(types) + t = [] + types[1..-1].each_with_index{|x,i| t.push(func_arg(x,i))} + t.join(", ") +end + +def funcall_args(types) + num = types.length - 1 + if( num > 0 ) + t = [] + types[1..-1].each_with_index{|x,i| t.push(DLTYPE[x][:c2rb].call("arg#{i}"))} + return num.to_s + ", " + t.join(", ") + else + return num.to_s + end +end + +def output_func(types, n = 0) + func_name = "rb_dl_func#{types2num(types)}_#{n}" + code = + "#{func_name}(#{func_args(types)}) /* #{types2ctypes(types).inspect} */\n" + + "{\n" + + " VALUE val, obj;\n" + + "#ifdef DEBUG\n" + + " printf(\"#{func_name}()\\n\");\n" + + "#endif\n" + + " obj = rb_hash_aref(DLFuncTable, INT2NUM(#{types2num(types)}));\n" + + " obj = rb_hash_aref(obj,INT2NUM(#{n}));\n" + + " val = rb_funcall(obj, id_call,\n" + + " #{funcall_args(types)});\n" + + rtype = DLTYPE[types[0]][:ctype] + rcode = DLTYPE[types[0]][:rb2c] + if( rcode ) + code += " return #{rcode.call('val')};\n" + end + + code = + rtype + "\n" + + code + + "}\n\n" + if( n < MAX_CBENT - 1) + return code + output_func(types, n+1) + else + return code + end +end + + +def rec_output(types = [VOID]) + print output_func(types) + if( types.length <= MAX_CBARG ) + DLTYPE.keys.sort.each{|t| + if( t != VOID && DLTYPE[t][:cb] ) + rec_output(types + [t]) + end + } + end +end + +DLTYPE.keys.sort.each{|t| + if( DLTYPE[t][:cb] ) + rec_output([t]) + end +} diff --git a/ext/dl/mkcbtable.rb b/ext/dl/mkcbtable.rb new file mode 100644 index 000000000..f25f012e4 --- /dev/null +++ b/ext/dl/mkcbtable.rb @@ -0,0 +1,42 @@ +# -*- ruby -*- + +require 'mkmf' +$:.unshift File.dirname(__FILE__) +require 'type' +require 'dlconfig' + +$int_eq_long = try_run(<<EOF) +int main() { + return sizeof(int) == sizeof(long) ? 0 : 1; +} +EOF + +def output_func(types, n = 0) + code = + "/* #{types2ctypes(types).inspect} */\n" + + "rb_dl_func_table[#{types2num(types)}][#{n}] " + + "= rb_dl_func#{types2num(types)}_#{n};\n" + if( n < MAX_CBENT - 1) + return code + output_func(types, n+1) + else + return code + end +end + + +def rec_output(types = [VOID]) + print output_func(types) + if( types.length <= MAX_CBARG ) + DLTYPE.keys.sort.each{|t| + if( t != VOID && DLTYPE[t][:cb] ) + rec_output(types + [t]) + end + } + end +end + +DLTYPE.keys.sort.each{|t| + if( DLTYPE[t][:cb] ) + rec_output([t]) + end +} diff --git a/ext/dl/ptr.c b/ext/dl/ptr.c new file mode 100644 index 000000000..6ac214bc4 --- /dev/null +++ b/ext/dl/ptr.c @@ -0,0 +1,1075 @@ +/* -*- C -*- + * $Id$ + */ + +#include <ruby.h> +#include <version.h> /* for ruby version code */ +#include "dl.h" + +VALUE rb_cDLPtrData; +VALUE rb_mDLMemorySpace; +static VALUE DLMemoryTable; + +#ifndef T_SYMBOL +# define T_SYMBOL T_FIXNUM +#endif + +#if RUBY_VERSION_CODE < 171 +static VALUE +rb_hash_delete(VALUE hash, VALUE key) +{ + return rb_funcall(hash, rb_intern("delete"), 1, key); +} +#endif + +static void +rb_dlmem_delete(void *ptr) +{ + rb_hash_delete(DLMemoryTable, DLLONG2NUM(ptr)); +}; + +static void +rb_dlmem_aset(void *ptr, VALUE obj) +{ + if( obj == Qnil ){ + rb_dlmem_delete(ptr); + } + else{ + rb_hash_aset(DLMemoryTable, DLLONG2NUM(ptr), DLLONG2NUM(obj)); + }; +}; + +static VALUE +rb_dlmem_aref(void *ptr) +{ + VALUE val; + + val = rb_hash_aref(DLMemoryTable, DLLONG2NUM(ptr)); + return val == Qnil ? Qnil : (VALUE)DLNUM2LONG(val); +}; + +void +dlptr_free(struct ptr_data *data) +{ + if( data->ptr ){ + DEBUG_CODE({ + printf("dlptr_free(): removing the pointer `0x%x' from the MemorySpace\n", + data->ptr); + }); + rb_dlmem_delete(data->ptr); + if( data->free ){ + DEBUG_CODE({ + printf("dlptr_free(): 0x%x(data->ptr:0x%x)\n",data->free,data->ptr); + }); + (*(data->free))(data->ptr); + }; + }; + if( data->stype ) dlfree(data->stype); + if( data->ssize ) dlfree(data->ssize); + if( data->ids ) dlfree(data->ids); +}; + +void +dlptr_init(VALUE val) +{ + struct ptr_data *data; + + Data_Get_Struct(val, struct ptr_data, data); + DEBUG_CODE({ + printf("dlptr_init(): add the pointer `0x%x' to the MemorySpace\n", + data->ptr); + }); + rb_dlmem_aset(data->ptr, val); +}; + +VALUE +rb_dlptr_new(void *ptr, long size, freefunc_t func) +{ + struct ptr_data *data; + VALUE val; + + if( ptr ){ + val = rb_dlmem_aref(ptr); + if( val == Qnil ){ + val = Data_Make_Struct(rb_cDLPtrData, struct ptr_data, + 0, dlptr_free, data); + data->ptr = ptr; + data->free = func; + data->ctype = DLPTR_CTYPE_UNKNOWN; + data->stype = NULL; + data->ssize = NULL; + data->slen = 0; + data->size = size; + data->ids = NULL; + data->ids_num = 0; + dlptr_init(val); + } + else{ + if( func ){ + Data_Get_Struct(val, struct ptr_data, data); + data->free = func; + }; + }; + } + else{ + val = Qnil; + }; + + return val; +}; + +VALUE +rb_dlptr_alloc(long size, freefunc_t func) +{ + return rb_dlptr_new(dlmalloc((size_t)size), size, func); +}; + +void * +rb_dlptr2cptr(VALUE val) +{ + struct ptr_data *data; + void *ptr; + + if( rb_obj_is_kind_of(val, rb_cDLPtrData) ){ + Data_Get_Struct(val, struct ptr_data, data); + ptr = data->ptr; + } + else if( val == Qnil ){ + ptr = NULL; + } + else{ + rb_raise(rb_eTypeError, "DL::PtrData was expected"); + }; + + return ptr; +}; + +static VALUE +rb_dlptr_s_new(int argc, VALUE argv[], VALUE klass) +{ + VALUE ptr, sym, obj, size; + void *p = NULL; + void (*f)() = NULL; + long s = 0; + + switch( rb_scan_args(argc, argv, "12", &ptr, &size, &sym) ){ + case 1: + p = (void*)(DLNUM2LONG(rb_Integer(ptr))); + break; + case 2: + p = (void*)(DLNUM2LONG(rb_Integer(ptr))); + s = DLNUM2LONG(size); + break; + case 3: + p = (void*)(DLNUM2LONG(rb_Integer(ptr))); + s = DLNUM2LONG(size); + f = rb_dlsym2csym(sym); + break; + default: + rb_bug("rb_dlptr_s_new"); + }; + + obj = rb_dlptr_new(p,s,f); + + rb_obj_call_init(obj, argc, argv); + + return obj; +}; + +static VALUE +rb_dlptr_s_alloc(int argc, VALUE argv[], VALUE klass) +{ + VALUE size, sym, obj; + int s; + void (*f)() = NULL; + + switch( rb_scan_args(argc, argv, "11", &size, &sym) ){ + case 1: + s = NUM2INT(size); + break; + case 2: + s = NUM2INT(size); + f = rb_dlsym2csym(sym); + break; + default: + rb_bug("rb_dlptr_s_new"); + }; + + obj = rb_dlptr_alloc(s,f); + + rb_obj_call_init(obj, argc, argv); + + return obj; +}; + +static VALUE +rb_dlptr_init(int argc, VALUE argv[], VALUE self) +{ + return Qnil; +}; + +static VALUE +rb_dlptr_cast(int argc, VALUE argv[], VALUE self) +{ + VALUE klass, rest, val, *pass_argv; + int pass_argc, i; + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + rb_scan_args(argc, argv, "1*", &klass, &rest); + + /* prepare the arguments of `new' method */ + pass_argc = argc + 1; + pass_argv = ALLOCA_N(VALUE, pass_argc); + pass_argv[0] = DLLONG2NUM(data->ptr); + pass_argv[1] = rb_dlsym_new(data->free, NULL, NULL); + for( i=2; i < pass_argc; i++ ){ + pass_argv[i] = rb_ary_entry(rest,i-2); + }; + + /* remove the data */ + ((struct ptr_data *)(RDATA(self)->data))->ptr = 0; + (RDATA(self)->dfree)(RDATA(self)->data); + + /* call the `new' method of klass with prepared arguments */ + val = rb_funcall2(klass, rb_intern("new"), pass_argc, pass_argv); + + RDATA(self)->basic.klass = RDATA(val)->basic.klass; + RDATA(self)->basic.flags = RDATA(val)->basic.flags; + RDATA(self)->dmark = RDATA(val)->dmark; + RDATA(self)->dfree = RDATA(val)->dfree; + RDATA(self)->data = RDATA(val)->data; + + RDATA(val)->dmark = 0; + RDATA(val)->dfree = 0; + + return Qnil; +}; + +VALUE +rb_dlptr_to_i(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return DLLONG2NUM(data->ptr); +}; + +VALUE +rb_dlptr_ptr(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return rb_dlptr_new(*((void**)(data->ptr)),0,0); +}; + +VALUE +rb_dlptr_ref(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return rb_dlptr_new(&(data->ptr),0,0); +}; + +VALUE +rb_dlptr_null_p(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return data->ptr ? Qfalse : Qtrue; +}; + +VALUE +rb_dlptr_free_set(VALUE self, VALUE val) +{ + struct ptr_data *data; + int i; + + Data_Get_Struct(self, struct ptr_data, data); + + data->free = DLFREEFUNC(rb_dlsym2csym(val)); + + return Qnil; +}; + +VALUE +rb_dlptr_free_get(VALUE self) +{ + struct ptr_data *pdata; + + Data_Get_Struct(self, struct ptr_data, pdata); + + return rb_dlsym_new(pdata->free,"(free)","0P"); +}; + +VALUE +rb_dlptr_to_array(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + int n; + int i; + int t; + VALUE ary; + VALUE type, size; + + Data_Get_Struct(self, struct ptr_data, data); + + switch( rb_scan_args(argc, argv, "11", &type, &size) ){ + case 2: + t = STR2CSTR(type)[0]; + n = NUM2INT(size); + break; + case 1: + t = STR2CSTR(type)[0]; + switch( t ){ + case 'C': + n = data->size; + break; + case 'H': + n = data->size / sizeof(short); + break; + case 'I': + n = data->size / sizeof(int); + break; + case 'L': + n = data->size / sizeof(long); + break; + case 'F': + n = data->size / sizeof(float); + break; + case 'D': + n = data->size / sizeof(double); + break; + case 'S': case 'P': + n = data->size / sizeof(void*); + break; + default: + if( t == 'p' || t == 's' ){ + int i; + for( i=0; ((void**)(data->ptr))[i]; i++ ){}; + n = i; + } + else{ + n = 0; + }; + }; + break; + default: + rb_bug("rb_dlptr_to_array"); + }; + + ary = rb_ary_new(); + + for( i=0; i < n; i++ ){ + switch( t ){ + case 'C': + rb_ary_push(ary, INT2NUM(((char*)(data->ptr))[i])); + break; + case 'H': + rb_ary_push(ary, INT2NUM(((short*)(data->ptr))[i])); + break; + case 'I': + rb_ary_push(ary, INT2NUM(((int*)(data->ptr))[i])); + break; + case 'L': + rb_ary_push(ary, DLLONG2NUM(((long*)(data->ptr))[i])); + break; + case 'D': + rb_ary_push(ary, rb_float_new(((double*)(data->ptr))[i])); + case 'F': + rb_ary_push(ary, rb_float_new(((float*)(data->ptr))[i])); + break; + case 'S': + { + char *str = ((char**)(data->ptr))[i]; + if( str ){ + rb_ary_push(ary, rb_tainted_str_new2(str)); + } + else{ + rb_ary_push(ary, Qnil); + }; + }; + break; + case 's': + { + char *str = ((char**)(data->ptr))[i]; + if( str ){ + rb_ary_push(ary, rb_tainted_str_new2(str)); + xfree(str); + } + else{ + rb_ary_push(ary, Qnil); + }; + }; + break; + case 'P': + rb_ary_push(ary, rb_dlptr_new(((void**)(data->ptr))[i],0,0)); + break; + case 'p': + rb_ary_push(ary, + rb_dlptr_new(((void**)(data->ptr))[i],0,dlfree)); + break; + }; + }; + + return ary; +}; + + +VALUE +rb_dlptr_to_s(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + VALUE arg1, val; + int len; + + Data_Get_Struct(self, struct ptr_data, data); + switch( rb_scan_args(argc, argv, "01", &arg1) ){ + case 0: + val = rb_tainted_str_new2((char*)(data->ptr)); + break; + case 1: + len = NUM2INT(arg1); + val = rb_tainted_str_new((char*)(data->ptr), len); + break; + default: + rb_bug("rb_dlptr_to_s"); + }; + + return val; +}; + +VALUE +rb_dlptr_to_str(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + VALUE arg1, val; + int len; + + Data_Get_Struct(self, struct ptr_data, data); + switch( rb_scan_args(argc, argv, "01", &arg1) ){ + case 0: + val = rb_tainted_str_new((char*)(data->ptr),data->size); + break; + case 1: + len = NUM2INT(arg1); + val = rb_tainted_str_new((char*)(data->ptr), len); + break; + default: + rb_bug("rb_dlptr_to_str"); + }; + + return val; +}; + +VALUE +rb_dlptr_inspect(VALUE self) +{ + struct ptr_data *data; + char str[1024]; + VALUE name; + + Data_Get_Struct(self, struct ptr_data, data); + snprintf(str, 1023, "#<%s:0x%x ptr=0x%x size=%ld free=0x%x>", + rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free); + return rb_str_new2(str); +}; + +VALUE +rb_dlptr_eql(VALUE self, VALUE other) +{ + void *ptr1, *ptr2; + ptr1 = rb_dlptr2cptr(self); + ptr2 = rb_dlptr2cptr(other); + + return ptr1 == ptr2 ? Qtrue : Qfalse; +}; + +VALUE +rb_dlptr_cmp(VALUE self, VALUE other) +{ + void *ptr1, *ptr2; + ptr1 = rb_dlptr2cptr(self); + ptr2 = rb_dlptr2cptr(other); + return DLLONG2NUM((long)ptr1 - (long)ptr2); +}; + +VALUE +rb_dlptr_plus(VALUE self, VALUE other) +{ + void *ptr; + long num, size; + + ptr = rb_dlptr2cptr(self); + size = RDLPTR(self)->size; + num = DLNUM2LONG(other); + return rb_dlptr_new((void*)(ptr + num), size - num, 0); +}; + +VALUE +rb_dlptr_minus(VALUE self, VALUE other) +{ + void *ptr; + long num, size; + + ptr = rb_dlptr2cptr(self); + size = RDLPTR(self)->size; + num = DLNUM2LONG(other); + return rb_dlptr_new((void*)(ptr - num), size + num, 0); +}; + +VALUE +rb_dlptr_define_data_type(int argc, VALUE argv[], VALUE self) +{ + VALUE data_type, type, rest, vid; + struct ptr_data *data; + int i, t, len, num; + char *ctype; + long size; + + rb_scan_args(argc, argv, "11*", &data_type, &type, &rest); + Data_Get_Struct(self, struct ptr_data, data); + + if( argc == 1 || (argc == 2 && type == Qnil) ){ + if( NUM2INT(data_type) == DLPTR_CTYPE_UNKNOWN ){ + data->ctype = DLPTR_CTYPE_UNKNOWN; + data->slen = 0; + data->ids_num = 0; + if( data->stype ){ + dlfree(data->stype); + data->stype = NULL; + }; + if( data->ids ){ + dlfree(data->ids); + data->ids = NULL; + }; + return Qnil; + } + else{ + raise(rb_eArgError, "wrong arguments"); + }; + }; + + Check_Type(data_type, T_FIXNUM); + Check_Type(type, T_STRING); + Check_Type(rest, T_ARRAY); + t = FIX2INT(data_type); + num = RARRAY(rest)->len; + for( i=0; i<num; i++ ){ + vid = rb_ary_entry(rest,i); + if( !(TYPE(vid)==T_STRING || TYPE(vid)==T_SYMBOL) ){ + rb_raise(rb_eTypeError, "#%d must be a string or symbol", i + 2); + }; + }; + + data->ctype = t; + data->slen = num; + data->ids_num = num; + if( data->stype ) dlfree(data->stype); + data->stype = (char*)dlmalloc(sizeof(char) * num); + if( data->ssize ) dlfree(data->ssize); + data->ssize = (int*)dlmalloc(sizeof(int) * num); + if( data->ids ) dlfree(data->ids); + data->ids = (ID*)dlmalloc(sizeof(ID) * data->ids_num); + + ctype = STR2CSTR(type); + for( i=0; i<num; i++ ){ + vid = rb_ary_entry(rest,i); + data->ids[i] = rb_to_id(vid); + data->stype[i] = *ctype; + ctype ++; + if( isdigit(*ctype) ){ + char *p, *d; + for( p=ctype; isdigit(*p); p++ ) ; + d = ALLOCA_N(char, p - ctype + 1); + strncpy(d, ctype, p - ctype); + d[p - ctype] = '\0'; + data->ssize[i] = atoi(d); + ctype = p; + } + else{ + data->ssize[i] = 1; + }; + }; + + if( *ctype ){ + rb_raise(rb_eArgError, "too few/many arguments"); + }; + + if( !data->size ) + data->size = dlsizeof(STR2CSTR(type)); + + return Qnil; +}; + +VALUE +rb_dlptr_define_struct(int argc, VALUE argv[], VALUE self) +{ + VALUE *pass_argv; + int pass_argc, i; + + pass_argc = argc + 1; + pass_argv = ALLOCA_N(VALUE, pass_argc); + pass_argv[0] = INT2FIX(DLPTR_CTYPE_STRUCT); + for( i=1; i<pass_argc; i++ ){ + pass_argv[i] = argv[i-1]; + }; + return rb_dlptr_define_data_type(pass_argc, pass_argv, self); +}; + +VALUE +rb_dlptr_define_union(int argc, VALUE argv[], VALUE self) +{ + VALUE *pass_argv; + int pass_argc, i; + + pass_argc = argc + 1; + pass_argv = ALLOCA_N(VALUE, pass_argc); + pass_argv[0] = INT2FIX(DLPTR_CTYPE_UNION); + for( i=1; i<pass_argc; i++ ){ + pass_argv[i] = argv[i-1]; + }; + return rb_dlptr_define_data_type(pass_argc, pass_argv, self); +}; + +VALUE +rb_dlptr_get_data_type(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + if( data->stype ) + return rb_assoc_new(INT2FIX(data->ctype), + rb_tainted_str_new(data->stype, data->slen)); + else + return rb_assoc_new(INT2FIX(data->ctype), Qnil); +}; + +static VALUE +cary2ary(void *ptr, char t, int len) +{ + VALUE ary; + VALUE elem; + int i; + + if( len < 1 ) + return Qnil; + + if( len == 1 ){ + switch( t ){ + case 'I': case 'i': + elem = INT2NUM(*((int*)ptr)); + ptr += sizeof(int); + break; + case 'L': case 'l': + elem = DLLONG2NUM(*((long*)ptr)); + ptr += sizeof(long); + break; + case 'P': case 'p': + elem = rb_dlptr_new(*((void**)ptr),0, 0); + ptr += sizeof(void*); + break; + case 'F': case 'f': + elem = rb_float_new(*((float*)ptr)); + ptr += sizeof(float); + break; + case 'D': case 'd': + elem = rb_float_new(*((float*)ptr)); + ptr += sizeof(double); + break; + case 'C': case 'c': + elem = INT2NUM(*((char*)ptr)); + ptr += sizeof(char); + break; + case 'H': case 'h': + elem = INT2NUM(*((short*)ptr)); + ptr += sizeof(short); + default: + raise(rb_eDLTypeError, "unsupported type '%c'", t); + }; + return elem; + }; + + ary = rb_ary_new(); + for( i=0; i < len; i++ ){ + switch( t ){ + case 'I': case 'i': + elem = INT2NUM(*((int*)ptr)); + ptr += sizeof(int); + break; + case 'L': case 'l': + elem = DLLONG2NUM(*((long*)ptr)); + ptr += sizeof(long); + break; + case 'P': case 'p': + elem = rb_dlptr_new(*((void**)ptr), 0, 0); + ptr += sizeof(void*); + break; + case 'F': case 'f': + elem = rb_float_new(*((float*)ptr)); + ptr += sizeof(float); + break; + case 'D': case 'd': + elem = rb_float_new(*((float*)ptr)); + ptr += sizeof(double); + break; + case 'C': case 'c': + elem = INT2NUM(*((char*)ptr)); + ptr += sizeof(char); + break; + case 'H': case 'h': + elem = INT2NUM(*((short*)ptr)); + ptr += sizeof(short); + default: + raise(rb_eDLTypeError, "unsupported type '%c'", t); + }; + rb_ary_push(ary, elem); + }; + + return ary; +}; + +VALUE +rb_dlptr_aref(int argc, VALUE argv[], VALUE self) +{ + VALUE val, key = Qnil, num = Qnil; + ID id; + int idx; + struct ptr_data *data; + int i; + int offset; + + if( rb_scan_args(argc, argv, "11", &key, &num) == 1 ){ + num = INT2NUM(0); + }; + + if( TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM ){ + VALUE pass[] = {num}; + return rb_dlptr_to_str(1, pass, rb_dlptr_plus(self, key)); + }; + + if( ! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL ) ){ + rb_raise(rb_eTypeError, "the key must be a string or symbol"); + }; + + id = rb_to_id(key); + Data_Get_Struct(self, struct ptr_data, data); + switch( data->ctype ){ + case DLPTR_CTYPE_STRUCT: + offset = 0; + for( i=0; i < data->ids_num; i++ ){ + if( data->ids[i] == id ){ + switch( data->stype[i] ){ + case 'I': + DLALIGN(data->ptr,offset,INT_ALIGN); + break; + case 'L': + DLALIGN(data->ptr,offset,LONG_ALIGN); + break; + case 'P': + DLALIGN(data->ptr,offset,VOIDP_ALIGN); + case 'F': + DLALIGN(data->ptr,offset,FLOAT_ALIGN); + break; + case 'D': + DLALIGN(data->ptr,offset,DOUBLE_ALIGN); + break; + case 'C': + break; + case 'H': + DLALIGN(data->ptr,offset,SHORT_ALIGN); + break; + default: + raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]); + }; + return cary2ary(data->ptr + offset, data->stype[i], data->ssize[i]); + }; + switch( data->stype[i] ){ + case 'I': + case 'i': + offset += sizeof(int) * data->ssize[i]; + break; + case 'L': + case 'l': + offset += sizeof(long) * data->ssize[i]; + break; + case 'P': + case 'p': + offset += sizeof(void*) * data->ssize[i]; + break; + case 'F': + case 'f': + offset += sizeof(float) * data->ssize[i]; + break; + case 'D': + case 'd': + offset += sizeof(double) * data->ssize[i]; + break; + case 'C': + case 'c': + offset += sizeof(char) * data->ssize[i]; + break; + case 'H': + case 'h': + offset += sizeof(short) * data->ssize[i]; + break; + default: + raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]); + }; + }; + break; + case DLPTR_CTYPE_UNION: + for( i=0; i < data->ids_num; i++ ){ + if( data->ids[i] == id ){ + return cary2ary(data->ptr + offset, data->stype[i], data->ssize[i]); + }; + }; + break; + }; /* end of switch */ + + rb_raise(rb_eNameError, "undefined key `%s' for %s", + rb_id2name(id), rb_class2name(CLASS_OF(self))); + + return Qnil; +}; + +static void * +ary2cary(char t, VALUE val, long *size) +{ + void *ptr; + + if( TYPE(val) == T_ARRAY ){ + ptr = rb_ary2cary(t, val, size); + } + else{ + ptr = rb_ary2cary(t, rb_ary_new3(1, val), size); + }; + return ptr; +}; + +VALUE +rb_dlptr_aset(int argc, VALUE argv[], VALUE self) +{ + VALUE key = Qnil, num = Qnil, val = Qnil; + ID id; + struct ptr_data *data; + int i; + int offset; + long memsize; + void *memimg; + + switch( rb_scan_args(argc, argv, "21", &key, &num, &val) ){ + case 2: + val = num; + num = Qnil; + break; + }; + + if( TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM ){ + void *dst, *src; + int len; + + Check_Type(val, T_STRING); + Data_Get_Struct(self, struct ptr_data, data); + dst = (void*)((long)(data->ptr) + DLNUM2LONG(key)); + src = STR2CSTR(val); + if( num == Qnil ){ + len = RSTRING(val)->len; + } + else{ + len = NUM2INT(num); + }; + memcpy(dst, src, len); + return val; + }; + + if( ! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL ) ){ + rb_raise(rb_eTypeError, "the key must be a string or symbol"); + }; + + id = rb_to_id(key); + Data_Get_Struct(self, struct ptr_data, data); + switch( data->ctype ){ + case DLPTR_CTYPE_STRUCT: + offset = 0; + for( i=0; i < data->ids_num; i++ ){ + if( data->ids[i] == id ){ + switch( data->stype[i] ){ + case 'I': + DLALIGN(data->ptr,offset,INT_ALIGN); + break; + case 'L': + DLALIGN(data->ptr,offset,LONG_ALIGN); + break; + case 'P': + DLALIGN(data->ptr,offset,VOIDP_ALIGN); + break; + case 'D': + DLALIGN(data->ptr,offset,DOUBLE_ALIGN); + break; + case 'F': + DLALIGN(data->ptr,offset,FLOAT_ALIGN); + break; + case 'C': + break; + case 'H': + DLALIGN(data->ptr,offset,SHORT_ALIGN); + break; + default: + raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]); + }; + memimg = ary2cary(data->stype[i], val, &memsize); + memcpy(data->ptr + offset, memimg, memsize); + return val; + }; + switch( data->stype[i] ){ + case 'I': + case 'i': + offset += sizeof(int) * data->ssize[i]; + break; + case 'L': + case 'l': + offset += sizeof(long) * data->ssize[i]; + break; + case 'P': + case 'p': + offset += sizeof(void*) * data->ssize[i]; + break; + case 'D': + case 'd': + offset += sizeof(double) * data->ssize[i]; + break; + case 'F': + case 'f': + offset += sizeof(float) * data->ssize[i]; + break; + case 'C': + case 'c': + offset += sizeof(char) * data->ssize[i]; + break; + case 'H': + case 'h': + offset += sizeof(short) * data->ssize[i]; + break; + default: + raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]); + }; + }; + return val; + /* break; */ + case DLPTR_CTYPE_UNION: + for( i=0; i < data->ids_num; i++ ){ + if( data->ids[i] == id ){ + switch( data->stype[i] ){ + case 'I': case 'i': + memsize = sizeof(int) * data->ssize[i]; + break; + case 'L': case 'l': + memsize = sizeof(long) * data->ssize[i]; + break; + case 'P': case 'p': + memsize = sizeof(void*) * data->ssize[i]; + break; + case 'F': case 'f': + memsize = sizeof(float) * data->ssize[i]; + break; + case 'D': case 'd': + memsize = sizeof(double) * data->ssize[i]; + break; + case 'C': case 'c': + memsize = sizeof(char) * data->ssize[i]; + break; + case 'H': case 'h': + memsize = sizeof(short) * data->ssize[i]; + break; + default: + raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]); + }; + memimg = ary2cary(data->stype[i], val, NULL); + memcpy(data->ptr, memimg, memsize); + }; + }; + return val; + /* break; */ + }; + + rb_raise(rb_eNameError, "undefined key `%s' for %s", + rb_id2name(id), rb_class2name(CLASS_OF(self))); + + return Qnil; +}; + +VALUE +rb_dlptr_size(int argc, VALUE argv[], VALUE self) +{ + VALUE size; + + switch( rb_scan_args(argc, argv, "01", &size) ){ + case 0: + return DLLONG2NUM(RDLPTR(self)->size); + case 1: + RDLPTR(self)->size = DLNUM2LONG(size); + return size; + }; +}; + +static VALUE +dlmem_each_i(VALUE assoc, void *data) +{ + VALUE key, val; + key = rb_ary_entry(assoc, 0); + val = rb_ary_entry(assoc, 1); + rb_yield(rb_assoc_new(key,(VALUE)DLNUM2LONG(val))); + return Qnil; +}; + +VALUE +rb_dlmem_each(VALUE self) +{ + rb_iterate(rb_each, DLMemoryTable, dlmem_each_i, 0); + return Qnil; +}; + +void +Init_dlptr() +{ + rb_cDLPtrData = rb_define_class_under(rb_mDL, "PtrData", rb_cData); + rb_define_singleton_method(rb_cDLPtrData, "new", rb_dlptr_s_new, -1); + rb_define_singleton_method(rb_cDLPtrData, "alloc", rb_dlptr_s_alloc, -1); + rb_define_method(rb_cDLPtrData, "initialize", rb_dlptr_init, -1); + rb_define_method(rb_cDLPtrData, "free=", rb_dlptr_free_set, 1); + rb_define_method(rb_cDLPtrData, "free", rb_dlptr_free_get, 0); + rb_define_method(rb_cDLPtrData, "to_i", rb_dlptr_to_i, 0); + rb_define_method(rb_cDLPtrData, "ptr", rb_dlptr_ptr, 0); + rb_define_method(rb_cDLPtrData, "+@", rb_dlptr_ptr, 0); + rb_define_method(rb_cDLPtrData, "ref", rb_dlptr_ref, 0); + rb_define_method(rb_cDLPtrData, "-@", rb_dlptr_ref, 0); + rb_define_method(rb_cDLPtrData, "null?", rb_dlptr_null_p, 0); + rb_define_method(rb_cDLPtrData, "to_a", rb_dlptr_to_array, -1); + rb_define_method(rb_cDLPtrData, "to_s", rb_dlptr_to_s, -1); + rb_define_method(rb_cDLPtrData, "to_str", rb_dlptr_to_str, -1); + rb_define_method(rb_cDLPtrData, "inspect", rb_dlptr_inspect, 0); + rb_define_method(rb_cDLPtrData, "<=>", rb_dlptr_cmp, 1); + rb_define_method(rb_cDLPtrData, "==", rb_dlptr_eql, 1); + rb_define_method(rb_cDLPtrData, "eql?", rb_dlptr_eql, 1); + rb_define_method(rb_cDLPtrData, "+", rb_dlptr_plus, 1); + rb_define_method(rb_cDLPtrData, "-", rb_dlptr_minus, 1); + rb_define_method(rb_cDLPtrData, "cast!", rb_dlptr_cast, -1); + rb_define_method(rb_cDLPtrData, "define_data_type", + rb_dlptr_define_data_type, -1); + rb_define_method(rb_cDLPtrData, "struct!", rb_dlptr_define_struct, -1); + rb_define_method(rb_cDLPtrData, "union!", rb_dlptr_define_union, -1); + rb_define_method(rb_cDLPtrData, "data_type", rb_dlptr_get_data_type, 0); + rb_define_method(rb_cDLPtrData, "[]", rb_dlptr_aref, -1); + rb_define_method(rb_cDLPtrData, "[]=", rb_dlptr_aset, -1); + rb_define_method(rb_cDLPtrData, "size", rb_dlptr_size, -1); + rb_define_method(rb_cDLPtrData, "size=", rb_dlptr_size, -1); + + rb_mDLMemorySpace = rb_define_module_under(rb_mDL, "MemorySpace"); + DLMemoryTable = rb_hash_new(); + rb_define_const(rb_mDLMemorySpace, "MemoryTable", DLMemoryTable); + rb_define_module_function(rb_mDLMemorySpace, "each", rb_dlmem_each, 0); +}; diff --git a/ext/dl/sample/drives.rb b/ext/dl/sample/drives.rb new file mode 100644 index 000000000..8a590404b --- /dev/null +++ b/ext/dl/sample/drives.rb @@ -0,0 +1,70 @@ +# -*- ruby -*- +# drives.rb -- find existing drives and show the drive type. + +require 'dl' +require 'dl/import' + +module Kernel32 + extend DL::Importable + + dlload "kernel32" + + extern "long GetLogicalDrives()" + extern "int GetDriveType(char*)" + extern "long GetDiskFreeSpace(char*, long ref, long ref, long ref, long ref)" +end + +include Kernel32 + +buff = Kernel32.getLogicalDrives() + +i = 0 +ds = [] +while( i < 26 ) + mask = (1 << i) + if( buff & mask > 0 ) + ds.push((65+i).chr) + end + i += 1 +end + +=begin +From the cygwin's /usr/include/w32api/winbase.h: +#define DRIVE_UNKNOWN 0 +#define DRIVE_NO_ROOT_DIR 1 +#define DRIVE_REMOVABLE 2 +#define DRIVE_FIXED 3 +#define DRIVE_REMOTE 4 +#define DRIVE_CDROM 5 +#define DRIVE_RAMDISK 6 +=end + +types = [ + "unknown", + "no root dir", + "Removable", + "Fixed", + "Remote", + "CDROM", + "RAM", +] +print("Drive : Type (Free Space/Available Space)\n") +ds.each{|d| + t = Kernel32.getDriveType(d + ":\\") + Kernel32.getDiskFreeSpace(d + ":\\", 0, 0, 0, 0) + _,sec_per_clus,byte_per_sec,free_clus,total_clus = Kernel32._args_ + fbytes = sec_per_clus * byte_per_sec * free_clus + tbytes = sec_per_clus * byte_per_sec * total_clus + unit = "B" + if( fbytes > 1024 && tbytes > 1024 ) + fbytes = fbytes / 1024 + tbytes = tbytes / 1024 + unit = "K" + end + if( fbytes > 1024 && tbytes > 1024 ) + fbytes = fbytes / 1024 + tbytes = tbytes / 1024 + unit = "M" + end + print("#{d} : #{types[t]} (#{fbytes} #{unit}/#{tbytes} #{unit})\n") +} diff --git a/ext/dl/sample/getch.rb b/ext/dl/sample/getch.rb new file mode 100644 index 000000000..3f7261c97 --- /dev/null +++ b/ext/dl/sample/getch.rb @@ -0,0 +1,5 @@ +require 'dl' + +crtdll = DL::dlopen("crtdll") +getch = crtdll['_getch', 'L'] +print(getch.call, "\n") diff --git a/ext/dl/sample/libc.rb b/ext/dl/sample/libc.rb new file mode 100644 index 000000000..c88fc687f --- /dev/null +++ b/ext/dl/sample/libc.rb @@ -0,0 +1,84 @@ +require 'dl' + +module LIBC + begin + LIB = DL.dlopen('libc.so.6') + rescue RuntimeError + LIB = DL.dlopen('libc.so.5') + end + + SYM = { + :atoi => LIB['atoi', 'IS'], + :isdigit => LIB['isdigit', 'II'], + } + + def atoi(str) + r,rs = SYM[:atoi].call(str) + return r + end + + def isdigit(c) + r,rs = SYM[:isdigit].call(c) + return (r != 0) + end +end + +module LIBC + SYM[:strcat] = LIB['strcat', 'SsS'] + def strcat(str1,str2) + r,rs = SYM[:strcat].call(str1 + "\0#{str2}",str2) + return rs[0] + end +end + +module LIBC + SYM[:fopen] = LIB['fopen', 'PSS'] + SYM[:fclose] = LIB['fclose', '0P'] + SYM[:fgetc] = LIB['fgetc', 'IP'] + + def fopen(filename, mode) + r,rs = SYM[:fopen].call(filename, mode) + return r + end + + def fclose(ptr) + SYM[:fclose].call(ptr) + return nil + end + + def fgetc(ptr) + r,rs = SYM[:fgetc].call(ptr) + return r + end +end + +module LIBC + SYM[:strlen] = LIB['strlen', 'IP'] + def strlen(str) + r,rs = SYM[:strlen].call(str) + return r + end +end + +$cb1 = DL.set_callback('IPP', 0){|ptr1, ptr2| + str1 = ptr1.ptr.to_s + str2 = ptr2.ptr.to_s + str1 <=> str2 +} + +module LIBC + SYM[:qsort] = LIB['qsort', '0aIIP'] + def qsort(ary, comp) + len = ary.length + r,rs = SYM[:qsort].call(ary, len, DL.sizeof('P'), comp) + return rs[0].to_a('S', len) + end +end + +include LIBC + +p atoi("10") +p isdigit(?1) +p isdigit(?a) +p strcat("a", "b") +p qsort(["a","c","b"],$cb1) diff --git a/ext/dl/sample/msgbox.rb b/ext/dl/sample/msgbox.rb new file mode 100644 index 000000000..091e64609 --- /dev/null +++ b/ext/dl/sample/msgbox.rb @@ -0,0 +1,19 @@ +# This script works on Windows. + +require 'dl' + +User32 = DL.dlopen("user32") +Kernel32 = DL.dlopen("kernel32") + +MB_OK = 0 +MB_OKCANCEL = 1 + +message_box = User32['MessageBoxA', 'ILSSI'] +r,rs = message_box.call(0, 'ok?', 'error', MB_OKCANCEL) + +case r +when 1 + print("OK!\n") +when 2 + print("Cancel!\n") +end diff --git a/ext/dl/sample/msgbox2.rb b/ext/dl/sample/msgbox2.rb new file mode 100644 index 000000000..e49846cc5 --- /dev/null +++ b/ext/dl/sample/msgbox2.rb @@ -0,0 +1,18 @@ +# This script works on Windows. + +require 'dl/win32' + +MB_OK = 0 +MB_OKCANCEL = 1 + +message_box = Win32API.new("user32",'MessageBoxA', 'ISSI', 'I') +r = message_box.call(0, 'ok?', 'error', MB_OKCANCEL) + +case r +when 1 + print("OK!\n") +when 2 + print("Cancel!\n") +else + p r +end diff --git a/ext/dl/sample/stream.rb b/ext/dl/sample/stream.rb new file mode 100644 index 000000000..179836999 --- /dev/null +++ b/ext/dl/sample/stream.rb @@ -0,0 +1,87 @@ +# -*- ruby -*- +# Display a file name and stream names of a file with those size. + +require 'dl' +require 'dl/import' + +module NTFS + extend DL::Importable + + dlload "kernel32.dll" + + OPEN_EXISTING = 3 + GENERIC_READ = 0x80000000 + BACKUP_DATA = 0x00000001 + BACKUP_ALTERNATE_DATA = 0x00000004 + FILE_SHARE_READ = 0x00000001 + FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 + + typealias "LPSECURITY_ATTRIBUTES", "void*" + + extern "BOOL BackupRead(HANDLE, PBYTE, DWORD, PDWORD, BOOL, BOOL, PVOID)" + extern "BOOL BackupSeek(HANDLE, DWORD, DWORD, PDWORD, PDWORD, PVOID)" + extern "BOOL CloseHandle(HANDLE)" + extern "HANDLE CreateFile(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, + DWORD, DWORD, HANDLE)" + + module_function + + def streams(filename) + status = [] + h = createFile(filename,GENERIC_READ,FILE_SHARE_READ,nil, + OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0) + if( h != 0 ) + begin + # allocate the memory for backup data used in backupRead(). + data = DL.malloc(DL.sizeof("L5")) + data.struct!("LLLLL", :id, :attrs, :size_low, :size_high, :name_size) + + # allocate memories for references to long values used in backupRead(). + context = DL.malloc(DL.sizeof("L")) + lval = DL.malloc(DL.sizeof("L")) + + while( backupRead(h, data, data.size, lval, false, false, context) ) + size = data[:size_low] + (data[:size_high] << (DL.sizeof("I") * 8)) + case data[:id] + when BACKUP_ALTERNATE_DATA + stream_name = DL.malloc(data[:name_size]) + backupRead(h, stream_name, stream_name.size, + lval, false, false, context) + name = stream_name[0, stream_name.size] + name.tr!("\000","") + if( name =~ /^:(.*?):.*$/ ) + status.push([$1,size]) + end + when BACKUP_DATA + status.push([nil,size]) + else + raise(RuntimeError, "unknown data type #{data[:id]}.") + end + l1 = DL.malloc(DL.sizeof("L")) + l2 = DL.malloc(DL.sizeof("L")) + if( !backupSeek(h, data[:size_low], data[:size_high], l1, l2, context) ) + break + end + end + ensure + backupRead(h, nil, 0, lval, true, false, context) + closeHandle(h) + end + return status + else + raise(RuntimeError, "can't open #{filename}.\n") + end + end +end + +ARGV.each{|filename| + if( File.exist?(filename) ) + NTFS.streams(filename).each{|name,size| + if( name ) + print("#{filename}:#{name}\t#{size}bytes\n") + else + print("#{filename}\t#{size}bytes\n") + end + } + end +} diff --git a/ext/dl/sym.c b/ext/dl/sym.c new file mode 100644 index 000000000..683092ae7 --- /dev/null +++ b/ext/dl/sym.c @@ -0,0 +1,771 @@ +/* -*- C -*- + * $Id$ + */ + +#include <ruby.h> +#include "dl.h" + +VALUE rb_cDLSymbol; + +#ifndef HAVE_RB_STR_CAT2 +static VALUE +rb_str_cat2(VALUE str, const char *s) +{ + return rb_str_cat(str, s, strlen(s)); +} +#endif + +static const char * +char2type(int ch) +{ + switch (ch) { + case '0': + return "void"; + case 'P': + return "void *"; + case 'p': + return "void *"; + case 'C': + return "char"; + case 'c': + return "char *"; + case 'H': + return "short"; + case 'h': + return "short *"; + case 'I': + return "int"; + case 'i': + return "int *"; + case 'L': + return "long"; + case 'l': + return "long *"; + case 'F': + return "double"; + case 'f': + return "double *"; + case 'D': + return "double"; + case 'd': + return "double *"; + case 'S': + return "const char *"; + case 's': + return "char *"; + case 'A': + return "[]"; + case 'a': + return "[]"; /* ?? */ + }; + return NULL; +}; + +void +dlsym_free(struct sym_data *data) +{ + if( data->name ){ + DEBUG_CODE({ + printf("dlsym_free(): free(data->name:%s)\n",data->name); + }); + free(data->name); + }; + if( data->type ){ + DEBUG_CODE({ + printf("dlsym_free(): free(data->type:%s)\n",data->type); + }); + free(data->type); + }; +}; + +VALUE +rb_dlsym_new(void (*func)(), const char *name, const char *type) +{ + VALUE val; + struct sym_data *data; + const char *ptype; + + if( !type || !type[0] ){ + return rb_dlptr_new((void*)func, 0, 0); + }; + + for( ptype = type; *ptype; ptype ++ ){ + if( ! char2type(*ptype) ){ + rb_raise(rb_eDLTypeError, "unknown type specifier '%c'", *ptype); + }; + }; + + if( func ){ + val = Data_Make_Struct(rb_cDLSymbol, struct sym_data, 0, dlsym_free, data); + data->func = func; + data->name = name ? strdup(name) : NULL; + data->type = type ? strdup(type) : NULL; + data->len = type ? strlen(type) : 0; +#ifndef USE_INLINE_ASM + if( data->len - 1 > MAX_ARG ){ + rb_raise(rb_eDLError, "maximum number of arguments is %d.", MAX_ARG); + }; +#endif + } + else{ + val = Qnil; + }; + + return val; +}; + +freefunc_t +rb_dlsym2csym(VALUE val) +{ + struct sym_data *data; + void (*func)(); + + if( rb_obj_is_kind_of(val, rb_cDLSymbol) ){ + Data_Get_Struct(val, struct sym_data, data); + func = data->func; + } + else if( val == Qnil ){ + func = NULL; + } + else{ + rb_raise(rb_eTypeError, "DL::Symbol was expected"); + }; + + return func; +}; + +VALUE +rb_dlsym_s_new(int argc, VALUE argv[], VALUE self) +{ + VALUE addr, name, type; + VALUE val; + void *saddr; + const char *sname, *stype; + + switch( rb_scan_args(argc, argv, "12", &addr, &name, &type) ){ + case 3: + break; + case 2: + type = Qnil; + break; + case 1: + name = Qnil; + type = Qnil; + }; + + saddr = (void*)(DLNUM2LONG(rb_Integer(addr))); + sname = (name == Qnil) ? NULL : STR2CSTR(name); + stype = (type == Qnil) ? NULL : STR2CSTR(type); + + val = rb_dlsym_new(saddr, sname, stype); + + if( val != Qnil ){ + rb_obj_call_init(val, argc, argv); + }; + + return val; +}; + +VALUE +rb_dlsym_initialize(int argc, VALUE argv[], VALUE self) +{ + return Qnil; +}; + +VALUE +rb_s_dlsym_char2type(VALUE self, VALUE ch) +{ + const char *type; + + type = char2type(STR2CSTR(ch)[0]); + + if (type == NULL) + return Qnil; + else + return rb_str_new2(type); +}; + +VALUE +rb_dlsym_name(VALUE self) +{ + struct sym_data *sym; + + Data_Get_Struct(self, struct sym_data, sym); + return sym->name ? rb_tainted_str_new2(sym->name) : Qnil; +}; + +VALUE +rb_dlsym_proto(VALUE self) +{ + struct sym_data *sym; + + Data_Get_Struct(self, struct sym_data, sym); + return sym->type ? rb_tainted_str_new2(sym->type) : Qnil; +}; + +VALUE +rb_dlsym_cproto(VALUE self) +{ + struct sym_data *sym; + const char *ptype, *typestr; + size_t len; + VALUE val; + + Data_Get_Struct(self, struct sym_data, sym); + + ptype = sym->type; + + if( ptype ){ + typestr = char2type(*ptype++); + len = strlen(typestr); + + val = rb_tainted_str_new(typestr, len); + if (typestr[len - 1] != '*') + rb_str_cat(val, " ", 1); + + if( sym->name ){ + rb_str_cat2(val, sym->name); + } + else{ + rb_str_cat2(val, "(null)"); + }; + rb_str_cat(val, "(", 1); + + while (*ptype) { + const char *ty = char2type(*ptype++); + rb_str_cat2(val, ty); + if (*ptype) + rb_str_cat(val, ", ", 2); + } + + rb_str_cat(val, ");", 2); + } + else{ + val = rb_tainted_str_new2("void ("); + if( sym->name ){ + rb_str_cat2(val, sym->name); + } + else{ + rb_str_cat2(val, "(null)"); + }; + rb_str_cat2(val, ")()"); + }; + + return val; +}; + +VALUE +rb_dlsym_inspect(VALUE self) +{ + VALUE proto; + VALUE val; + char *str; + int str_size; + struct sym_data *sym; + + Data_Get_Struct(self, struct sym_data, sym); + proto = rb_dlsym_cproto(self); + + str_size = RSTRING(proto)->len + 100; + str = dlmalloc(str_size); + snprintf(str, str_size - 1, + "#<DL::Symbol:0x%x func=0x%x '%s'>", + sym, sym->func, STR2CSTR(proto)); + val = rb_tainted_str_new2(str); + dlfree(str); + + return val; +}; + + +VALUE +rb_dlsym_call(int argc, VALUE argv[], VALUE self) +{ + struct sym_data *sym; + ANY_TYPE *args; + ANY_TYPE *dargs; + ANY_TYPE ret; + int *dtypes; + VALUE val; + VALUE dvals; + int i; + long ftype; + void *func; + + Data_Get_Struct(self, struct sym_data, sym); + DEBUG_CODE({ + printf("rb_dlsym_call(): type = '%s', func = 0x%x\n", sym->type, sym->func); + }); + if( (sym->len - 1) != argc ){ + rb_raise(rb_eArgError, "%d arguments are needed", sym->len - 1); + }; + + ftype = 0; + dvals = Qnil; + + args = ALLOC_N(ANY_TYPE, sym->len - 1); + dargs = ALLOC_N(ANY_TYPE, sym->len - 1); + dtypes = ALLOC_N(int, sym->len - 1); +#define FREE_ARGS {xfree(args); xfree(dargs); xfree(dtypes);} + + for( i = sym->len - 2; i >= 0; i-- ){ + dtypes[i] = 0; + + switch( sym->type[i+1] ){ + case 'p': + dtypes[i] = 'p'; + case 'P': + { + struct ptr_data *data; + VALUE pval; + + if( argv[i] == Qnil ){ + ANY2P(args[i]) = DLVOIDP(0); + } + else{ + if( rb_obj_is_kind_of(argv[i], rb_cDLPtrData) ){ + pval = argv[i]; + } + else{ + pval = rb_funcall(argv[i], rb_intern("to_ptr"), 0); + if( !rb_obj_is_kind_of(pval, rb_cDLPtrData) ){ + rb_raise(rb_eDLTypeError, "unexpected type of argument #%d", i); + }; + }; + Data_Get_Struct(pval, struct ptr_data, data); + ANY2P(args[i]) = DLVOIDP(data->ptr); + }; + }; + PUSH_P(ftype); + break; + case 'a': + dtypes[i] = 'a'; + case 'A': + if( argv[i] == Qnil ){ + ANY2P(args[i]) = DLVOIDP(0); + } + else{ + ANY2P(args[i]) = DLVOIDP(rb_ary2cary(0, argv[i], NULL)); + }; + PUSH_P(ftype); + break; + case 'C': + ANY2C(args[i]) = DLCHAR(NUM2CHR(argv[i])); + PUSH_C(ftype); + break; + case 'c': + ANY2C(dargs[i]) = DLCHAR(NUM2CHR(argv[i])); + ANY2P(args[i]) = DLVOIDP(&(ANY2C(dargs[i]))); + dtypes[i] = 'c'; + PUSH_P(ftype); + break; + case 'H': + ANY2H(args[i]) = DLSHORT(NUM2CHR(argv[i])); + PUSH_C(ftype); + break; + case 'h': + ANY2H(dargs[i]) = DLSHORT(NUM2CHR(argv[i])); + ANY2P(args[i]) = DLVOIDP(&(ANY2H(dargs[i]))); + dtypes[i] = 'h'; + PUSH_P(ftype); + break; + case 'I': + ANY2I(args[i]) = DLINT(NUM2INT(argv[i])); + PUSH_I(ftype); + break; + case 'i': + ANY2I(dargs[i]) = DLINT(NUM2INT(argv[i])); + ANY2P(args[i]) = DLVOIDP(&(ANY2I(dargs[i]))); + dtypes[i] = 'i'; + PUSH_P(ftype); + break; + case 'L': + ANY2L(args[i]) = DLNUM2LONG(argv[i]); + PUSH_L(ftype); + break; + case 'l': + ANY2L(dargs[i]) = DLNUM2LONG(argv[i]); + ANY2P(args[i]) = DLVOIDP(&(ANY2L(dargs[i]))); + dtypes[i] = 'l'; + PUSH_P(ftype); + break; + case 'F': + Check_Type(argv[i], T_FLOAT); + ANY2F(args[i]) = DLFLOAT(RFLOAT(argv[i])->value); + PUSH_F(ftype); + break; + case 'f': + Check_Type(argv[i], T_FLOAT); + ANY2F(dargs[i]) = DLFLOAT(RFLOAT(argv[i])->value); + ANY2P(args[i]) = DLVOIDP(&(ANY2F(dargs[i]))); + dtypes[i] = 'f'; + PUSH_P(ftype); + break; + case 'D': + Check_Type(argv[i], T_FLOAT); + ANY2D(args[i]) = RFLOAT(argv[i])->value; + PUSH_D(ftype); + break; + case 'd': + Check_Type(argv[i], T_FLOAT); + ANY2D(dargs[i]) = RFLOAT(argv[i])->value; + ANY2P(args[i]) = DLVOIDP(&(ANY2D(dargs[i]))); + dtypes[i] = 'd'; + PUSH_P(ftype); + break; + case 'S': + if( argv[i] == Qnil ){ + ANY2S(args[i]) = DLSTR(0); + } + else{ + ANY2S(args[i]) = DLSTR(STR2CSTR(argv[i])); + }; + PUSH_P(ftype); + break; + case 's': + if( argv[i] == Qnil ){ + raise(rb_eDLError, "#%d must be a string",i); + }; + ANY2S(args[i]) = DLSTR(dlmalloc(RSTRING(argv[i])->len + 1)); + memcpy((char*)(ANY2S(args[i])), STR2CSTR(argv[i]), RSTRING(argv[i])->len + 1); + dtypes[i] = 's'; + PUSH_P(ftype); + break; + default: + FREE_ARGS; + rb_raise(rb_eDLTypeError, + "unknown type '%c' of the return value.", + sym->type[i+1]); + }; + }; + + switch( sym->type[0] ){ + case '0': + PUSH_0(ftype); + break; + case 'P': + case 'p': + case 'S': + case 's': + case 'A': + case 'a': + PUSH_P(ftype); + break; + case 'C': + case 'c': + PUSH_C(ftype); + break; + case 'H': + case 'h': + PUSH_H(ftype); + break; + case 'I': + case 'i': + PUSH_I(ftype); + break; + case 'L': + case 'l': + PUSH_L(ftype); + break; + case 'F': + case 'f': + PUSH_F(ftype); + break; + case 'D': + case 'd': + PUSH_D(ftype); + break; + default: + FREE_ARGS; + rb_raise(rb_eDLTypeError, + "unknown type `%c' of the return value.", + sym->type[0]); + }; + + func = sym->func; + +#ifdef USE_INLINE_ASM + ASM_START(sym->type); + for( i = sym->len - 2; i >= 0; i-- ){ + switch( sym->type[i+1] ){ + case 'p': + case 'P': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'a': + case 'A': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'C': + ASM_PUSH_C(ANY2C(args[i])); + break; + case 'c': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'H': + ASM_PUSH_H(ANY2H(args[i])); + break; + case 'h': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'I': + ASM_PUSH_I(ANY2I(args[i])); + break; + case 'i': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'L': + ASM_PUSH_L(ANY2L(args[i])); + break; + case 'l': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'F': + ASM_PUSH_F(ANY2F(args[i])); + break; + case 'f': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'D': + ASM_PUSH_D(ANY2D(args[i])); + break; + case 'd': + ASM_PUSH_P(ANY2P(args[i])); + break; + case 'S': + case 's': + ASM_PUSH_P(ANY2S(args[i])); + break; + }; + } + ASM_END(sym->type); + + { + switch( sym->type[0] ){ + case '0': + { + void (*f)() = func; + f(); + }; + break; + case 'P': + case 'p': + { + void * (*f)() = func; + ret.p = f(); + }; + break; + case 'C': + case 'c': + { + char (*f)() = func; + ret.c = f(); + }; + break; + case 'H': + case 'h': + { + short (*f)() = func; + ret.h = f(); + }; + break; + case 'I': + case 'i': + { + int (*f)() = func; + ret.i = f(); + }; + break; + case 'L': + case 'l': + { + long (*f)() = func; + ret.l = f(); + }; + break; + case 'F': + case 'f': + { + float (*f)() = func; + ret.f = f(); + }; + break; + case 'D': + case 'd': + { + double (*f)() = func; + ret.d = f(); + }; + break; + case 'S': + case 's': + { + char * (*f)() = func; + ret.s = f(); + }; + break; + default: + FREE_ARGS; + rb_raise(rb_eDLTypeError, "unknown type `%c'", sym->type[0]); + }; + }; +#else + switch(ftype){ +#include "call.func" + default: + FREE_ARGS; + rb_raise(rb_eDLTypeError, "unsupported function type `%s'", sym->type); + }; +#endif + + switch( sym->type[0] ){ + case '0': + val = Qnil; + break; + case 'P': + val = rb_dlptr_new((void*)(ANY2P(ret)), 0, 0); + break; + case 'p': + val = rb_dlptr_new((void*)(ANY2P(ret)), 0, dlfree); + break; + case 'C': + case 'c': + val = CHR2FIX((char)(ANY2C(ret))); + break; + case 'H': + case 'h': + val = INT2NUM((short)(ANY2H(ret))); + break; + case 'I': + case 'i': + val = INT2NUM((int)(ANY2I(ret))); + break; + case 'L': + case 'l': + val = DLLONG2NUM((long)(ANY2L(ret))); + break; + case 'F': + case 'f': + val = rb_float_new((double)(ANY2F(ret))); + break; + case 'D': + case 'd': + val = rb_float_new((double)(ANY2D(ret))); + break; + case 'S': + if( ANY2S(ret) ){ + val = rb_tainted_str_new2((char*)(ANY2S(ret))); + } + else{ + val = Qnil; + }; + break; + case 's': + if( ANY2S(ret) ){ + val = rb_tainted_str_new2((char*)(ANY2S(ret))); + DEBUG_CODE({ + printf("dlfree(%s)\n",(char*)(ANY2S(ret))); + }); + dlfree((void*)(ANY2S(ret))); + } + else{ + val = Qnil; + }; + break; + default: + FREE_ARGS; + rb_raise(rb_eDLTypeError, "unknown type `%c'", sym->type[0]); + }; + + dvals = rb_ary_new(); + for( i = 0; i <= sym->len - 2; i++ ){ + if( dtypes[i] ){ + switch( dtypes[i] ){ + case 'c': + rb_ary_push(dvals, CHR2FIX(*((char*)(ANY2P(args[i]))))); + break; + case 'h': + rb_ary_push(dvals, INT2NUM(*((short*)(ANY2P(args[i]))))); + break; + case 'i': + rb_ary_push(dvals, INT2NUM(*((int*)(ANY2P(args[i]))))); + break; + case 'l': + rb_ary_push(dvals, DLLONG2NUM(*((long*)(ANY2P(args[i]))))); + break; + case 'f': + rb_ary_push(dvals, rb_float_new(*((float*)(ANY2P(args[i]))))); + break; + case 'd': + rb_ary_push(dvals, rb_float_new(*((double*)(ANY2P(args[i]))))); + break; + case 'p': + rb_ary_push(dvals, rb_dlptr_new((void*)(ANY2P(args[i])), 0, 0)); + break; + case 'a': + rb_ary_push(dvals, rb_dlptr_new((void*)ANY2P(args[i]), 0, 0)); + break; + case 's': + rb_ary_push(dvals, rb_tainted_str_new2((char*)ANY2S(args[i]))); + DEBUG_CODE({ + printf("dlfree(%s)\n",(char*)ANY2S(args[i])); + }); + dlfree((void*)ANY2S(args[i])); + break; + default: + { + char c = dtypes[i]; + FREE_ARGS; + rb_raise(rb_eRuntimeError, "unknown argument type '%c'", i, c); + }; + }; + } + else{ + switch( sym->type[i+1] ){ + case 'A': + dlfree((void*)ANY2P(args[i])); + break; + }; + rb_ary_push(dvals, argv[i]); + }; + }; + +#undef FREE_ARGS + return rb_assoc_new(val,dvals); +}; + +VALUE +rb_dlsym_to_i(VALUE self) +{ + struct sym_data *sym; + + Data_Get_Struct(self, struct sym_data, sym); + return DLLONG2NUM(sym); +}; + +VALUE +rb_dlsym_to_ptr(VALUE self) +{ + struct sym_data *sym; + + Data_Get_Struct(self, struct sym_data, sym); + return rb_dlptr_new(sym->func, sizeof(freefunc_t), 0); +}; + +void +Init_dlsym() +{ + rb_cDLSymbol = rb_define_class_under(rb_mDL, "Symbol", rb_cData); + rb_define_singleton_method(rb_cDLSymbol, "new", rb_dlsym_s_new, -1); + rb_define_singleton_method(rb_cDLSymbol, "char2type", rb_s_dlsym_char2type, 1); + rb_define_method(rb_cDLSymbol, "initialize", rb_dlsym_initialize, -1); + rb_define_method(rb_cDLSymbol, "call", rb_dlsym_call, -1); + rb_define_method(rb_cDLSymbol, "[]", rb_dlsym_call, -1); + rb_define_method(rb_cDLSymbol, "name", rb_dlsym_name, 0); + rb_define_method(rb_cDLSymbol, "proto", rb_dlsym_proto, 0); + rb_define_method(rb_cDLSymbol, "cproto", rb_dlsym_cproto, 0); + rb_define_method(rb_cDLSymbol, "inspect", rb_dlsym_inspect, 0); + rb_define_method(rb_cDLSymbol, "to_s", rb_dlsym_cproto, 0); + rb_define_method(rb_cDLSymbol, "to_ptr", rb_dlsym_to_ptr, 0); + rb_define_method(rb_cDLSymbol, "to_i", rb_dlsym_to_i, 0); +}; diff --git a/ext/dl/test/libtest.def b/ext/dl/test/libtest.def new file mode 100644 index 000000000..d2585286e --- /dev/null +++ b/ext/dl/test/libtest.def @@ -0,0 +1,29 @@ +EXPORTS +test_alloc_test_struct +test_append +test_arylen +test_c2i +test_call_func1 +test_callback1 +test_close +test_d2f +test_f2d +test_fill_test_struct +test_fill_test_union +test_gets +test_i2c +test_init +test_isucc +test_lcc +test_lsucc +test_open +test_strcat +test_strlen +test_succ +test_data_init +test_data_add +test_data_print +test_data_aref +test_set_long_value +test_get_long_value +internal_long_value diff --git a/ext/dl/test/test.c b/ext/dl/test/test.c new file mode 100644 index 000000000..c5ead7981 --- /dev/null +++ b/ext/dl/test/test.c @@ -0,0 +1,251 @@ +#include <stdio.h> +#include <string.h> + +static char internal_string[] = "internal_string"; +long internal_long_value = 100; + +struct test_struct { + char c; + long l; +}; + +union test_union { + char c; + int i; + long l; + void *p; +}; + +struct test_data { + char name[1024]; + struct test_data *next; +}; + +long +test_get_long_value() +{ + return internal_long_value; +}; + +void +test_set_long_value(long l) +{ + internal_long_value = l; +}; + +void +test_fill_test_struct(struct test_struct *ptr, char c, long l) +{ + ptr->c = c; + ptr->l = l; +}; + +void +test_fill_test_union(union test_union *ptr, long l) +{ + ptr->l = l; +}; + +struct test_struct * +test_alloc_test_struct(char c, long l) +{ + struct test_struct *data; + + data = (struct test_struct *)malloc(sizeof(struct test_struct)); + data->c = c; + data->l = l; + + return data; +}; + +int +test_c2i(char c) +{ + return (int)c; +}; + +char +test_i2c(int i) +{ + return (char)i; +}; + +long +test_lcc(char c1, char c2) +{ + return (long)(c1 + c2); +}; + +double +test_f2d(float f) +{ + double d; + d = f; + return d; +}; + +float +test_d2f(double d) +{ + float f; + f = d; + return f; +}; + +int +test_strlen(const char *str) +{ + return strlen(str); +}; + +int +test_isucc(int i) +{ + return (i+1); +}; + +long +test_lsucc(long l) +{ + return (l+1); +}; + +void +test_succ(long *l) +{ + (*l)++; +}; + +char * +test_strcat(char *str1, const char *str2) +{ + return strcat(str1, str2); +}; + +int +test_arylen(char *ary[]) +{ + int i; + for( i=0; ary[i]; i++ ){}; + return i; +}; + +void +test_append(char *ary[], int len, char *astr) +{ + int i; + int size1,size2; + char *str; + + size2 = strlen(astr); + + for( i=0; i <= len - 1; i++ ){ + size1 = strlen(ary[i]); + str = (char*)malloc(size1 + size2 + 1); + strcpy(str, ary[i]); + strcat(str, astr); + ary[i] = str; + }; +}; + +void +test_init(int *argc, char **argv[]) +{ + int i; + printf("test_init(0x%x,0x%x)\n",argc,argv); + printf("\t*(0x%x) => %d\n",argc,*argc); + for( i=0; i < (*argc); i++ ){ + printf("\t(*(0x%x)[%d]) => %s\n", argv, i, (*argv)[i]); + }; +}; + +FILE * +test_open(const char *filename, const char *mode) +{ + FILE *file; + file = fopen(filename,mode); + printf("test_open(%s,%s):0x%x\n",filename,mode,file); + return file; +}; + +void +test_close(FILE *file) +{ + printf("test_close(0x%x)\n",file); + fclose(file); +}; + +char * +test_gets(char *s, int size, FILE *f) +{ + return fgets(s,size,f); +}; + +typedef int callback1_t(int, char *); + +int +test_callback1(int err, const char *msg) +{ + printf("internal callback function (err = %d, msg = '%s')\n", + err, msg ? msg : "(null)"); + return 1; +}; + +int +test_call_func1(callback1_t *func) +{ + if( func ){ + return (*func)(0, "this is test_call_func1."); + } + else{ + printf("test_call_func1(func = 0)\n"); + return -1; + } +}; + +struct test_data * +test_data_init() +{ + struct test_data *data; + + data = (struct test_data *)malloc(sizeof(struct test_data)); + data->next = NULL; + memset(data->name, 0, 1024); + + return data; +}; + +void +test_data_add(struct test_data *list, const char *name) +{ + struct test_data *data; + + data = (struct test_data *)malloc(sizeof(struct test_data)); + strcpy(data->name, name); + data->next = list->next; + list->next = data; +}; + +void +test_data_print(struct test_data *list) +{ + struct test_data *data; + + for( data = list->next; data; data = data->next ){ + printf("name = %s\n", data->name); + }; +}; + +struct data * +test_data_aref(struct test_data *list, int i) +{ + struct test_data *data; + int j; + + for( data = list->next, j=0; data; data = data->next, j++ ){ + if( i == j ){ + return data; + }; + }; + return NULL; +}; diff --git a/ext/dl/test/test.rb b/ext/dl/test/test.rb new file mode 100644 index 000000000..de9a9cc53 --- /dev/null +++ b/ext/dl/test/test.rb @@ -0,0 +1,272 @@ +# -*- ruby -*- + +require 'dl' +require 'dl/import' + +def assert(label, ty, *conds) + cond = !conds.include?(false) + if( cond ) + printf("succeed in `#{label}'\n") + else + case ty + when :may + printf("fail in `#{label}' ... expected\n") + when :must + printf("fail in `#{label}' ... unexpected\n") + when :raise + raise(RuntimeError, "fail in `#{label}'") + end + end +end + +def debug(*xs) + if( $DEBUG ) + xs.each{|x| + p x + } + end +end + +print("VERSION = #{DL::VERSION}\n") +print("MAJOR_VERSION = #{DL::MAJOR_VERSION}\n") +print("MINOR_VERSION = #{DL::MINOR_VERSION}\n") +print("\n") +print("MAX_ARG = #{DL::MAX_ARG}\n") +print("MAX_CBARG = #{DL::MAX_CBARG}\n") +print("MAX_CBENT = #{DL::MAX_CBENT}\n") +print("\n") +print("DL::FREE = #{DL::FREE.inspect}\n") +print("\n") + +$LIB = nil +if( !$LIB && File.exist?("libtest.so") ) + $LIB = "./libtest.so" +end +if( !$LIB && File.exist?("test/libtest.so") ) + $LIB = "./test/libtest.so" +end + +module LIBTest + extend DL::Importable + + dlload($LIB) + extern "int test_c2i(char)" + extern "char test_i2c(int)" + extern "long test_lcc(char, char)" + extern "double test_f2d(float)" + extern "float test_d2f(double)" + extern "int test_strlen(char*)" + extern "int test_isucc(int)" + extern "long test_lsucc(long)" + extern "void test_succ(long *)" + extern "int test_arylen(int [])" + extern "void test_append(char*[], int, char *)" +end + +DL.dlopen($LIB){|h| + c2i = h["test_c2i","IC"] + debug c2i + r,rs = c2i[?a] + debug r,rs + assert("c2i", :may, r == ?a) + assert("extern c2i", :must, r == LIBTest.test_c2i(?a)) + + i2c = h["test_i2c","CI"] + debug i2c + r,rs = i2c[?a] + debug r,rs + assert("i2c", :may, r == ?a) + assert("exern i2c", :must, r == LIBTest.test_i2c(?a)) + + lcc = h["test_lcc","LCC"] + debug lcc + r,rs = lcc[1,2] + assert("lcc", :may, r == 3) + assert("extern lcc", :must, r == LIBTest.test_lcc(1,2)) + + f2d = h["test_f2d","DF"] + debug f2d + r,rs = f2d[20.001] + debug r,rs + assert("f2d", :may, r.to_i == 20) + assert("extern f2d", :must, r = LIBTest.test_f2d(20.001)) + + d2f = h["test_d2f","FD"] + debug d2f + r,rs = d2f[20.001] + debug r,rs + assert("d2f", :may, r.to_i == 20) + assert("extern d2f", :must, r == LIBTest.test_d2f(20.001)) + + strlen = h["test_strlen","IS"] + debug strlen + r,rs = strlen["0123456789"] + debug r,rs + assert("strlen", :must, r == 10) + assert("extern strlen", :must, r == LIBTest.test_strlen("0123456789")) + + isucc = h["test_isucc","II"] + debug isucc + r,rs = isucc[2] + debug r,rs + assert("isucc", :must, r == 3) + assert("extern isucc", :must, r == LIBTest.test_isucc(2)) + + lsucc = h["test_lsucc","LL"] + debug lsucc + r,rs = lsucc[10000000] + debug r,rs + assert("lsucc", :must, r == 10000001) + assert("extern lsucc", :must, r == LIBTest.test_lsucc(10000000)) + + succ = h["test_succ","0l"] + debug succ + r,rs = succ[0] + debug r,rs + assert("succ", :must, rs[0] == 1) + l = DL.malloc(DL.sizeof("L")) + l.struct!("L",:lval) + LIBTest.test_succ(l) + assert("extern succ", :must, rs[0] == l[:lval]) + + arylen = h["test_arylen","IA"] + debug arylen + r,rs = arylen[["a","b","c","d",nil]] + debug r,rs + assert("arylen", :must, r == 4) + + arylen = h["test_arylen","IP"] + debug arylen + r,rs = arylen[["a","b","c","d",nil]] + debug r,rs + assert("arylen", :must, r == 4) + assert("extern arylen", :must, r == LIBTest.test_arylen(["a","b","c","d",nil])) + + append = h["test_append","0aIS"] + debug append + r,rs = append[["a","b","c"],3,"x"] + debug r,rs + assert("append", :must, rs[0].to_a('S',3) == ["ax","bx","cx"]) + + LIBTest.test_append(["a","b","c"],3,"x") + assert("extern append", :must, rs[0].to_a('S',3) == LIBTest._args_[0].to_a('S',3)) + + strcat = h["test_strcat","SsS"] + debug strcat + r,rs = strcat["abc\0","x"] + debug r,rs + assert("strcat", :must, rs[0].to_s == "abcx") + + init = h["test_init","0iP"] + debug init + argc = 3 + argv = ["arg0","arg1","arg2"].to_ptr + r,rs = init[argc, argv.ref] + debug r,rs +} + + +h = DL.dlopen($LIB) + +sym_open = h["test_open", "PSS"] +sym_gets = h["test_gets", "SsIP"] +sym_close = h["test_close", "0P"] +debug sym_open,sym_gets,sym_close + +line = "Hello world!\n" +File.open("tmp.txt", "w"){|f| + f.print(line) +} + +fp,rs = sym_open["tmp.txt", "r"] +if( fp ) + fp.free = sym_close + r,rs = sym_gets[" " * 256, 256, fp] + debug r,rs + assert("open,gets", :must, rs[0] == line) +else + assert("open,gets", :must, line == nil) +end +File.unlink("tmp.txt") + + +callback1 = h["test_callback1"] +debug callback1 +r,rs = h["test_call_func1", "IP"][callback1] +debug r,rs +assert("callback1", :must, r == 1) + + +callback2 = DL.set_callback("LLP", 0){|arg1,arg2| + ptr = arg2 # DL::PtrData.new(arg2) + msg = ptr.to_s + print("user defined callback function", + "(err = #{arg1}, msg = '#{msg}')\n") + 2 +} +debug callback2 +r,rs = h["test_call_func1", "IP"][callback2] +debug r,rs +assert("callback2", :must, r == 2) + + +ptr = DL.malloc(DL.sizeof('CL')) +ptr.struct!("CL", :c, :l) +ptr["c"] = 0 +ptr["l"] = 0 +r,rs = h["test_fill_test_struct","0PIL"][ptr,100,1000] +debug r,rs +assert("fill_test_struct", :must, ptr["c"] == 100, ptr["l"] == 1000) +assert("fill_test_struct", :must, ptr[:c] == 100, ptr[:l] == 1000) unless (Fixnum === :-) + + +r,rs = h["test_alloc_test_struct", "PIL"][100,200] +r.free = DL::FREE +r.struct!("CL", :c, :l) +assert("alloc_test_struct", :must, r["c"] == 100, r["l"] == 200) +assert("alloc_test_struct", :must, r[:c] == 100, r[:l] == 200) unless (Fixnum === :-) + +ptr = h["test_strlen"] +sym1 = DL::Symbol.new(ptr,"foo","0") +sym2 = h["test_strlen","LS"] +assert("Symbol.new", :must, ptr == sym1.to_ptr, sym1.to_ptr == sym2.to_ptr) + +set_val = h["test_set_long_value","0"] +get_val = h["test_get_long_value","L"] +lval = get_val[][0] +ptr = h["internal_long_value"] +ptr.struct!("l", :l) +assert("get value", :must, ptr["l"] == lval) +assert("get value", :must, ptr[:l] == lval) unless (Fixnum === :-) +ptr["l"] = 200 +lval = get_val[][0] +assert("set value", :must, ptr["l"] == lval) +assert("set value", :must, ptr[:l] == lval) unless (Fixnum === :-) + + +data_init = h["test_data_init", "P"] +data_add = h["test_data_add", "0PS"] +data_print = h["test_data_print", "0P"] +data_aref = h["test_data_aref", "PPI"] +r,rs = data_init[] +ptr = r +data_add[ptr, "name1"] +data_add[ptr, "name2"] +data_add[ptr, "name3"] +data_print[ptr] + +r,rs = data_aref[ptr, 1] +ptr = r +ptr.struct!("C1024P", :name, :next) +assert("data_aref", :must, + ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name2") +assert("data_aref", :must, + ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name2") unless (Fixnum === :-) +ptr = ptr["next"] +ptr.struct!("C1024P", :name, :next) +assert("data_aref", :must, + ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name1") +assert("data_aref", :must, + ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name1") unless (Fixnum === :-) + +GC.start diff --git a/ext/dl/type.rb b/ext/dl/type.rb new file mode 100644 index 000000000..804420c39 --- /dev/null +++ b/ext/dl/type.rb @@ -0,0 +1,115 @@ +# example: +# DLTYPE[INT][:rb2c]["arg0"] => "NUM2INT(arg0)" +# DLTYPE[DOUBLE][:c2rb]["r"] => "rb_float_new(r)" + +DLTYPE = { + VOID = 0x00 => { + :name => 'VOID', + :rb2c => nil, + :c2rb => nil, + :ctype => "void", + :stmem => "v", + :sym => true, + :cb => true, + }, + CHAR = 0x01 => { + :name => 'CHAR', + :rb2c => proc{|x| "NUM2CHR(#{x})"}, + :c2rb => proc{|x| "CHR2FIX(#{x})"}, + :ctype => "char", + :stmem => "c", + :sym => false, + :cb => false, + }, + SHORT = 0x02 => { + :name => 'SHORT', + :rb2c => proc{|x| "FIX2INT(#{x})"}, + :c2rb => proc{|x| "INT2FIX(#{x})"}, + :ctype => "short", + :stmem => "h", + :sym => false, + :cb => false, + }, + INT = 0x03 => { + :name => 'INT', + :rb2c => proc{|x| "NUM2INT(#{x})"}, + :c2rb => proc{|x| "INT2NUM(#{x})"}, + :ctype => "int", + :stmem => "i", + :sym => true, + :cb => false, + }, + LONG = 0x04 => { + :name => 'LONG', + :rb2c => proc{|x| "NUM2INT(#{x})"}, + :c2rb => proc{|x| "INT2NUM(#{x})"}, + :ctype => "long", + :stmem => "l", + :sym => true, + :cb => true, + }, + FLOAT = 0x05 => { + :name => 'FLOAT', + :rb2c => proc{|x| "(float)(RFLOAT(#{x})->value)"}, + :c2rb => proc{|x| "rb_float_new((double)#{x})"}, + :ctype => "float", + :stmem => "f", + :sym => false, + :cb => false, + }, + DOUBLE = 0x06 => { + :name => 'DOUBLE', + :rb2c => proc{|x| "RFLOAT(#{x})->value"}, + :c2rb => proc{|x| "rb_float_new(#{x})"}, + :ctype => "double", + :stmem => "d", + :sym => true, + :cb => true, + }, + VOIDP = 0x07 => { + :name => 'VOIDP', + :rb2c => proc{|x| "rb_dlptr2cptr(#{x})"}, + :c2rb => proc{|x| "rb_dlptr_new(#{x},sizeof(void*),0)"}, + :ctype => "void *", + :stmem => "p", + :sym => true, + :cb => true, + }, +} + +def tpush(t, x) + (t << 3)|x +end + +def tget(t, i) + (t & (0x07 << (i * 3))) >> (i * 3) +end + +def types2num(types) + res = 0x00 + r = types.reverse + r.each{|t| + res = tpush(res,t) + } + res +end + +def num2types(num) + ts = [] + i = 0 + t = tget(num,i) + while( (t != VOID && i > 0) || (i == 0) ) + ts.push(DLTYPE[t][:ctype]) + i += 1 + t = tget(num,i) + end + ts +end + +def types2ctypes(types) + res = [] + types.each{|t| + res.push(DLTYPE[t][:ctype]) + } + res +end |