From 9d3371de13f45f0809a0423488c2646d90df6172 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Sep 2022 08:27:59 -0700 Subject: [PATCH] Add `sym_defined?` methods to test if a symbol is defined (#108) 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 --- ext/fiddle/handle.c | 59 +++++++++++++++++++++++++++++++++++--- test/fiddle/test_handle.rb | 4 +++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c index 76b90909..ae8cc3a5 100644 --- a/ext/fiddle/handle.c +++ b/ext/fiddle/handle.c @@ -321,8 +321,10 @@ 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; @@ -330,13 +332,13 @@ fiddle_handle_sym(void *handle, VALUE symbol) #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 ){ @@ -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); } @@ -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 @@ -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); diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb index 7e3ff9d8..3bb80b75 100644 --- a/test/fiddle/test_handle.rb +++ b/test/fiddle/test_handle.rb @@ -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 @@ -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 @@ -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