Skip to content

Commit

Permalink
Add sym_defined? methods to test if a symbol is defined (#108)
Browse files Browse the repository at this point in the history
I would like to check if a symbol is defined before trying to access it.
Some symbols aren't available on all platforms, so instead of raising an
exception, I want to check if it's defined first.

Today we have to do:

```ruby
begin
  addr = Fiddle::Handle.sym("something")
  # do something
rescue Fiddle::DLError
end
```

I want to write this:

```ruby
if Fiddle::Handle.sym_defined?("something")
  addr = Fiddle::Handle.sym("something")
  # do something
end
```

Co-authored-by: Sutou Kouhei <kou@clear-code.com>
  • Loading branch information
tenderlove and kou committed Sep 13, 2022
1 parent 49fa723 commit 9d3371d
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 4 deletions.
59 changes: 55 additions & 4 deletions ext/fiddle/handle.c
Expand Up @@ -321,22 +321,24 @@ rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
return fiddle_handle_sym(RTLD_NEXT, sym);
}

static VALUE
fiddle_handle_sym(void *handle, VALUE symbol)
typedef void (*fiddle_void_func)(void);

static fiddle_void_func
fiddle_handle_find_func(void *handle, VALUE symbol)
{
#if defined(HAVE_DLERROR)
const char *err;
# define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; }
#else
# define CHECK_DLERROR
#endif
void (*func)();
fiddle_void_func func;
const char *name = StringValueCStr(symbol);

#ifdef HAVE_DLERROR
dlerror();
#endif
func = (void (*)())(VALUE)dlsym(handle, name);
func = (fiddle_void_func)(VALUE)dlsym(handle, name);
CHECK_DLERROR;
#if defined(FUNC_STDCALL)
if( !func ){
Expand Down Expand Up @@ -379,6 +381,53 @@ fiddle_handle_sym(void *handle, VALUE symbol)
xfree(name_n);
}
#endif

return func;
}

static VALUE
rb_fiddle_handle_s_sym_defined(VALUE self, VALUE sym)
{
fiddle_void_func func;

func = fiddle_handle_find_func(RTLD_NEXT, sym);

if( func ) {
return PTR2NUM(func);
}
else {
return Qnil;
}
}

static VALUE
rb_fiddle_handle_sym_defined(VALUE self, VALUE sym)
{
struct dl_handle *fiddle_handle;
fiddle_void_func func;

TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
if( ! fiddle_handle->open ){
rb_raise(rb_eFiddleDLError, "closed handle");
}

func = fiddle_handle_find_func(fiddle_handle->ptr, sym);

if( func ) {
return PTR2NUM(func);
}
else {
return Qnil;
}
}

static VALUE
fiddle_handle_sym(void *handle, VALUE symbol)
{
fiddle_void_func func;

func = fiddle_handle_find_func(handle, symbol);

if( !func ){
rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
}
Expand Down Expand Up @@ -468,6 +517,7 @@ Init_fiddle_handle(void)
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, "sym_defined?", rb_fiddle_handle_s_sym_defined, 1);
rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);

/* Document-const: NEXT
Expand Down Expand Up @@ -526,6 +576,7 @@ Init_fiddle_handle(void)
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, "sym_defined?", rb_fiddle_handle_sym_defined, 1);
rb_define_method(rb_cHandle, "file_name", rb_fiddle_handle_file_name, 0);
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);
Expand Down
4 changes: 4 additions & 0 deletions test/fiddle/test_handle.rb
Expand Up @@ -22,12 +22,14 @@ def test_to_ptr
def test_static_sym_unknown
assert_raise(DLError) { Fiddle::Handle.sym('fooo') }
assert_raise(DLError) { Fiddle::Handle['fooo'] }
refute Fiddle::Handle.sym_defined?('fooo')
end

def test_static_sym
begin
# Linux / Darwin / FreeBSD
refute_nil Fiddle::Handle.sym('dlopen')
assert Fiddle::Handle.sym_defined?('dlopen')
assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen']
return
rescue
Expand All @@ -54,6 +56,7 @@ def test_sym_unknown
handle = Fiddle::Handle.new(LIBC_SO)
assert_raise(DLError) { handle.sym('fooo') }
assert_raise(DLError) { handle['fooo'] }
refute handle.sym_defined?('fooo')
end

def test_sym_with_bad_args
Expand All @@ -66,6 +69,7 @@ def test_sym
handle = Handle.new(LIBC_SO)
refute_nil handle.sym('calloc')
refute_nil handle['calloc']
assert handle.sym_defined?('calloc')
end

def test_handle_close
Expand Down

0 comments on commit 9d3371d

Please sign in to comment.