Add ruby-dl

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2324 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ttate 2002-04-02 10:56:13 +00:00
parent 64b6406445
commit 7d711b817e
30 changed files with 5872 additions and 0 deletions

View File

@ -9,6 +9,7 @@
#digest/rmd160
#digest/sha1
#digest/sha2
#dl
#etc
#fcntl
#gdbm

166
ext/dl/Changes Normal file
View File

@ -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.

24
ext/dl/MANIFEST Normal file
View File

@ -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

186
ext/dl/README Normal file
View File

@ -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

247
ext/dl/README.html Normal file
View File

@ -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>

45
ext/dl/depend Normal file
View File

@ -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

655
ext/dl/dl.c Normal file
View File

@ -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);
};

70
ext/dl/dl.def Normal file
View File

@ -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

282
ext/dl/dl.h Normal file
View File

@ -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 */

197
ext/dl/extconf.rb Normal file
View File

@ -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')

500
ext/dl/h2rb Normal file
View File

@ -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)

207
ext/dl/handle.c Normal file
View File

@ -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);
};

49
ext/dl/install.rb Normal file
View File

@ -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()

228
ext/dl/lib/dl/import.rb Normal file
View File

@ -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

26
ext/dl/lib/dl/win32.rb Normal file
View File

@ -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

68
ext/dl/mkcall.rb Normal file
View File

@ -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
}

83
ext/dl/mkcallback.rb Normal file
View File

@ -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
}

42
ext/dl/mkcbtable.rb Normal file
View File

@ -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
}

1075
ext/dl/ptr.c Normal file

File diff suppressed because it is too large Load Diff

70
ext/dl/sample/drives.rb Normal file
View File

@ -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")
}

5
ext/dl/sample/getch.rb Normal file
View File

@ -0,0 +1,5 @@
require 'dl'
crtdll = DL::dlopen("crtdll")
getch = crtdll['_getch', 'L']
print(getch.call, "\n")

84
ext/dl/sample/libc.rb Normal file
View File

@ -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)

19
ext/dl/sample/msgbox.rb Normal file
View File

@ -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

18
ext/dl/sample/msgbox2.rb Normal file
View File

@ -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

87
ext/dl/sample/stream.rb Normal file
View File

@ -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
}

771
ext/dl/sym.c Normal file
View File

@ -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);
};

29
ext/dl/test/libtest.def Normal file
View File

@ -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

251
ext/dl/test/test.c Normal file
View File

@ -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;
};

272
ext/dl/test/test.rb Normal file
View File

@ -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

115
ext/dl/type.rb Normal file
View File

@ -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