Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support array argument in ffi_lib #2404

Merged
merged 6 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions lib/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,29 @@ Value init_ffi(Env *env, Value self) {
Value FFI_Library_ffi_lib(Env *env, Value self, Args &&args, Block *) {
args.ensure_argc_is(env, 1);
auto name = args.at(0);
name->assert_type(env, Object::Type::String, "String");
auto handle = dlopen(name->as_string()->c_str(), RTLD_LAZY);
if (!handle)
env->raise("LoadError", "Could not open library '{}': {}.", name->as_string()->c_str(), dlerror());
void *handle = nullptr;
if (name->is_array()) {
for (auto name2 : *name->as_array()) {
name2->assert_type(env, Object::Type::String, "String");
handle = dlopen(name2->as_string()->c_str(), RTLD_LAZY);
if (handle) {
name = name2;
break;
}
}
if (!handle) {
auto error = new StringObject;
for (auto name2 : *name->as_array())
error->append_sprintf("Could not open library '%s': %s.\n", name2->as_string()->string().c_str(), dlerror());
error->chomp_in_place(env, nullptr);
env->raise("LoadError", error->string());
}
} else {
name->assert_type(env, Object::Type::String, "String");
handle = dlopen(name->as_string()->c_str(), RTLD_LAZY);
if (!handle)
env->raise("LoadError", "Could not open library '{}': {}.", name->as_string()->c_str(), dlerror());
}
auto handle_ptr = new VoidPObject { handle, [](auto p) { dlclose(p->void_ptr()); } };
auto libs = self->ivar_get(env, "@ffi_libs"_s);
if (libs->is_nil())
Expand Down
35 changes: 34 additions & 1 deletion test/natalie/ffi_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module LibRubyParser
describe 'FFI' do
it 'raises an error if the library cannot be found' do
lambda do
module Foo
Module.new do
extend FFI::Library
ffi_lib "non_existent.so"
end
Expand All @@ -44,6 +44,39 @@ module Foo
)
end

it 'supports an array of strings as library and uses the first one available' do
foo = Module.new do
extend FFI::Library
ffi_lib ['non_existent.so', STUB_LIBRARY_PATH, PRISM_LIBRARY_PATH]
end
libs = foo.instance_variable_get(:@ffi_libs)
libs.size.should == 1
lib = libs.first
lib.should be_an_instance_of FFI::DynamicLibrary
lib.name.should == STUB_LIBRARY_PATH
end

it 'raises an error if no library can be found' do
lambda do
Module.new do
extend FFI::Library
ffi_lib ["non_existent.so", "neither_existent.so"]
end
end.should raise_error(
LoadError,
/Could not open library.*non_existent\.so.*Could not open library.*neither_existent\.so/m
)
end

it 'raises an error if an empty list is provided' do
lambda do
Module.new do
extend FFI::Library
ffi_lib []
end
end.should raise_error(LoadError)
end

it 'links to a shared object' do
libs = LibRubyParser.instance_variable_get(:@ffi_libs)
libs.size.should == 1
Expand Down
Loading