Skip to content

Commit b0a75ab

Browse files
committed
Merge pull request #10 from mattn/cfunc_libcall
Cfunc libcall
2 parents 8b9aef1 + b68f6b3 commit b0a75ab

13 files changed

+177
-9
lines changed

mrbgem.rake

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ MRuby::Gem::Specification.new('mruby-cfunc') do |spec|
3737
end
3838
end
3939

40-
spec.mruby.linker.libraries << %w(ffi dl)
40+
spec.linker.libraries << %w(ffi dl pthread)
4141

42-
if `uname`.chomp == 'Darwin'
42+
if ENV['OS'] == 'Windows_NT'
43+
spec.cc.flags << %w(-pthread)
44+
elsif `uname`.chomp == 'Darwin'
4345
spec.cc.flags << %w(-pthread)
4446
spec.linker.flags << %w(-Wl,-allow_stack_execute -all_load)
4547
else

src/cfunc_call.c

+120-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,34 @@
1717
#include <stdbool.h>
1818
#include "ffi.h"
1919

20+
#ifdef _WIN32
21+
#include <windows.h>
22+
static void*
23+
get_proc_address(const char* funcname)
24+
{
25+
HINSTANCE hInst = GetModuleHandle(NULL);
26+
PBYTE pImage = (PBYTE) hInst;
27+
PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER) hInst;
28+
PIMAGE_NT_HEADERS pPE;
29+
PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
30+
31+
if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
32+
return NULL;
33+
pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
34+
if (pPE->Signature != IMAGE_NT_SIGNATURE)
35+
return NULL;
36+
pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
37+
+ pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
38+
for (; pImpDesc->FirstThunk; ++pImpDesc) {
39+
HINSTANCE hLib = LoadLibrary((const char*)(pImage + pImpDesc->Name));
40+
if (hLib) {
41+
void* p = GetProcAddress(hLib, "strcpy");
42+
if (p) return p;
43+
}
44+
}
45+
return NULL;
46+
}
47+
#endif
2048

2149
static mrb_value
2250
cfunc_call(mrb_state *mrb, mrb_value self)
@@ -25,14 +53,18 @@ cfunc_call(mrb_state *mrb, mrb_value self)
2553
mrb_value mresult_type, mname, *margs;
2654
void **values = NULL;
2755
ffi_type **args = NULL;
28-
56+
2957
mrb_get_args(mrb, "oo*", &mresult_type, &mname, &margs, &margc);
30-
58+
3159
void *fp = NULL;
3260
if(mrb_string_p(mname) || mrb_symbol_p(mname)) {
61+
#ifndef _WIN32
3362
void *dlh = dlopen(NULL, RTLD_LAZY);
3463
fp = dlsym(dlh, mrb_string_value_ptr(mrb, mname));
3564
dlclose(dlh);
65+
#else
66+
fp = get_proc_address(mrb_string_value_ptr(mrb, mname));
67+
#endif
3668

3769
if(fp == NULL) {
3870
mrb_raisef(mrb, E_NAME_ERROR, "can't find C function %s", mrb_string_value_ptr(mrb, mname));
@@ -103,7 +135,93 @@ cfunc_call(mrb_state *mrb, mrb_value self)
103135
}
104136

105137

138+
static mrb_value
139+
cfunc_libcall(mrb_state *mrb, mrb_value self)
140+
{
141+
int margc;
142+
mrb_value mresult_type, mlib, mname, *margs;
143+
void **values = NULL;
144+
ffi_type **args = NULL;
145+
146+
mrb_get_args(mrb, "oSo*", &mresult_type, &mlib, &mname, &margs, &margc);
147+
148+
void *fp = NULL;
149+
if((mrb_string_p(mname) || mrb_symbol_p(mname))) {
150+
void *dlh = dlopen(mrb_string_value_ptr(mrb, mlib), RTLD_LAZY);
151+
fp = dlsym(dlh, mrb_string_value_ptr(mrb, mname));
152+
dlclose(dlh);
153+
154+
if(fp == NULL) {
155+
mrb_raisef(mrb, E_NAME_ERROR, "can't find C function %s", mrb_string_value_ptr(mrb, mname));
156+
goto cfunc_call_exit;
157+
}
158+
}
159+
else {
160+
fp = cfunc_pointer_ptr(mname);
161+
if(fp == NULL) {
162+
mrb_raisef(mrb, E_NAME_ERROR, "can't call NULL pointer");
163+
goto cfunc_call_exit;
164+
}
165+
}
166+
167+
args = malloc(sizeof(ffi_type*) * margc);
168+
values = malloc(sizeof(void*) * margc);
169+
mrb_sym sym_to_ffi_value = mrb_intern(mrb, "to_ffi_value");
170+
171+
mrb_value nil_ary[1];
172+
nil_ary[0] = mrb_nil_value();
173+
int i;
174+
for(i = 0; i < margc; ++i) {
175+
if(mrb_respond_to(mrb, margs[i], sym_to_ffi_value)) {
176+
args[i] = mrb_value_to_mrb_ffi_type(mrb, margs[i])->ffi_type_value;
177+
values[i] = cfunc_pointer_ptr(mrb_funcall_argv(mrb, margs[i], sym_to_ffi_value, 1, nil_ary));
178+
}
179+
else {
180+
cfunc_mrb_raise_without_jump(mrb, E_TYPE_ERROR, "ignore argument type %s", mrb_obj_classname(mrb, margs[i]));
181+
goto cfunc_call_exit;
182+
}
183+
}
184+
185+
ffi_type *result_type = rclass_to_mrb_ffi_type(mrb, mrb_class_ptr(mresult_type))->ffi_type_value;
186+
if (result_type == NULL) {
187+
cfunc_mrb_raise_without_jump(mrb, E_ARGUMENT_ERROR, "ignore return type %s", mrb_class_name(mrb, mrb_class_ptr(mresult_type)));
188+
goto cfunc_call_exit;
189+
}
190+
191+
mrb_value mresult = mrb_nil_value();
192+
ffi_cif cif;
193+
if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, margc, result_type, args) == FFI_OK) {
194+
void *result;
195+
if(result_type->size > sizeof(long)) {
196+
result = malloc(result_type->size);
197+
}
198+
else if(result_type->size) {
199+
result = malloc(sizeof(long));
200+
}
201+
else {
202+
result = NULL;
203+
}
204+
ffi_call(&cif, fp, result, values);
205+
206+
if(result) {
207+
mrb_value result_ptr = cfunc_pointer_new_with_pointer(mrb, result, true);
208+
mresult = mrb_funcall(mrb, mresult_type, "refer", 1, result_ptr);
209+
}
210+
}
211+
else {
212+
mrb_raisef(mrb, E_NAME_ERROR, "Can't find C function %s", mname);
213+
goto cfunc_call_exit;
214+
}
215+
216+
cfunc_call_exit:
217+
free(values);
218+
free(args);
219+
return mresult;
220+
}
221+
222+
106223
void init_cfunc_call(mrb_state *mrb, struct RClass* module)
107224
{
108225
mrb_define_class_method(mrb, module, "call", cfunc_call, ARGS_ANY());
226+
mrb_define_module_function(mrb, module, "libcall", cfunc_libcall, ARGS_ANY());
109227
}

src/cfunc_platform.c

+12
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,23 @@ cfunc_platform_is_win32(mrb_state *mrb, mrb_value self)
3030
}
3131

3232

33+
static mrb_value
34+
cfunc_platform_is_macosx(mrb_state *mrb, mrb_value self)
35+
{
36+
#ifdef __APPLE__
37+
return mrb_true_value();
38+
#else
39+
return mrb_false_value();
40+
#endif
41+
}
42+
43+
3344
void init_cfunc_platform(mrb_state *mrb, struct RClass* module)
3445
{
3546
struct cfunc_state *state = cfunc_state(mrb, module);
3647
struct RClass *struct_class = mrb_define_class_under(mrb, module, "Platform", mrb->object_class);
3748

3849
mrb_define_class_method(mrb, struct_class, "is_posix?", cfunc_platform_is_posix, ARGS_NONE());
3950
mrb_define_class_method(mrb, struct_class, "is_win32?", cfunc_platform_is_win32, ARGS_NONE());
51+
mrb_define_class_method(mrb, struct_class, "is_macosx?", cfunc_platform_is_macosx, ARGS_NONE());
4052
}

test/call_strcpy.rb

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
mobiruby_test "CFunc::call_strcpy" do
22
ptr = CFunc::Pointer.malloc(12)
33

4-
result = CFunc::call(CFunc::Pointer, "strcpy", ptr, "Hello")
4+
if CFunc::Platform.is_win32?
5+
result = CFunc::libcall(CFunc::Pointer, "msvcrt.dll", "strcpy", ptr, "Hello")
6+
else
7+
result = CFunc::call(CFunc::Pointer, "strcpy", ptr, "Hello")
8+
end
59
assert_equal "Hello", result.to_s
610
assert_equal "Hello", ptr.to_s
711

8-
result = CFunc::call(CFunc::Pointer, :strcat, ptr, " World")
12+
if CFunc::Platform.is_win32?
13+
result = CFunc::libcall(CFunc::Pointer, "msvcrt.dll", :strcat, ptr, " World")
14+
else
15+
result = CFunc::call(CFunc::Pointer, :strcat, ptr, " World")
16+
end
917
assert_equal "Hello World", result.to_s
1018
assert_equal "Hello World", ptr.to_s
1119
end

test/call_struct1.rb

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class STest < CFunc::Struct
77
end
88

99
mobiruby_test "CFunc::call_struct1" do
10+
skip if CFunc::Platform.is_win32?
11+
1012
stest = STest.new
1113

1214
assert_equal 10, stest[:x] = 10

test/call_struct2.rb

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class ::STest2 < CFunc::Struct
1414
end
1515

1616
mobiruby_test "CFunc::call_struct2" do
17+
skip if CFunc::Platform.is_win32?
18+
1719
stest2 = ::STest2.new
1820

1921
assert_equal 10, stest2[:s][:x] = 10

test/closure1.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mobiruby_test "CFunc::closure1" do
2+
skip if CFunc::Platform.is_win32?
3+
24
val = 5
35
callback = CFunc::Closure.new(CFunc::Int, [CFunc::SInt32, CFunc::SInt32]) do |a, b|
46
a.to_i * b.to_i * val

test/closure2.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mobiruby_test "CFunc::closure2" do
2+
skip if CFunc::Platform.is_win32?
3+
24
compar = CFunc::Closure.new(CFunc::Int, [CFunc::Pointer(CFunc::Int), CFunc::Pointer(CFunc::Int)]) do |a, b|
35
a.refer.to_i - b.refer.to_i
46
end

test/define_cfunc.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mobiruby_test "CFunc::define_cfunc" do
2+
skip if CFunc::Platform.is_win32?
3+
24
CFunc::define_function CFunc::Pointer, "strcpy", CFunc::Pointer, CFunc::Pointer
35
CFunc::define_function CFunc::Pointer, "strcat"
46
CFunc::define_function CFunc::Int, "cfunc_test_func4", CFunc::Int, CFunc::Int

test/func_pointer.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mobiruby_test "CFunc::Function pointer" do
2+
skip if CFunc::Platform.is_win32?
3+
24
dlh = CFunc::call(CFunc::Pointer, "dlopen", nil, nil)
35

46
strcpy_ptr = CFunc::call(CFunc::Pointer, "dlsym", dlh, "strcpy")

test/mobitest.rb

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
$ok_test, $ko_test, $kill_test = 0, 0, 0
22
$asserts = []
33

4+
class MobiRubyTestSkip < NotImplementedError
5+
end
6+
47
class MobiRubyTest
58
def initialize(label)
69
@label = label
@@ -11,9 +14,14 @@ def run(&block)
1114
begin
1215
instance_eval(&block)
1316
rescue Exception => e
14-
$asserts.push "Error: #{@label} ##{@index} #{e}"
15-
$kill_test += 1
16-
print('X')
17+
if e.class.to_s != 'MobiRubyTestSkip'
18+
$asserts.push "Error: #{@label} ##{@index} #{e}"
19+
$kill_test += 1
20+
print('X')
21+
else
22+
$asserts.push "Skip: #{@label} ##{@index}"
23+
print('?')
24+
end
1725
end
1826
end
1927

@@ -29,6 +37,10 @@ def assert(result, label='')
2937
end
3038
end
3139

40+
def skip
41+
raise MobiRubyTestSkip.new
42+
end
43+
3244
def assert_equal(a, b)
3345
assert(a===b, "<#{a.inspect}> expected but was <#{b.inspect}>")
3446
end

test/pointer.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mobiruby_test "CFunc::Pointer" do
2+
skip if CFunc::Platform.is_win32?
3+
24
ptr1 = CFunc::Pointer.new
35
assert ptr1.is_null?
46

test/rubyvm.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mobiruby_test "CFunc::RubyVM" do
2+
skip if CFunc::Platform.is_win32?
3+
24
vm = CFunc::RubyVM.thread('_rubyvm1')
35

46
def sleep(sec)

0 commit comments

Comments
 (0)