summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--ext/dl/lib/dl/struct.rb2
-rw-r--r--ext/fiddle/extconf.rb14
-rw-r--r--ext/fiddle/fiddle.c196
-rw-r--r--ext/fiddle/fiddle.h27
-rw-r--r--ext/fiddle/function.c12
-rw-r--r--ext/fiddle/handle.c465
-rw-r--r--ext/fiddle/lib/fiddle.rb16
-rw-r--r--ext/fiddle/lib/fiddle/function.rb7
-rw-r--r--ext/fiddle/pointer.c691
-rw-r--r--test/dl/test_callback.rb2
-rw-r--r--test/fiddle/helper.rb5
-rw-r--r--test/fiddle/test_closure.rb4
-rw-r--r--test/fiddle/test_fiddle.rb21
-rw-r--r--test/fiddle/test_func.rb79
-rw-r--r--test/fiddle/test_handle.rb186
-rw-r--r--test/fiddle/test_pointer.rb231
17 files changed, 1949 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index 17be602..40eb8a1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+Wed Nov 28 04:53:40 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/fiddle/handle.c: Make Fiddle independent of DL, copy DL::Handle
+ to Fiddle::Handle.
+ * ext/fiddle/pointer.c: Make Fiddle independent of DL, copy
+ DL::Pointer to Fiddle::Pointer.
+ * test/fiddle/test_func.rb: relevent tests
+ * test/fiddle/test_handle.rb: ditto
+ * test/fiddle/test_pointer.rb: ditto
+ * ext/dl/lib/dl/struct.rb: use Fiddle::Pointer if available
+ * ext/fiddle/extconf.rb: check for dlfcn.h
+ * ext/fiddle/fiddle.c: add constants for sizeof() things
+ * ext/fiddle/fiddle.h: include dlfcn.h
+ * ext/fiddle/function.c: expose a C function for creating new
+ Fiddle::Function objects.
+ * ext/fiddle/lib/fiddle.rb: include constants for dl backwards compat
+ * ext/fiddle/lib/fiddle/function.rb: read the pointer from the
+ function for dl backwards compat.
+ * test/dl/test_callback.rb: check the addresses of the pointers rather
+ than their types.
+ * test/fiddle/helper.rb: remove dependency on dl
+ * test/fiddle/test_closure.rb: ditto
+ * test/fiddle/test_fiddle.rb: ditto
+
Wed Nov 28 03:03:28 2012 NARUSE, Yui <naruse@ruby-lang.org>
* configure.in (opt-dir): don't use non portable flag -E of sed.
diff --git a/ext/dl/lib/dl/struct.rb b/ext/dl/lib/dl/struct.rb
index 9359ca4..e2d91a6 100644
--- a/ext/dl/lib/dl/struct.rb
+++ b/ext/dl/lib/dl/struct.rb
@@ -76,7 +76,7 @@ module DL
end
# A C struct wrapper
- class CStructEntity < CPtr
+ class CStructEntity < (DL.fiddle? ? Fiddle::Pointer : CPtr)
include PackInfo
include ValueUtil
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index a9cdcc2..9b9803f 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -19,6 +19,20 @@ end
have_header 'sys/mman.h'
+if have_header "dlfcn.h"
+ have_library "dl"
+
+ %w{ dlopen dlclose dlsym }.each do |func|
+ abort "missing function #{func}" unless have_func(func)
+ end
+
+ have_func "dlerror"
+elsif have_header "windows.h"
+ %w{ LoadLibrary FreeLibrary GetProcAddress }.each do |func|
+ abort "missing function #{func}" unless have_func(func)
+ end
+end
+
config = File.read(RbConfig.expand(File.join($arch_hdrdir, "ruby/config.h")))
types = {"SIZE_T"=>"SSIZE_T", "PTRDIFF_T"=>nil, "INTPTR_T"=>nil}
types.each do |type, signed|
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index 3006bbe..06b0863 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -1,6 +1,7 @@
#include <fiddle.h>
VALUE mFiddle;
+VALUE rb_eFiddleError;
#ifndef TYPE_SSIZE_T
# if SIZEOF_SIZE_T == SIZEOF_INT
@@ -34,6 +35,100 @@ VALUE mFiddle;
#endif
#define TYPE_UINTPTR_T (-TYPE_INTPTR_T)
+void Init_fiddle_pointer(void);
+
+/*
+ * call-seq: Fiddle.malloc(size)
+ *
+ * Allocate +size+ bytes of memory and return the integer memory address
+ * for the allocated memory.
+ */
+static VALUE
+rb_fiddle_malloc(VALUE self, VALUE size)
+{
+ void *ptr;
+
+ rb_secure(4);
+ ptr = (void*)ruby_xmalloc(NUM2INT(size));
+ return PTR2NUM(ptr);
+}
+
+/*
+ * call-seq: Fiddle.realloc(addr, size)
+ *
+ * Change the size of the memory allocated at the memory location +addr+ to
+ * +size+ bytes. Returns the memory address of the reallocated memory, which
+ * may be different than the address passed in.
+ */
+static VALUE
+rb_fiddle_realloc(VALUE self, VALUE addr, VALUE size)
+{
+ void *ptr = NUM2PTR(addr);
+
+ rb_secure(4);
+ ptr = (void*)ruby_xrealloc(ptr, NUM2INT(size));
+ return PTR2NUM(ptr);
+}
+
+/*
+ * call-seq: Fiddle.free(addr)
+ *
+ * Free the memory at address +addr+
+ */
+VALUE
+rb_fiddle_free(VALUE self, VALUE addr)
+{
+ void *ptr = NUM2PTR(addr);
+
+ rb_secure(4);
+ ruby_xfree(ptr);
+ return Qnil;
+}
+
+/*
+ * call-seq: Fiddle.dlunwrap(addr)
+ *
+ * Returns the hexadecimal representation of a memory pointer address +addr+
+ *
+ * Example:
+ *
+ * lib = Fiddle.dlopen('/lib64/libc-2.15.so')
+ * => #<Fiddle::Handle:0x00000001342460>
+ *
+ * lib['strcpy'].to_s(16)
+ * => "7f59de6dd240"
+ *
+ * Fiddle.dlunwrap(Fiddle.dlwrap(lib['strcpy'].to_s(16)))
+ * => "7f59de6dd240"
+ */
+VALUE
+rb_fiddle_ptr2value(VALUE self, VALUE addr)
+{
+ rb_secure(4);
+ return (VALUE)NUM2PTR(addr);
+}
+
+/*
+ * call-seq: Fiddle.dlwrap(val)
+ *
+ * Returns a memory pointer of a function's hexadecimal address location +val+
+ *
+ * Example:
+ *
+ * lib = Fiddle.dlopen('/lib64/libc-2.15.so')
+ * => #<Fiddle::Handle:0x00000001342460>
+ *
+ * Fiddle.dlwrap(lib['strcpy'].to_s(16))
+ * => 25522520
+ */
+static VALUE
+rb_fiddle_value2ptr(VALUE self, VALUE val)
+{
+ return PTR2NUM((void*)val);
+}
+
+void Init_fiddle_handle(void);
+
void
Init_fiddle(void)
{
@@ -47,6 +142,13 @@ Init_fiddle(void)
*/
mFiddle = rb_define_module("Fiddle");
+ /*
+ * Document-class: Fiddle::DLError
+ *
+ * standard dynamic load exception
+ */
+ rb_eFiddleError = rb_define_class_under(mFiddle, "DLError", rb_eStandardError);
+
/* Document-const: TYPE_VOID
*
* C type - void
@@ -143,7 +245,101 @@ Init_fiddle(void)
rb_define_const(mFiddle, "WINDOWS", Qfalse);
#endif
+ /* Document-const: SIZEOF_VOIDP
+ *
+ * size of a void*
+ */
+ rb_define_const(mFiddle, "SIZEOF_VOIDP", INT2NUM(sizeof(void*)));
+
+ /* Document-const: SIZEOF_CHAR
+ *
+ * size of a char
+ */
+ rb_define_const(mFiddle, "SIZEOF_CHAR", INT2NUM(sizeof(char)));
+
+ /* Document-const: SIZEOF_SHORT
+ *
+ * size of a short
+ */
+ rb_define_const(mFiddle, "SIZEOF_SHORT", INT2NUM(sizeof(short)));
+
+ /* Document-const: SIZEOF_INT
+ *
+ * size of an int
+ */
+ rb_define_const(mFiddle, "SIZEOF_INT", INT2NUM(sizeof(int)));
+
+ /* Document-const: SIZEOF_LONG
+ *
+ * size of a long
+ */
+ rb_define_const(mFiddle, "SIZEOF_LONG", INT2NUM(sizeof(long)));
+
+#if HAVE_LONG_LONG
+ /* Document-const: SIZEOF_LONG_LONG
+ *
+ * size of a long long
+ */
+ rb_define_const(mFiddle, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG)));
+#endif
+
+ /* Document-const: SIZEOF_FLOAT
+ *
+ * size of a float
+ */
+ rb_define_const(mFiddle, "SIZEOF_FLOAT", INT2NUM(sizeof(float)));
+
+ /* Document-const: SIZEOF_DOUBLE
+ *
+ * size of a double
+ */
+ rb_define_const(mFiddle, "SIZEOF_DOUBLE",INT2NUM(sizeof(double)));
+
+ /* Document-const: SIZEOF_SIZE_T
+ *
+ * size of a size_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_SIZE_T", INT2NUM(sizeof(size_t)));
+
+ /* Document-const: SIZEOF_SSIZE_T
+ *
+ * size of a ssize_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_SSIZE_T", INT2NUM(sizeof(size_t))); /* same as size_t */
+
+ /* Document-const: SIZEOF_PTRDIFF_T
+ *
+ * size of a ptrdiff_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_PTRDIFF_T", INT2NUM(sizeof(ptrdiff_t)));
+
+ /* Document-const: SIZEOF_INTPTR_T
+ *
+ * size of a intptr_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_INTPTR_T", INT2NUM(sizeof(intptr_t)));
+
+ /* Document-const: SIZEOF_UINTPTR_T
+ *
+ * size of a uintptr_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_UINTPTR_T", INT2NUM(sizeof(uintptr_t)));
+
+ /* Document-const: RUBY_FREE
+ *
+ * Address of the ruby_xfree() function
+ */
+ rb_define_const(mFiddle, "RUBY_FREE", PTR2NUM(ruby_xfree));
+
+ rb_define_module_function(mFiddle, "dlwrap", rb_fiddle_value2ptr, 1);
+ rb_define_module_function(mFiddle, "dlunwrap", rb_fiddle_ptr2value, 1);
+ rb_define_module_function(mFiddle, "malloc", rb_fiddle_malloc, 1);
+ rb_define_module_function(mFiddle, "realloc", rb_fiddle_realloc, 2);
+ rb_define_module_function(mFiddle, "free", rb_fiddle_free, 1);
+
Init_fiddle_function();
Init_fiddle_closure();
+ Init_fiddle_handle();
+ Init_fiddle_pointer();
}
/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index 3a829fe..8c8a934 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -12,6 +12,30 @@
#include <sys/mman.h>
#endif
+#if defined(HAVE_DLFCN_H)
+# include <dlfcn.h>
+# /* some stranger systems may not define all of these */
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#else
+# if defined(_WIN32)
+# include <windows.h>
+# define dlopen(name,flag) ((void*)LoadLibrary(name))
+# define dlerror() strerror(rb_w32_map_errno(GetLastError()))
+# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
+# define RTLD_LAZY -1
+# define RTLD_NOW -1
+# define RTLD_GLOBAL -1
+# endif
+#endif
+
#ifdef USE_HEADER_HACKS
#include <ffi/ffi.h>
#else
@@ -98,6 +122,9 @@
#define TYPE_DOUBLE 8
extern VALUE mFiddle;
+extern VALUE rb_eFiddleError;
+
+VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type);
#endif
/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
index 6e89099..7fc5127 100644
--- a/ext/fiddle/function.c
+++ b/ext/fiddle/function.c
@@ -38,6 +38,18 @@ allocate(VALUE klass)
return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
}
+VALUE
+rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
+{
+ VALUE argv[3];
+
+ argv[0] = address;
+ argv[1] = arg_types;
+ argv[2] = ret_type;
+
+ return rb_class_new_instance(3, argv, cFiddleFunction);
+}
+
static VALUE
initialize(int argc, VALUE argv[], VALUE self)
{
diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c
new file mode 100644
index 0000000..346635f
--- /dev/null
+++ b/ext/fiddle/handle.c
@@ -0,0 +1,465 @@
+#include <ruby.h>
+#include <fiddle.h>
+
+VALUE rb_cHandle;
+
+struct dl_handle {
+ void *ptr;
+ int open;
+ int enable_close;
+};
+
+#ifdef _WIN32
+# ifndef _WIN32_WCE
+static void *
+w32_coredll(void)
+{
+ MEMORY_BASIC_INFORMATION m;
+ memset(&m, 0, sizeof(m));
+ if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
+ return m.AllocationBase;
+}
+# endif
+
+static int
+w32_dlclose(void *ptr)
+{
+# ifndef _WIN32_WCE
+ if( ptr == w32_coredll() ) return 0;
+# endif
+ if( FreeLibrary((HMODULE)ptr) ) return 0;
+ return errno = rb_w32_map_errno(GetLastError());
+}
+#define dlclose(ptr) w32_dlclose(ptr)
+#endif
+
+static void
+fiddle_handle_free(void *ptr)
+{
+ struct dl_handle *fiddle_handle = ptr;
+ if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
+ dlclose(fiddle_handle->ptr);
+ }
+}
+
+static size_t
+fiddle_handle_memsize(const void *ptr)
+{
+ return ptr ? sizeof(struct dl_handle) : 0;
+}
+
+static const rb_data_type_t fiddle_handle_data_type = {
+ "fiddle/handle",
+ {0, fiddle_handle_free, fiddle_handle_memsize,},
+};
+
+/*
+ * call-seq: close
+ *
+ * Close this Fiddle::Handle. Calling close more than once will raise a
+ * Fiddle::DLError exception.
+ */
+static VALUE
+rb_fiddle_handle_close(VALUE self)
+{
+ struct dl_handle *fiddle_handle;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ if(fiddle_handle->open) {
+ int ret = dlclose(fiddle_handle->ptr);
+ fiddle_handle->open = 0;
+
+ /* Check dlclose for successful return value */
+ if(ret) {
+#if defined(HAVE_DLERROR)
+ rb_raise(rb_eFiddleError, "%s", dlerror());
+#else
+ rb_raise(rb_eFiddleError, "could not close handle");
+#endif
+ }
+ return INT2NUM(ret);
+ }
+ rb_raise(rb_eFiddleError, "dlclose() called too many times");
+
+ UNREACHABLE;
+}
+
+static VALUE
+rb_fiddle_handle_s_allocate(VALUE klass)
+{
+ VALUE obj;
+ struct dl_handle *fiddle_handle;
+
+ obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ fiddle_handle->ptr = 0;
+ fiddle_handle->open = 0;
+ fiddle_handle->enable_close = 0;
+
+ return obj;
+}
+
+static VALUE
+predefined_fiddle_handle(void *handle)
+{
+ VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle);
+ struct dl_handle *fiddle_handle = DATA_PTR(obj);
+
+ fiddle_handle->ptr = handle;
+ fiddle_handle->open = 1;
+ OBJ_FREEZE(obj);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * initialize(lib = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
+ *
+ * Create a new handler that opens library named +lib+ with +flags+. If no
+ * library is specified, RTLD_DEFAULT is used.
+ */
+static VALUE
+rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
+{
+ void *ptr;
+ struct dl_handle *fiddle_handle;
+ VALUE lib, flag;
+ char *clib;
+ int cflag;
+ const char *err;
+
+ switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
+ case 0:
+ clib = NULL;
+ cflag = RTLD_LAZY | RTLD_GLOBAL;
+ break;
+ case 1:
+ clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
+ cflag = RTLD_LAZY | RTLD_GLOBAL;
+ break;
+ case 2:
+ clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
+ cflag = NUM2INT(flag);
+ break;
+ default:
+ rb_bug("rb_fiddle_handle_new");
+ }
+
+ rb_secure(2);
+
+#if defined(_WIN32)
+ if( !clib ){
+ HANDLE rb_libruby_handle(void);
+ ptr = rb_libruby_handle();
+ }
+ else if( STRCASECMP(clib, "libc") == 0
+# ifdef RUBY_COREDLL
+ || STRCASECMP(clib, RUBY_COREDLL) == 0
+ || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
+# endif
+ ){
+# ifdef _WIN32_WCE
+ ptr = dlopen("coredll.dll", cflag);
+# else
+ ptr = w32_coredll();
+# endif
+ }
+ else
+#endif
+ ptr = dlopen(clib, cflag);
+#if defined(HAVE_DLERROR)
+ if( !ptr && (err = dlerror()) ){
+ rb_raise(rb_eFiddleError, "%s", err);
+ }
+#else
+ if( !ptr ){
+ err = dlerror();
+ rb_raise(rb_eFiddleError, "%s", err);
+ }
+#endif
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
+ dlclose(fiddle_handle->ptr);
+ }
+ fiddle_handle->ptr = ptr;
+ fiddle_handle->open = 1;
+ fiddle_handle->enable_close = 0;
+
+ if( rb_block_given_p() ){
+ rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq: enable_close
+ *
+ * Enable a call to dlclose() when this Fiddle::Handle is garbage collected.
+ */
+static VALUE
+rb_fiddle_handle_enable_close(VALUE self)
+{
+ struct dl_handle *fiddle_handle;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ fiddle_handle->enable_close = 1;
+ return Qnil;
+}
+
+/*
+ * call-seq: disable_close
+ *
+ * Disable a call to dlclose() when this Fiddle::Handle is garbage collected.
+ */
+static VALUE
+rb_fiddle_handle_disable_close(VALUE self)
+{
+ struct dl_handle *fiddle_handle;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ fiddle_handle->enable_close = 0;
+ return Qnil;
+}
+
+/*
+ * call-seq: close_enabled?
+ *
+ * Returns +true+ if dlclose() will be called when this Fiddle::Handle is
+ * garbage collected.
+ */
+static VALUE
+rb_fiddle_handle_close_enabled_p(VALUE self)
+{
+ struct dl_handle *fiddle_handle;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+
+ if(fiddle_handle->enable_close) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq: to_i
+ *
+ * Returns the memory address for this handle.
+ */
+static VALUE
+rb_fiddle_handle_to_i(VALUE self)
+{
+ struct dl_handle *fiddle_handle;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ return PTR2NUM(fiddle_handle);
+}
+
+static VALUE fiddle_handle_sym(void *handle, const char *symbol);
+
+/*
+ * Document-method: sym
+ * Document-method: []
+ *
+ * call-seq: sym(name)
+ *
+ * Get the address as an Integer for the function named +name+.
+ */
+static VALUE
+rb_fiddle_handle_sym(VALUE self, VALUE sym)
+{
+ struct dl_handle *fiddle_handle;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ if( ! fiddle_handle->open ){
+ rb_raise(rb_eFiddleError, "closed handle");
+ }
+
+ return fiddle_handle_sym(fiddle_handle->ptr, StringValueCStr(sym));
+}
+
+#ifndef RTLD_NEXT
+#define RTLD_NEXT NULL
+#endif
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT NULL
+#endif
+
+/*
+ * Document-method: sym
+ * Document-method: []
+ *
+ * call-seq: sym(name)
+ *
+ * Get the address as an Integer for the function named +name+. The function
+ * is searched via dlsym on RTLD_NEXT. See man(3) dlsym() for more info.
+ */
+static VALUE
+rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
+{
+ return fiddle_handle_sym(RTLD_NEXT, StringValueCStr(sym));
+}
+
+static VALUE
+fiddle_handle_sym(void *handle, const char *name)
+{
+#if defined(HAVE_DLERROR)
+ const char *err;
+# define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
+#else
+# define CHECK_DLERROR
+#endif
+ void (*func)();
+
+ rb_secure(2);
+#ifdef HAVE_DLERROR
+ dlerror();
+#endif
+ func = (void (*)())(VALUE)dlsym(handle, name);
+ CHECK_DLERROR;
+#if defined(FUNC_STDCALL)
+ if( !func ){
+ int i;
+ int len = (int)strlen(name);
+ char *name_n;
+#if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
+ {
+ char *name_a = (char*)xmalloc(len+2);
+ strcpy(name_a, name);
+ name_n = name_a;
+ name_a[len] = 'A';
+ name_a[len+1] = '\0';
+ func = dlsym(handle, name_a);
+ CHECK_DLERROR;
+ if( func ) goto found;
+ name_n = xrealloc(name_a, len+6);
+ }
+#else
+ name_n = (char*)xmalloc(len+6);
+#endif
+ memcpy(name_n, name, len);
+ name_n[len++] = '@';
+ for( i = 0; i < 256; i += 4 ){
+ sprintf(name_n + len, "%d", i);
+ func = dlsym(handle, name_n);
+ CHECK_DLERROR;
+ if( func ) break;
+ }
+ if( func ) goto found;
+ name_n[len-1] = 'A';
+ name_n[len++] = '@';
+ for( i = 0; i < 256; i += 4 ){
+ sprintf(name_n + len, "%d", i);
+ func = dlsym(handle, name_n);
+ CHECK_DLERROR;
+ if( func ) break;
+ }
+ found:
+ xfree(name_n);
+ }
+#endif
+ if( !func ){
+ rb_raise(rb_eFiddleError, "unknown symbol \"%s\"", name);
+ }
+
+ return PTR2NUM(func);
+}
+
+void
+Init_fiddle_handle(void)
+{
+ /*
+ * Document-class: Fiddle::Handle
+ *
+ * The Fiddle::Handle is the manner to access the dynamic library
+ *
+ * == Example
+ *
+ * === Setup
+ *
+ * libc_so = "/lib64/libc.so.6"
+ * => "/lib64/libc.so.6"
+ * @handle = Fiddle::Handle.new(libc_so)
+ * => #<Fiddle::Handle:0x00000000d69ef8>
+ *
+ * === Setup, with flags
+ *
+ * libc_so = "/lib64/libc.so.6"
+ * => "/lib64/libc.so.6"
+ * @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
+ * => #<Fiddle::Handle:0x00000000d69ef8>
+ *
+ * === Addresses to symbols
+ *
+ * strcpy_addr = @handle['strcpy']
+ * => 140062278451968
+ *
+ * or
+ *
+ * strcpy_addr = @handle.sym('strcpy')
+ * => 140062278451968
+ *
+ */
+ rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
+ rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
+ rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
+ rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
+
+ /* Document-const: NEXT
+ *
+ * A predefined pseudo-handle of RTLD_NEXT
+ *
+ * Which will find the next occurrence of a function in the search order
+ * after the current library.
+ */
+ rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
+
+ /* Document-const: DEFAULT
+ *
+ * A predefined pseudo-handle of RTLD_DEFAULT
+ *
+ * Which will find the first occurrence of the desired symbol using the
+ * default library search order
+ */
+ rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
+
+ /* Document-const: RTLD_GLOBAL
+ *
+ * rtld Fiddle::Handle flag.
+ *
+ * The symbols defined by this library will be made available for symbol
+ * resolution of subsequently loaded libraries.
+ */
+ rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
+
+ /* Document-const: RTLD_LAZY
+ *
+ * rtld Fiddle::Handle flag.
+ *
+ * Perform lazy binding. Only resolve symbols as the code that references
+ * them is executed. If the symbol is never referenced, then it is never
+ * resolved. (Lazy binding is only performed for function references;
+ * references to variables are always immediately bound when the library
+ * is loaded.)
+ */
+ rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
+
+ /* Document-const: RTLD_NOW
+ *
+ * rtld Fiddle::Handle flag.
+ *
+ * If this value is specified or the environment variable LD_BIND_NOW is
+ * set to a nonempty string, all undefined symbols in the library are
+ * resolved before dlopen() returns. If this cannot be done an error is
+ * returned.
+ */
+ rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
+
+ rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
+ rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
+ rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
+ rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
+ rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
+ rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
+ rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
+ rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
+}
+
+/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb
index 7d55a1f..1c68b7b 100644
--- a/ext/fiddle/lib/fiddle.rb
+++ b/ext/fiddle/lib/fiddle.rb
@@ -1,13 +1,8 @@
require 'fiddle.so'
require 'fiddle/function'
require 'fiddle/closure'
-require 'dl' unless Object.const_defined?(:DL)
module Fiddle
-
- # A reference to DL::CPtr
- Pointer = DL::CPtr
-
if WINDOWS
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
# if none
@@ -31,4 +26,15 @@ module Fiddle
Thread.current[:__DL2_LAST_ERROR__] = error
Thread.current[:__FIDDLE_LAST_ERROR__] = error
end
+
+ def dlopen library
+ Fiddle::Handle.new library
+ end
+ module_function :dlopen
+
+ # Add constants for backwards compat
+
+ RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc:
+ RTLD_LAZY = Handle::RTLD_LAZY # :nodoc:
+ RTLD_NOW = Handle::RTLD_NOW # :nodoc:
end
diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb
index 1657682..f4d41e6 100644
--- a/ext/fiddle/lib/fiddle/function.rb
+++ b/ext/fiddle/lib/fiddle/function.rb
@@ -2,5 +2,12 @@ module Fiddle
class Function
# The ABI of the Function.
attr_reader :abi
+
+ # The address of this function
+ attr_reader :ptr
+
+ def to_i
+ ptr.to_i
+ end
end
end
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c
new file mode 100644
index 0000000..7cf8212
--- /dev/null
+++ b/ext/fiddle/pointer.c
@@ -0,0 +1,691 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#include <ruby/ruby.h>
+#include <ruby/io.h>
+#include <ctype.h>
+#include <fiddle.h>
+
+VALUE rb_cPointer;
+
+typedef void (*freefunc_t)(void*);
+
+struct ptr_data {
+ void *ptr;
+ long size;
+ freefunc_t free;
+ VALUE wrap[2];
+};
+
+#define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj)))
+
+static inline freefunc_t
+get_freefunc(VALUE func, volatile VALUE *wrap)
+{
+ VALUE addrnum;
+ if (NIL_P(func)) {
+ *wrap = 0;
+ return NULL;
+ }
+ addrnum = rb_Integer(func);
+ *wrap = (addrnum != func) ? func : 0;
+ return (freefunc_t)(VALUE)NUM2PTR(addrnum);
+}
+
+static ID id_to_ptr;
+
+static void
+fiddle_ptr_mark(void *ptr)
+{
+ struct ptr_data *data = ptr;
+ if (data->wrap[0]) {
+ rb_gc_mark(data->wrap[0]);
+ }
+ if (data->wrap[1]) {
+ rb_gc_mark(data->wrap[1]);
+ }
+}
+
+static void
+fiddle_ptr_free(void *ptr)
+{
+ struct ptr_data *data = ptr;
+ if (data->ptr) {
+ if (data->free) {
+ (*(data->free))(data->ptr);
+ }
+ }
+}
+
+static size_t
+fiddle_ptr_memsize(const void *ptr)
+{
+ const struct ptr_data *data = ptr;
+ return data ? sizeof(*data) + data->size : 0;
+}
+
+static const rb_data_type_t fiddle_ptr_data_type = {
+ "fiddle/pointer",
+ {fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,},
+};
+
+static VALUE
+rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
+{
+ struct ptr_data *data;
+ VALUE val;
+
+ rb_secure(4);
+ val = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data);
+ data->ptr = ptr;
+ data->free = func;
+ data->size = size;
+ OBJ_TAINT(val);
+
+ return val;
+}
+
+static VALUE
+rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func)
+{
+ return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func);
+}
+
+static VALUE
+rb_fiddle_ptr_malloc(long size, freefunc_t func)
+{
+ void *ptr;
+
+ rb_secure(4);
+ ptr = ruby_xmalloc((size_t)size);
+ memset(ptr,0,(size_t)size);
+ return rb_fiddle_ptr_new(ptr, size, func);
+}
+
+static void *
+rb_fiddle_ptr2cptr(VALUE val)
+{
+ struct ptr_data *data;
+ void *ptr;
+
+ if (rb_obj_is_kind_of(val, rb_cPointer)) {
+ TypedData_Get_Struct(val, struct ptr_data, &fiddle_ptr_data_type, data);
+ ptr = data->ptr;
+ }
+ else if (val == Qnil) {
+ ptr = NULL;
+ }
+ else{
+ rb_raise(rb_eTypeError, "DL::PtrData was expected");
+ }
+
+ return ptr;
+}
+
+static VALUE
+rb_fiddle_ptr_s_allocate(VALUE klass)
+{
+ VALUE obj;
+ struct ptr_data *data;
+
+ rb_secure(4);
+ obj = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data);
+ data->ptr = 0;
+ data->size = 0;
+ data->free = 0;
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * Fiddle::Pointer.new(address) => fiddle_cptr
+ * Fiddle::Pointer.new(address, size) => fiddle_cptr
+ * Fiddle::Pointer.new(address, size, freefunc) => fiddle_cptr
+ *
+ * Create a new pointer to +address+ with an optional +size+ and +freefunc+.
+ * +freefunc+ will be called when the instance is garbage collected.
+ */
+static VALUE
+rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
+{
+ VALUE ptr, sym, size, wrap = 0, funcwrap = 0;
+ struct ptr_data *data;
+ void *p = NULL;
+ freefunc_t f = NULL;
+ long s = 0;
+
+ if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) {
+ VALUE addrnum = rb_Integer(ptr);
+ if (addrnum != ptr) wrap = ptr;
+ p = NUM2PTR(addrnum);
+ }
+ if (argc >= 2) {
+ s = NUM2LONG(size);
+ }
+ if (argc >= 3) {
+ f = get_freefunc(sym, &funcwrap);
+ }
+
+ if (p) {
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ if (data->ptr && data->free) {
+ /* Free previous memory. Use of inappropriate initialize may cause SEGV. */
+ (*(data->free))(data->ptr);
+ }
+ data->wrap[0] = wrap;
+ data->wrap[1] = funcwrap;
+ data->ptr = p;
+ data->size = s;
+ data->free = f;
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ *
+ * Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
+ *
+ * Allocate +size+ bytes of memory and associate it with an optional
+ * +freefunc+ that will be called when the pointer is garbage collected.
+ * +freefunc+ must be an address pointing to a function or an instance of
+ * Fiddle::Function
+ */
+static VALUE
+rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
+{
+ VALUE size, sym, obj, wrap = 0;
+ long s;
+ freefunc_t f;
+
+ switch (rb_scan_args(argc, argv, "11", &size, &sym)) {
+ case 1:
+ s = NUM2LONG(size);
+ f = NULL;
+ break;
+ case 2:
+ s = NUM2LONG(size);
+ f = get_freefunc(sym, &wrap);
+ break;
+ default:
+ rb_bug("rb_fiddle_ptr_s_malloc");
+ }
+
+ obj = rb_fiddle_ptr_malloc(s,f);
+ if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;
+
+ return obj;
+}
+
+/*
+ * call-seq: to_i
+ *
+ * Returns the integer memory location of this DL::CPtr.
+ */
+static VALUE
+rb_fiddle_ptr_to_i(VALUE self)
+{
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ return PTR2NUM(data->ptr);
+}
+
+/*
+ * call-seq: to_value
+ *
+ * Cast this CPtr to a ruby object.
+ */
+static VALUE
+rb_fiddle_ptr_to_value(VALUE self)
+{
+ struct ptr_data *data;
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ return (VALUE)(data->ptr);
+}
+
+/*
+ * call-seq: ptr
+ *
+ * Returns a DL::CPtr that is a dereferenced pointer for this DL::CPtr.
+ * Analogous to the star operator in C.
+ */
+static VALUE
+rb_fiddle_ptr_ptr(VALUE self)
+{
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ return rb_fiddle_ptr_new(*((void**)(data->ptr)),0,0);
+}
+
+/*
+ * call-seq: ref
+ *
+ * Returns a DL::CPtr that is a reference pointer for this DL::CPtr.
+ * Analogous to the ampersand operator in C.
+ */
+static VALUE
+rb_fiddle_ptr_ref(VALUE self)
+{
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ return rb_fiddle_ptr_new(&(data->ptr),0,0);
+}
+
+/*
+ * call-seq: null?
+ *
+ * Returns true if this is a null pointer.
+ */
+static VALUE
+rb_fiddle_ptr_null_p(VALUE self)
+{
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ return data->ptr ? Qfalse : Qtrue;
+}
+
+/*
+ * call-seq: free=(function)
+ *
+ * Set the free function for this pointer to the DL::CFunc in +function+.
+ */
+static VALUE
+rb_fiddle_ptr_free_set(VALUE self, VALUE val)
+{
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ data->free = get_freefunc(val, &data->wrap[1]);
+
+ return Qnil;
+}
+
+/*
+ * call-seq: free
+ *
+ * Get the free function for this pointer. Returns Fiddle::Function.
+ */
+static VALUE
+rb_fiddle_ptr_free_get(VALUE self)
+{
+ struct ptr_data *pdata;
+ VALUE address;
+ VALUE arg_types;
+ VALUE ret_type;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata);
+
+ if (!pdata->free)
+ return Qnil;
+
+ address = PTR2NUM(pdata->free);
+ ret_type = INT2NUM(TYPE_VOID);
+ arg_types = rb_ary_new();
+ rb_ary_push(arg_types, INT2NUM(TYPE_VOIDP));
+
+ return rb_fiddle_new_function(address, arg_types, ret_type);
+}
+
+/*
+ * call-seq:
+ *
+ * ptr.to_s => string
+ * ptr.to_s(len) => string
+ *
+ * Returns the pointer contents as a string. When called with no arguments,
+ * this method will return the contents until the first NULL byte. When
+ * called with +len+, a string of +len+ bytes will be returned.
+ */
+static VALUE
+rb_fiddle_ptr_to_s(int argc, VALUE argv[], VALUE self)
+{
+ struct ptr_data *data;
+ VALUE arg1, val;
+ int len;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ switch (rb_scan_args(argc, argv, "01", &arg1)) {
+ case 0:
+ val = rb_tainted_str_new2((char*)(data->ptr));
+ break;
+ case 1:
+ len = NUM2INT(arg1);
+ val = rb_tainted_str_new((char*)(data->ptr), len);
+ break;
+ default:
+ rb_bug("rb_fiddle_ptr_to_s");
+ }
+
+ return val;
+}
+
+/*
+ * call-seq:
+ *
+ * ptr.to_str => string
+ * ptr.to_str(len) => string
+ *
+ * Returns the pointer contents as a string. When called with no arguments,
+ * this method will return the contents with the length of this pointer's
+ * +size+. When called with +len+, a string of +len+ bytes will be returned.
+ */
+static VALUE
+rb_fiddle_ptr_to_str(int argc, VALUE argv[], VALUE self)
+{
+ struct ptr_data *data;
+ VALUE arg1, val;
+ int len;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ switch (rb_scan_args(argc, argv, "01", &arg1)) {
+ case 0:
+ val = rb_tainted_str_new((char*)(data->ptr),data->size);
+ break;
+ case 1:
+ len = NUM2INT(arg1);
+ val = rb_tainted_str_new((char*)(data->ptr), len);
+ break;
+ default:
+ rb_bug("rb_fiddle_ptr_to_str");
+ }
+
+ return val;
+}
+
+/*
+ * call-seq: inspect
+ *
+ * Returns a string formatted with an easily readable representation of the
+ * internal state of the DL::CPtr
+ */
+static VALUE
+rb_fiddle_ptr_inspect(VALUE self)
+{
+ struct ptr_data *data;
+ char str[1024];
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ snprintf(str, 1023, "#<%s:%p ptr=%p size=%ld free=%p>",
+ rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free);
+ return rb_str_new2(str);
+}
+
+/*
+ * call-seq:
+ * ptr == other => true or false
+ * ptr.eql?(other) => true or false
+ *
+ * Returns true if +other+ wraps the same pointer, otherwise returns
+ * false.
+ */
+static VALUE
+rb_fiddle_ptr_eql(VALUE self, VALUE other)
+{
+ void *ptr1, *ptr2;
+
+ if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qfalse;
+
+ ptr1 = rb_fiddle_ptr2cptr(self);
+ ptr2 = rb_fiddle_ptr2cptr(other);
+
+ return ptr1 == ptr2 ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * ptr <=> other => -1, 0, 1, or nil
+ *
+ * Returns -1 if less than, 0 if equal to, 1 if greater than +other+. Returns
+ * nil if +ptr+ cannot be compared to +other+.
+ */
+static VALUE
+rb_fiddle_ptr_cmp(VALUE self, VALUE other)
+{
+ void *ptr1, *ptr2;
+ SIGNED_VALUE diff;
+
+ if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qnil;
+
+ ptr1 = rb_fiddle_ptr2cptr(self);
+ ptr2 = rb_fiddle_ptr2cptr(other);
+ diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2;
+ if (!diff) return INT2FIX(0);
+ return diff > 0 ? INT2NUM(1) : INT2NUM(-1);
+}
+
+/*
+ * call-seq:
+ * ptr + n => new cptr
+ *
+ * Returns a new DL::CPtr that has been advanced +n+ bytes.
+ */
+static VALUE
+rb_fiddle_ptr_plus(VALUE self, VALUE other)
+{
+ void *ptr;
+ long num, size;
+
+ ptr = rb_fiddle_ptr2cptr(self);
+ size = RPTR_DATA(self)->size;
+ num = NUM2LONG(other);
+ return rb_fiddle_ptr_new((char *)ptr + num, size - num, 0);
+}
+
+/*
+ * call-seq:
+ * ptr - n => new cptr
+ *
+ * Returns a new DL::CPtr that has been moved back +n+ bytes.
+ */
+static VALUE
+rb_fiddle_ptr_minus(VALUE self, VALUE other)
+{
+ void *ptr;
+ long num, size;
+
+ ptr = rb_fiddle_ptr2cptr(self);
+ size = RPTR_DATA(self)->size;
+ num = NUM2LONG(other);
+ return rb_fiddle_ptr_new((char *)ptr - num, size + num, 0);
+}
+
+/*
+ * call-seq:
+ * ptr[index] -> an_integer
+ * ptr[start, length] -> a_string
+ *
+ * Returns integer stored at _index_. If _start_ and _length_ are given,
+ * a string containing the bytes from _start_ of length _length_ will be
+ * returned.
+ */
+static VALUE
+rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self)
+{
+ VALUE arg0, arg1;
+ VALUE retval = Qnil;
+ size_t offset, len;
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
+ switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
+ case 1:
+ offset = NUM2ULONG(arg0);
+ retval = INT2NUM(*((char *)data->ptr + offset));
+ break;
+ case 2:
+ offset = NUM2ULONG(arg0);
+ len = NUM2ULONG(arg1);
+ retval = rb_tainted_str_new((char *)data->ptr + offset, len);
+ break;
+ default:
+ rb_bug("rb_fiddle_ptr_aref()");
+ }
+ return retval;
+}
+
+/*
+ * call-seq:
+ * ptr[index] = int -> int
+ * ptr[start, length] = string or cptr or addr -> string or dl_cptr or addr
+ *
+ * Set the value at +index+ to +int+. Or, set the memory at +start+ until
+ * +length+ with the contents of +string+, the memory from +dl_cptr+, or the
+ * memory pointed at by the memory address +addr+.
+ */
+static VALUE
+rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self)
+{
+ VALUE arg0, arg1, arg2;
+ VALUE retval = Qnil;
+ size_t offset, len;
+ void *mem;
+ struct ptr_data *data;
+
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
+ if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
+ switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
+ case 2:
+ offset = NUM2ULONG(arg0);
+ ((char*)data->ptr)[offset] = NUM2UINT(arg1);
+ retval = arg1;
+ break;
+ case 3:
+ offset = NUM2ULONG(arg0);
+ len = NUM2ULONG(arg1);
+ if (RB_TYPE_P(arg2, T_STRING)) {
+ mem = StringValuePtr(arg2);
+ }
+ else if( rb_obj_is_kind_of(arg2, rb_cPointer) ){
+ mem = rb_fiddle_ptr2cptr(arg2);
+ }
+ else{
+ mem = NUM2PTR(arg2);
+ }
+ memcpy((char *)data->ptr + offset, mem, len);
+ retval = arg2;
+ break;
+ default:
+ rb_bug("rb_fiddle_ptr_aset()");
+ }
+ return retval;
+}
+
+/*
+ * call-seq: size=(size)
+ *
+ * Set the size of this pointer to +size+
+ */
+static VALUE
+rb_fiddle_ptr_size_set(VALUE self, VALUE size)
+{
+ RPTR_DATA(self)->size = NUM2LONG(size);
+ return size;
+}
+
+/*
+ * call-seq: size
+ *
+ * Get the size of this pointer.
+ */
+static VALUE
+rb_fiddle_ptr_size_get(VALUE self)
+{
+ return LONG2NUM(RPTR_DATA(self)->size);
+}
+
+/*
+ * call-seq:
+ * Fiddle::Pointer.to_ptr(val) => cptr
+ * Fiddle::Pointer[val] => cptr
+ *
+ * Get the underlying pointer for ruby object +val+ and return it as a
+ * Fiddle::Pointer object.
+ */
+static VALUE
+rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
+{
+ VALUE ptr, wrap = val, vptr;
+
+ if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){
+ rb_io_t *fptr;
+ FILE *fp;
+ GetOpenFile(val, fptr);
+ fp = rb_io_stdio_file(fptr);
+ ptr = rb_fiddle_ptr_new(fp, 0, NULL);
+ }
+ else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){
+ char *str = StringValuePtr(val);
+ ptr = rb_fiddle_ptr_new(str, RSTRING_LEN(val), NULL);
+ }
+ else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){
+ if (rb_obj_is_kind_of(vptr, rb_cPointer)){
+ ptr = vptr;
+ wrap = 0;
+ }
+ else{
+ rb_raise(rb_eFiddleError, "to_ptr should return a Fiddle::Pointer object");
+ }
+ }
+ else{
+ VALUE num = rb_Integer(val);
+ if (num == val) wrap = 0;
+ ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL);
+ }
+ OBJ_INFECT(ptr, val);
+ if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap;
+ return ptr;
+}
+
+void
+Init_fiddle_pointer(void)
+{
+ id_to_ptr = rb_intern("to_ptr");
+
+ /* Document-class: Fiddle::Pointer
+ *
+ * Fiddle::Pointer is a class to handle C pointers
+ *
+ */
+ rb_cPointer = rb_define_class_under(mFiddle, "Pointer", rb_cObject);
+ rb_define_alloc_func(rb_cPointer, rb_fiddle_ptr_s_allocate);
+ rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1);
+ rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1);
+ rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1);
+ rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1);
+ rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1);
+ rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0);
+ rb_define_method(rb_cPointer, "to_i", rb_fiddle_ptr_to_i, 0);
+ rb_define_method(rb_cPointer, "to_int", rb_fiddle_ptr_to_i, 0);
+ rb_define_method(rb_cPointer, "to_value", rb_fiddle_ptr_to_value, 0);
+ rb_define_method(rb_cPointer, "ptr", rb_fiddle_ptr_ptr, 0);
+ rb_define_method(rb_cPointer, "+@", rb_fiddle_ptr_ptr, 0);
+ rb_define_method(rb_cPointer, "ref", rb_fiddle_ptr_ref, 0);
+ rb_define_method(rb_cPointer, "-@", rb_fiddle_ptr_ref, 0);
+ rb_define_method(rb_cPointer, "null?", rb_fiddle_ptr_null_p, 0);
+ rb_define_method(rb_cPointer, "to_s", rb_fiddle_ptr_to_s, -1);
+ rb_define_method(rb_cPointer, "to_str", rb_fiddle_ptr_to_str, -1);
+ rb_define_method(rb_cPointer, "inspect", rb_fiddle_ptr_inspect, 0);
+ rb_define_method(rb_cPointer, "<=>", rb_fiddle_ptr_cmp, 1);
+ rb_define_method(rb_cPointer, "==", rb_fiddle_ptr_eql, 1);
+ rb_define_method(rb_cPointer, "eql?", rb_fiddle_ptr_eql, 1);
+ rb_define_method(rb_cPointer, "+", rb_fiddle_ptr_plus, 1);
+ rb_define_method(rb_cPointer, "-", rb_fiddle_ptr_minus, 1);
+ rb_define_method(rb_cPointer, "[]", rb_fiddle_ptr_aref, -1);
+ rb_define_method(rb_cPointer, "[]=", rb_fiddle_ptr_aset, -1);
+ rb_define_method(rb_cPointer, "size", rb_fiddle_ptr_size_get, 0);
+ rb_define_method(rb_cPointer, "size=", rb_fiddle_ptr_size_set, 1);
+
+ /* Document-const: NULL
+ *
+ * A NULL pointer
+ */
+ rb_define_const(mFiddle, "NULL", rb_fiddle_ptr_new(0, 0, 0));
+}
diff --git a/test/dl/test_callback.rb b/test/dl/test_callback.rb
index fb24ff0..8ae652b 100644
--- a/test/dl/test_callback.rb
+++ b/test/dl/test_callback.rb
@@ -25,7 +25,7 @@ module DL
func = CFunc.new(addr, TYPE_VOIDP, 'test')
f = Function.new(func, [TYPE_VOIDP])
ptr = CPtr['blah']
- assert_equal ptr, f.call(ptr)
+ assert_equal ptr.to_i, f.call(ptr).to_i
end
def test_callback_return_arbitrary
diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb
index ad69f55..209dedb 100644
--- a/test/fiddle/helper.rb
+++ b/test/fiddle/helper.rb
@@ -1,5 +1,4 @@
require 'minitest/autorun'
-require 'dl'
require 'fiddle'
# FIXME: this is stolen from DL and needs to be refactored.
@@ -104,8 +103,8 @@ Fiddle::LIBM_SO = libm_so
module Fiddle
class TestCase < MiniTest::Unit::TestCase
def setup
- @libc = DL.dlopen(LIBC_SO)
- @libm = DL.dlopen(LIBM_SO)
+ @libc = Fiddle.dlopen(LIBC_SO)
+ @libm = Fiddle.dlopen(LIBM_SO)
end
end
end
diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb
index 381b3f9..56839e7 100644
--- a/test/fiddle/test_closure.rb
+++ b/test/fiddle/test_closure.rb
@@ -57,8 +57,8 @@ module Fiddle
end
%w[INT SHORT CHAR LONG LONG_LONG].each do |name|
- type = DL.const_get("TYPE_#{name}") rescue next
- size = DL.const_get("SIZEOF_#{name}")
+ type = Fiddle.const_get("TYPE_#{name}") rescue next
+ size = Fiddle.const_get("SIZEOF_#{name}")
[[type, size-1, name], [-type, size, "unsigned_"+name]].each do |t, s, n|
define_method("test_conversion_#{n.downcase}") do
arg = nil
diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb
index f910978..4c6ab97 100644
--- a/test/fiddle/test_fiddle.rb
+++ b/test/fiddle/test_fiddle.rb
@@ -4,27 +4,6 @@ rescue LoadError
end
class TestFiddle < Fiddle::TestCase
- def test_constants_match
- [
- :TYPE_VOID,
- :TYPE_VOIDP,
- :TYPE_CHAR,
- :TYPE_SHORT,
- :TYPE_INT,
- :TYPE_LONG,
- :TYPE_LONG_LONG,
- :TYPE_FLOAT,
- :TYPE_DOUBLE,
- :TYPE_SIZE_T,
- :TYPE_SSIZE_T,
- :TYPE_PTRDIFF_T,
- :TYPE_INTPTR_T,
- :TYPE_UINTPTR_T,
- ].each do |name|
- assert_equal(DL.const_get(name), Fiddle.const_get(name), "Fiddle::#{name}")
- end
- end
-
def test_windows_constant
require 'rbconfig'
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb
new file mode 100644
index 0000000..e77229b
--- /dev/null
+++ b/test/fiddle/test_func.rb
@@ -0,0 +1,79 @@
+require_relative 'helper'
+
+module Fiddle
+ class TestFunc < TestCase
+ def test_random
+ f = Function.new(@libc['srand'], [-TYPE_LONG], TYPE_VOID)
+ assert_nil f.call(10)
+ end
+
+ def test_sinf
+ begin
+ f = Function.new(@libm['sinf'], [TYPE_FLOAT], TYPE_FLOAT)
+ rescue Fiddle::DLError
+ skip "libm may not have sinf()"
+ end
+ assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
+ end
+
+ def test_sin
+ f = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
+ assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
+ end
+
+ def test_string
+ stress, GC.stress = GC.stress, true
+ f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
+ buff = "000"
+ str = f.call(buff, "123")
+ assert_equal("123", buff)
+ assert_equal("123", str.to_s)
+ ensure
+ GC.stress = stress
+ end
+
+ def test_isdigit
+ f = Function.new(@libc['isdigit'], [TYPE_INT], TYPE_INT)
+ r1 = f.call(?1.ord)
+ r2 = f.call(?2.ord)
+ rr = f.call(?r.ord)
+ assert_operator r1, :>, 0
+ assert_operator r2, :>, 0
+ assert_equal 0, rr
+ end
+
+ def test_atof
+ f = Function.new(@libc['atof'], [TYPE_VOIDP], TYPE_DOUBLE)
+ r = f.call("12.34")
+ assert_includes(12.00..13.00, r)
+ end
+
+ def test_strtod
+ f = Function.new(@libc['strtod'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_DOUBLE)
+ buff1 = Pointer["12.34"]
+ buff2 = buff1 + 4
+ r = f.call(buff1, - buff2)
+ assert_in_delta(12.34, r, 0.001)
+ end
+
+ def test_qsort1
+ cb = Class.new(Closure) {
+ def call(x, y)
+ Pointer.new(x)[0] <=> Pointer.new(y)[0]
+ end
+ }.new(TYPE_INT, [TYPE_VOIDP, TYPE_VOIDP])
+
+ qsort = Function.new(@libc['qsort'],
+ [TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP],
+ TYPE_VOID)
+ buff = "9341"
+ qsort.call(buff, buff.size, 1, cb)
+ assert_equal("1349", buff)
+
+ bug4929 = '[ruby-core:37395]'
+ buff = "9341"
+ EnvUtil.under_gc_stress {qsort.call(buff, buff.size, 1, cb)}
+ assert_equal("1349", buff, bug4929)
+ end
+ end
+end
diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb
new file mode 100644
index 0000000..f11272a
--- /dev/null
+++ b/test/fiddle/test_handle.rb
@@ -0,0 +1,186 @@
+require_relative 'helper'
+
+module Fiddle
+ class TestHandle < TestCase
+ include Fiddle
+
+ def test_to_i
+ handle = Fiddle::Handle.new(LIBC_SO)
+ assert_kind_of Integer, handle.to_i
+ end
+
+ def test_static_sym_secure
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ Fiddle::Handle.sym('calloc')
+ end.join
+ end
+ end
+
+ def test_static_sym_unknown
+ assert_raises(DLError) { Fiddle::Handle.sym('fooo') }
+ assert_raises(DLError) { Fiddle::Handle['fooo'] }
+ end
+
+ def test_static_sym
+ skip "Fiddle::Handle.sym is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
+ begin
+ # Linux / Darwin / FreeBSD
+ refute_nil Fiddle::Handle.sym('dlopen')
+ assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen']
+ rescue
+ # NetBSD
+ require 'objspace'
+ refute_nil Fiddle::Handle.sym('Init_objspace')
+ assert_equal Fiddle::Handle.sym('Init_objspace'), Fiddle::Handle['Init_objspace']
+ end
+ end
+
+ def test_sym_closed_handle
+ handle = Fiddle::Handle.new(LIBC_SO)
+ handle.close
+ assert_raises(DLError) { handle.sym("calloc") }
+ assert_raises(DLError) { handle["calloc"] }
+ end
+
+ def test_sym_unknown
+ handle = Fiddle::Handle.new(LIBC_SO)
+ assert_raises(DLError) { handle.sym('fooo') }
+ assert_raises(DLError) { handle['fooo'] }
+ end
+
+ def test_sym_with_bad_args
+ handle = Handle.new(LIBC_SO)
+ assert_raises(TypeError) { handle.sym(nil) }
+ assert_raises(TypeError) { handle[nil] }
+ end
+
+ def test_sym_secure
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ handle = Handle.new(LIBC_SO)
+ handle.sym('calloc')
+ end.join
+ end
+ end
+
+ def test_sym
+ handle = Handle.new(LIBC_SO)
+ refute_nil handle.sym('calloc')
+ refute_nil handle['calloc']
+ end
+
+ def test_handle_close
+ handle = Handle.new(LIBC_SO)
+ assert_equal 0, handle.close
+ end
+
+ def test_handle_close_twice
+ handle = Handle.new(LIBC_SO)
+ handle.close
+ assert_raises(DLError) do
+ handle.close
+ end
+ end
+
+ def test_dlopen_returns_handle
+ assert_instance_of Handle, dlopen(LIBC_SO)
+ end
+
+ def test_dlopen_safe
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ dlopen(LIBC_SO)
+ end.join
+ end
+ end
+
+ def test_initialize_safe
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ Handle.new(LIBC_SO)
+ end.join
+ end
+ end
+
+ def test_initialize_noargs
+ handle = Handle.new
+ refute_nil handle['rb_str_new']
+ end
+
+ def test_initialize_flags
+ handle = Handle.new(LIBC_SO, RTLD_LAZY | RTLD_GLOBAL)
+ refute_nil handle['calloc']
+ end
+
+ def test_enable_close
+ handle = Handle.new(LIBC_SO)
+ assert !handle.close_enabled?, 'close is enabled'
+
+ handle.enable_close
+ assert handle.close_enabled?, 'close is not enabled'
+ end
+
+ def test_disable_close
+ handle = Handle.new(LIBC_SO)
+
+ handle.enable_close
+ assert handle.close_enabled?, 'close is enabled'
+ handle.disable_close
+ assert !handle.close_enabled?, 'close is enabled'
+ end
+
+ def test_NEXT
+ begin
+ # Linux / Darwin
+ #
+ # There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find
+ # the first occurrence of the desired symbol using the default library search order. The
+ # latter will find the next occurrence of a function in the search order after the current
+ # library. This allows one to provide a wrapper around a function in another shared
+ # library.
+ # --- Ubuntu Linux 8.04 dlsym(3)
+ handle = Handle::NEXT
+ refute_nil handle['malloc']
+ rescue
+ # BSD
+ #
+ # If dlsym() is called with the special handle RTLD_NEXT, then the search
+ # for the symbol is limited to the shared objects which were loaded after
+ # the one issuing the call to dlsym(). Thus, if the function is called
+ # from the main program, all the shared libraries are searched. If it is
+ # called from a shared library, all subsequent shared libraries are
+ # searched. RTLD_NEXT is useful for implementing wrappers around library
+ # functions. For example, a wrapper function getpid() could access the
+ # "real" getpid() with dlsym(RTLD_NEXT, "getpid"). (Actually, the dlfunc()
+ # interface, below, should be used, since getpid() is a function and not a
+ # data object.)
+ # --- FreeBSD 8.0 dlsym(3)
+ require 'objspace'
+ handle = Handle::NEXT
+ refute_nil handle['Init_objspace']
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_DEFAULT
+ skip "Handle::DEFAULT is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
+ handle = Handle::DEFAULT
+ refute_nil handle['malloc']
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_dlerror
+ # FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
+ # getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if
+ # it calls _nss_cache_cycle_prevention_function with dlsym(3).
+ # So our Fiddle::Handle#sym must call dlerror(3) before call dlsym.
+ # In general uses of dlerror(3) should call it before use it.
+ require 'socket'
+ Socket.gethostbyname("localhost")
+ Fiddle.dlopen("/usr/lib/libc.so").sym('strcpy')
+ end if /freebsd/=~ RUBY_PLATFORM
+ end
+end
diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb
new file mode 100644
index 0000000..ef33b91
--- /dev/null
+++ b/test/fiddle/test_pointer.rb
@@ -0,0 +1,231 @@
+require_relative 'helper'
+require_relative '../ruby/envutil'
+
+module Fiddle
+ class TestPointer < TestCase
+ def dlwrap arg
+ Fiddle.dlwrap arg
+ end
+
+ include Test::Unit::Assertions
+
+ def test_cptr_to_int
+ null = Fiddle::NULL
+ assert_equal(null.to_i, null.to_int)
+ end
+
+ def test_malloc_free_func_int
+ free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+ assert_equal free.to_i, Fiddle::RUBY_FREE.to_i
+
+ ptr = Pointer.malloc(10, free.to_i)
+ assert_equal 10, ptr.size
+ assert_equal free.to_i, ptr.free.to_i
+ end
+
+ def test_malloc_free_func
+ free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+
+ ptr = Pointer.malloc(10, free)
+ assert_equal 10, ptr.size
+ assert_equal free.to_i, ptr.free.to_i
+ end
+
+ def test_to_str
+ str = "hello world"
+ ptr = Pointer[str]
+
+ assert_equal 3, ptr.to_str(3).length
+ assert_equal str, ptr.to_str
+
+ ptr[5] = 0
+ assert_equal "hello\0world", ptr.to_str
+ end
+
+ def test_to_s
+ str = "hello world"
+ ptr = Pointer[str]
+
+ assert_equal 3, ptr.to_s(3).length
+ assert_equal str, ptr.to_s
+
+ ptr[5] = 0
+ assert_equal 'hello', ptr.to_s
+ end
+
+ def test_minus
+ str = "hello world"
+ ptr = Pointer[str]
+ assert_equal ptr.to_s, (ptr + 3 - 3).to_s
+ end
+
+ # TODO: what if the pointer size is 0? raise an exception? do we care?
+ def test_plus
+ str = "hello world"
+ ptr = Pointer[str]
+ new_str = ptr + 3
+ assert_equal 'lo world', new_str.to_s
+ end
+
+ def test_inspect
+ ptr = Pointer.new(0)
+ inspect = ptr.inspect
+ assert_match(/size=#{ptr.size}/, inspect)
+ assert_match(/free=#{sprintf("%#x", ptr.free.to_i)}/, inspect)
+ assert_match(/ptr=#{sprintf("%#x", ptr.to_i)}/, inspect)
+ end
+
+ def test_to_ptr_string
+ str = "hello world"
+ ptr = Pointer[str]
+ assert ptr.tainted?, 'pointer should be tainted'
+ assert_equal str.length, ptr.size
+ assert_equal 'hello', ptr[0,5]
+ end
+
+ def test_to_ptr_io
+ buf = Pointer.malloc(10)
+ File.open(__FILE__, 'r') do |f|
+ ptr = Pointer.to_ptr f
+ fread = Function.new(@libc['fread'],
+ [TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP],
+ TYPE_INT)
+ fread.call(buf.to_i, Fiddle::SIZEOF_CHAR, buf.size - 1, ptr.to_i)
+ end
+
+ File.open(__FILE__, 'r') do |f|
+ assert_equal f.read(9), buf.to_s
+ end
+ end
+
+ def test_to_ptr_with_ptr
+ ptr = Pointer.new 0
+ ptr2 = Pointer.to_ptr Struct.new(:to_ptr).new(ptr)
+ assert_equal ptr, ptr2
+
+ assert_raises(Fiddle::DLError) do
+ Pointer.to_ptr Struct.new(:to_ptr).new(nil)
+ end
+ end
+
+ def test_to_ptr_with_num
+ ptr = Pointer.new 0
+ assert_equal ptr, Pointer[0]
+ end
+
+ def test_equals
+ ptr = Pointer.new 0
+ ptr2 = Pointer.new 0
+ assert_equal ptr2, ptr
+ end
+
+ def test_not_equals
+ ptr = Pointer.new 0
+ refute_equal 10, ptr, '10 should not equal the pointer'
+ end
+
+ def test_cmp
+ ptr = Pointer.new 0
+ assert_nil(ptr <=> 10, '10 should not be comparable')
+ end
+
+ def test_ref_ptr
+ ary = [0,1,2,4,5]
+ addr = Pointer.new(dlwrap(ary))
+ assert_equal addr.to_i, addr.ref.ptr.to_i
+
+ assert_equal addr.to_i, (+ (- addr)).to_i
+ end
+
+ def test_to_value
+ ary = [0,1,2,4,5]
+ addr = Pointer.new(dlwrap(ary))
+ assert_equal ary, addr.to_value
+ end
+
+ def test_free
+ ptr = Pointer.malloc(4)
+ assert_nil ptr.free
+ end
+
+ def test_free=
+ assert_normal_exit(<<-"End", '[ruby-dev:39269]')
+ require 'fiddle'
+ Fiddle::LIBC_SO = #{Fiddle::LIBC_SO.dump}
+ Fiddle::LIBM_SO = #{Fiddle::LIBM_SO.dump}
+ include Fiddle
+ @libc = dlopen(LIBC_SO)
+ @libm = dlopen(LIBM_SO)
+ free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+ ptr = Fiddle::Pointer.malloc(4)
+ ptr.free = free
+ free.ptr
+ ptr.free.ptr
+ End
+
+ free = Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+ ptr = Pointer.malloc(4)
+ ptr.free = free
+
+ assert_equal free.ptr, ptr.free.ptr
+ end
+
+ def test_null?
+ ptr = Pointer.new(0)
+ assert ptr.null?
+ end
+
+ def test_size
+ ptr = Pointer.malloc(4)
+ assert_equal 4, ptr.size
+ Fiddle.free ptr.to_i
+ end
+
+ def test_size=
+ ptr = Pointer.malloc(4)
+ ptr.size = 10
+ assert_equal 10, ptr.size
+ Fiddle.free ptr.to_i
+ end
+
+ def test_aref_aset
+ check = Proc.new{|str,ptr|
+ assert_equal(str.size(), ptr.size())
+ assert_equal(str, ptr.to_s())
+ assert_equal(str[0,2], ptr.to_s(2))
+ assert_equal(str[0,2], ptr[0,2])
+ assert_equal(str[1,2], ptr[1,2])
+ assert_equal(str[1,0], ptr[1,0])
+ assert_equal(str[0].ord, ptr[0])
+ assert_equal(str[1].ord, ptr[1])
+ }
+ str = 'abc'
+ ptr = Pointer[str]
+ check.call(str, ptr)
+
+ str[0] = "c"
+ assert_equal 'c'.ord, ptr[0] = "c".ord
+ check.call(str, ptr)
+
+ str[0,2] = "aa"
+ assert_equal 'aa', ptr[0,2] = "aa"
+ check.call(str, ptr)
+
+ ptr2 = Pointer['cdeeee']
+ str[0,2] = "cd"
+ assert_equal ptr2, ptr[0,2] = ptr2
+ check.call(str, ptr)
+
+ ptr3 = Pointer['vvvv']
+ str[0,2] = "vv"
+ assert_equal ptr3.to_i, ptr[0,2] = ptr3.to_i
+ check.call(str, ptr)
+ end
+
+ def test_null_pointer
+ nullpo = Pointer.new(0)
+ assert_raise(DLError) {nullpo[0]}
+ assert_raise(DLError) {nullpo[0] = 1}
+ end
+ end
+end