зеркало из https://github.com/github/ruby.git
* ext/fiddle/*: Adding fiddle library to wrap libffi
* test/fiddle/*: testing fiddle extension * ext/dl/lib/dl.rb: Requiring fiddle if it is available * ext/dl/lib/dl/callback.rb: using Fiddle if it is available * ext/dl/lib/dl/func.rb: ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
ca3c007f05
Коммит
4bada8b864
|
@ -1,3 +1,11 @@
|
|||
Thu May 6 15:56:12 2010 Aaron Patterson <aaron@tenderlovemaking.com>
|
||||
|
||||
* ext/fiddle/*: Adding fiddle library to wrap libffi
|
||||
* test/fiddle/*: testing fiddle extension
|
||||
* ext/dl/lib/dl.rb: Requiring fiddle if it is available
|
||||
* ext/dl/lib/dl/callback.rb: using Fiddle if it is available
|
||||
* ext/dl/lib/dl/func.rb: ditto
|
||||
|
||||
Thu May 6 15:04:37 2010 NARUSE, Yui <naruse@ruby-lang.org>
|
||||
|
||||
* string.c (rb_str_match_m): add description about optional
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
require 'dl.so'
|
||||
|
||||
begin
|
||||
require 'fiddle'
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
module DL
|
||||
def self.fiddle?
|
||||
Object.const_defined?(:Fiddle)
|
||||
end
|
||||
end
|
|
@ -4,22 +4,38 @@ require 'thread'
|
|||
module DL
|
||||
SEM = Mutex.new
|
||||
|
||||
def set_callback_internal(proc_entry, addr_entry, argc, ty, &cbp)
|
||||
if DL.fiddle?
|
||||
CdeclCallbackProcs = {}
|
||||
CdeclCallbackAddrs = {}
|
||||
StdcallCallbackProcs = {}
|
||||
StdcallCallbackAddrs = {}
|
||||
end
|
||||
|
||||
def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = nil, &cbp)
|
||||
if( argc < 0 )
|
||||
raise(ArgumentError, "arity should not be less than 0.")
|
||||
end
|
||||
addr = nil
|
||||
SEM.synchronize{
|
||||
ary = proc_entry[ty]
|
||||
(0...MAX_CALLBACK).each{|n|
|
||||
idx = (n * DLSTACK_SIZE) + argc
|
||||
if( ary[idx].nil? )
|
||||
ary[idx] = cbp
|
||||
addr = addr_entry[ty][idx]
|
||||
break
|
||||
end
|
||||
|
||||
if DL.fiddle?
|
||||
abi ||= Fiddle::Function::DEFAULT
|
||||
closure = Fiddle::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
|
||||
proc_entry[closure.to_i] = closure
|
||||
addr = closure.to_i
|
||||
else
|
||||
SEM.synchronize{
|
||||
ary = proc_entry[ty]
|
||||
(0...MAX_CALLBACK).each{|n|
|
||||
idx = (n * DLSTACK_SIZE) + argc
|
||||
if( ary[idx].nil? )
|
||||
ary[idx] = cbp
|
||||
addr = addr_entry[ty][idx]
|
||||
break
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
addr
|
||||
end
|
||||
|
||||
|
@ -28,31 +44,42 @@ module DL
|
|||
end
|
||||
|
||||
def set_stdcall_callback(ty, argc, &cbp)
|
||||
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
|
||||
if DL.fiddle?
|
||||
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, Fiddle::Function::STDCALL, &cbp)
|
||||
else
|
||||
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
|
||||
index = nil
|
||||
if( ctype )
|
||||
addr_entry[ctype].each_with_index{|xaddr, idx|
|
||||
if( xaddr == addr )
|
||||
index = idx
|
||||
end
|
||||
}
|
||||
if DL.fiddle?
|
||||
addr = addr.to_i
|
||||
return false unless proc_entry.key?(addr)
|
||||
proc_entry.delete(addr)
|
||||
true
|
||||
else
|
||||
addr_entry.each{|ty,entry|
|
||||
entry.each_with_index{|xaddr, idx|
|
||||
index = nil
|
||||
if( ctype )
|
||||
addr_entry[ctype].each_with_index{|xaddr, idx|
|
||||
if( xaddr == addr )
|
||||
index = idx
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
if( index and proc_entry[ctype][index] )
|
||||
proc_entry[ctype][index] = nil
|
||||
return true
|
||||
else
|
||||
return false
|
||||
else
|
||||
addr_entry.each{|ty,entry|
|
||||
entry.each_with_index{|xaddr, idx|
|
||||
if( xaddr == addr )
|
||||
index = idx
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
if( index and proc_entry[ctype][index] )
|
||||
proc_entry[ctype][index] = nil
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,21 +5,37 @@ require 'dl/value'
|
|||
require 'thread'
|
||||
|
||||
module DL
|
||||
class Function
|
||||
parent = DL.fiddle? ? Fiddle::Function : Object
|
||||
|
||||
class Function < parent
|
||||
include DL
|
||||
include ValueUtil
|
||||
|
||||
def initialize(cfunc, argtypes, &proc)
|
||||
@cfunc = cfunc
|
||||
@stack = Stack.new(argtypes.collect{|ty| ty.abs})
|
||||
if( @cfunc.ctype < 0 )
|
||||
@cfunc.ctype = @cfunc.ctype.abs
|
||||
@unsigned = true
|
||||
def initialize cfunc, argtypes, abi = nil, &block
|
||||
if DL.fiddle?
|
||||
abi ||= Fiddle::Function::DEFAULT
|
||||
if block_given?
|
||||
@cfunc = Class.new(Fiddle::Closure) {
|
||||
define_method(:call, block)
|
||||
}.new(cfunc.ctype, argtypes)
|
||||
else
|
||||
@cfunc = cfunc
|
||||
end
|
||||
|
||||
@args = argtypes
|
||||
super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
|
||||
else
|
||||
@unsigned = false
|
||||
end
|
||||
if( proc )
|
||||
bind(&proc)
|
||||
@cfunc = cfunc
|
||||
@stack = Stack.new(argtypes.collect{|ty| ty.abs})
|
||||
if( @cfunc.ctype < 0 )
|
||||
@cfunc.ctype = @cfunc.ctype.abs
|
||||
@unsigned = true
|
||||
else
|
||||
@unsigned = false
|
||||
end
|
||||
if block_given?
|
||||
bind(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,11 +48,18 @@ module DL
|
|||
end
|
||||
|
||||
def call(*args, &block)
|
||||
funcs = []
|
||||
args = wrap_args(args, @stack.types, funcs, &block)
|
||||
r = @cfunc.call(@stack.pack(args))
|
||||
funcs.each{|f| f.unbind_at_call()}
|
||||
return wrap_result(r)
|
||||
if DL.fiddle?
|
||||
if block_given?
|
||||
args.find { |a| DL::Function === a }.bind_at_call(&block)
|
||||
end
|
||||
super
|
||||
else
|
||||
funcs = []
|
||||
args = wrap_args(args, @stack.types, funcs, &block)
|
||||
r = @cfunc.call(@stack.pack(args))
|
||||
funcs.each{|f| f.unbind_at_call()}
|
||||
return wrap_result(r)
|
||||
end
|
||||
end
|
||||
|
||||
def wrap_result(r)
|
||||
|
@ -52,31 +75,44 @@ module DL
|
|||
end
|
||||
|
||||
def bind(&block)
|
||||
if( !block )
|
||||
raise(RuntimeError, "block must be given.")
|
||||
end
|
||||
if( @cfunc.ptr == 0 )
|
||||
cb = Proc.new{|*args|
|
||||
ary = @stack.unpack(args)
|
||||
@stack.types.each_with_index{|ty, idx|
|
||||
case ty
|
||||
when TYPE_VOIDP
|
||||
ary[idx] = CPtr.new(ary[idx])
|
||||
end
|
||||
}
|
||||
r = block.call(*ary)
|
||||
wrap_arg(r, @cfunc.ctype, [])
|
||||
}
|
||||
case @cfunc.calltype
|
||||
when :cdecl
|
||||
@cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb)
|
||||
when :stdcall
|
||||
@cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb)
|
||||
else
|
||||
raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
|
||||
if DL.fiddle?
|
||||
@cfunc = Class.new(Fiddle::Closure) {
|
||||
def initialize ctype, args, block
|
||||
super(ctype, args)
|
||||
@block = block
|
||||
end
|
||||
|
||||
def call *args
|
||||
@block.call(*args)
|
||||
end
|
||||
}.new(@cfunc.ctype, @args, block)
|
||||
else
|
||||
if( !block )
|
||||
raise(RuntimeError, "block must be given.")
|
||||
end
|
||||
if( @cfunc.ptr == 0 )
|
||||
raise(RuntimeException, "can't bind C function.")
|
||||
cb = Proc.new{|*args|
|
||||
ary = @stack.unpack(args)
|
||||
@stack.types.each_with_index{|ty, idx|
|
||||
case ty
|
||||
when TYPE_VOIDP
|
||||
ary[idx] = CPtr.new(ary[idx])
|
||||
end
|
||||
}
|
||||
r = block.call(*ary)
|
||||
wrap_arg(r, @cfunc.ctype, [])
|
||||
}
|
||||
case @cfunc.calltype
|
||||
when :cdecl
|
||||
@cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb)
|
||||
when :stdcall
|
||||
@cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb)
|
||||
else
|
||||
raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
|
||||
end
|
||||
if( @cfunc.ptr == 0 )
|
||||
raise(RuntimeException, "can't bind C function.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -211,9 +211,17 @@ module DL
|
|||
end
|
||||
|
||||
def bind_function(name, ctype, argtype, call_type = nil, &block)
|
||||
f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
|
||||
f.bind(&block)
|
||||
f
|
||||
if DL.fiddle?
|
||||
closure = Class.new(Fiddle::Closure) {
|
||||
define_method(:call, block)
|
||||
}.new(ctype, argtype)
|
||||
|
||||
Function.new(closure, argtype)
|
||||
else
|
||||
f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
|
||||
f.bind(&block)
|
||||
f
|
||||
end
|
||||
end
|
||||
|
||||
def create_temp_function(name, ctype, argtype, call_type = nil)
|
||||
|
|
|
@ -45,7 +45,7 @@ module DL
|
|||
result
|
||||
end
|
||||
|
||||
def wrap_arg(arg, ty, funcs, &block)
|
||||
def wrap_arg(arg, ty, funcs = [], &block)
|
||||
funcs ||= []
|
||||
case arg
|
||||
when nil
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
#include <fiddle.h>
|
||||
|
||||
VALUE cFiddleClosure;
|
||||
|
||||
typedef struct {
|
||||
void * code;
|
||||
ffi_closure *pcl;
|
||||
ffi_cif * cif;
|
||||
int argc;
|
||||
ffi_type **argv;
|
||||
} fiddle_closure;
|
||||
|
||||
static void
|
||||
dealloc(void * ptr)
|
||||
{
|
||||
fiddle_closure * cls = (fiddle_closure *)ptr;
|
||||
#ifndef MACOSX
|
||||
ffi_closure_free(cls->pcl);
|
||||
#else
|
||||
munmap(cls->pcl, sizeof(cls->pcl));
|
||||
#endif
|
||||
xfree(cls->cif);
|
||||
if (cls->argv) xfree(cls->argv);
|
||||
xfree(cls);
|
||||
}
|
||||
|
||||
static size_t
|
||||
closure_memsize(const void * ptr)
|
||||
{
|
||||
fiddle_closure * cls = (fiddle_closure *)ptr;
|
||||
size_t size = 0;
|
||||
|
||||
if (ptr) {
|
||||
size += sizeof(*cls);
|
||||
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
|
||||
size += ffi_raw_size(cls->cif);
|
||||
#endif
|
||||
size += sizeof(*cls->argv);
|
||||
size += sizeof(ffi_closure);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
const rb_data_type_t closure_data_type = {
|
||||
"fiddle/closure",
|
||||
0, dealloc, closure_memsize,
|
||||
};
|
||||
|
||||
void
|
||||
callback(ffi_cif *cif, void *resp, void **args, void *ctx)
|
||||
{
|
||||
VALUE self = (VALUE)ctx;
|
||||
VALUE rbargs = rb_iv_get(self, "@args");
|
||||
VALUE ctype = rb_iv_get(self, "@ctype");
|
||||
int argc = RARRAY_LENINT(rbargs);
|
||||
VALUE *params = xcalloc(argc, sizeof(VALUE *));
|
||||
VALUE ret;
|
||||
VALUE cPointer;
|
||||
int i, type;
|
||||
|
||||
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
type = NUM2INT(RARRAY_PTR(rbargs)[i]);
|
||||
switch (type) {
|
||||
case TYPE_VOID:
|
||||
argc = 0;
|
||||
break;
|
||||
case TYPE_INT:
|
||||
params[i] = INT2NUM(*(int *)args[i]);
|
||||
break;
|
||||
case TYPE_VOIDP:
|
||||
params[i] = rb_funcall(cPointer, rb_intern("[]"), 1,
|
||||
PTR2NUM(*(void **)args[i]));
|
||||
break;
|
||||
case TYPE_LONG:
|
||||
params[i] = LONG2NUM(*(long *)args[i]);
|
||||
break;
|
||||
case TYPE_CHAR:
|
||||
params[i] = INT2NUM(*(char *)args[i]);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
params[i] = rb_float_new(*(double *)args[i]);
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
params[i] = rb_float_new(*(float *)args[i]);
|
||||
break;
|
||||
#if HAVE_LONG_LONG
|
||||
case TYPE_LONG_LONG:
|
||||
params[i] = rb_ull2inum(*(unsigned LONG_LONG *)args[i]);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "closure args: %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
ret = rb_funcall2(self, rb_intern("call"), argc, params);
|
||||
|
||||
type = NUM2INT(ctype);
|
||||
switch (type) {
|
||||
case TYPE_VOID:
|
||||
break;
|
||||
case TYPE_LONG:
|
||||
*(long *)resp = NUM2LONG(ret);
|
||||
break;
|
||||
case TYPE_CHAR:
|
||||
*(char *)resp = NUM2INT(ret);
|
||||
break;
|
||||
case TYPE_VOIDP:
|
||||
*(void **)resp = NUM2PTR(ret);
|
||||
break;
|
||||
case TYPE_INT:
|
||||
*(int *)resp = NUM2INT(ret);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
*(double *)resp = NUM2DBL(ret);
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
*(float *)resp = (float)NUM2DBL(ret);
|
||||
break;
|
||||
#if HAVE_LONG_LONG
|
||||
case TYPE_LONG_LONG:
|
||||
*(unsigned LONG_LONG *)resp = rb_big2ull(ret);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "closure retval: %d", type);
|
||||
}
|
||||
xfree(params);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
allocate(VALUE klass)
|
||||
{
|
||||
fiddle_closure * closure;
|
||||
|
||||
VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
|
||||
&closure_data_type, closure);
|
||||
|
||||
#ifndef MACOSX
|
||||
closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
|
||||
#else
|
||||
closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
|
||||
MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
closure->cif = xmalloc(sizeof(ffi_cif));
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
initialize(int rbargc, VALUE argv[], VALUE self)
|
||||
{
|
||||
VALUE ret;
|
||||
VALUE args;
|
||||
VALUE abi;
|
||||
fiddle_closure * cl;
|
||||
ffi_cif * cif;
|
||||
ffi_closure *pcl;
|
||||
ffi_status result;
|
||||
int i, argc;
|
||||
|
||||
if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
|
||||
abi = INT2NUM(FFI_DEFAULT_ABI);
|
||||
|
||||
Check_Type(args, T_ARRAY);
|
||||
|
||||
argc = RARRAY_LENINT(args);
|
||||
|
||||
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
|
||||
|
||||
cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
int type = NUM2INT(RARRAY_PTR(args)[i]);
|
||||
cl->argv[i] = INT2FFI_TYPE(type);
|
||||
}
|
||||
cl->argv[argc] = NULL;
|
||||
|
||||
rb_iv_set(self, "@ctype", ret);
|
||||
rb_iv_set(self, "@args", args);
|
||||
|
||||
cif = cl->cif;
|
||||
pcl = cl->pcl;
|
||||
|
||||
result = ffi_prep_cif(cif, NUM2INT(abi), argc,
|
||||
INT2FFI_TYPE(NUM2INT(ret)),
|
||||
cl->argv);
|
||||
|
||||
if (FFI_OK != result)
|
||||
rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
|
||||
|
||||
#ifndef MACOSX
|
||||
result = ffi_prep_closure_loc(pcl, cif, callback,
|
||||
(void *)self, cl->code);
|
||||
#else
|
||||
result = ffi_prep_closure(pcl, cif, callback, (void *)self);
|
||||
cl->code = (void *)pcl;
|
||||
mprotect(pcl, sizeof(pcl), PROT_READ | PROT_EXEC);
|
||||
#endif
|
||||
|
||||
if (FFI_OK != result)
|
||||
rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
to_i(VALUE self)
|
||||
{
|
||||
fiddle_closure * cl;
|
||||
void *code;
|
||||
|
||||
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
|
||||
|
||||
code = cl->code;
|
||||
|
||||
return PTR2NUM(code);
|
||||
}
|
||||
|
||||
void
|
||||
Init_fiddle_closure()
|
||||
{
|
||||
cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
|
||||
|
||||
rb_define_alloc_func(cFiddleClosure, allocate);
|
||||
|
||||
rb_define_method(cFiddleClosure, "initialize", initialize, -1);
|
||||
rb_define_method(cFiddleClosure, "to_i", to_i, 0);
|
||||
}
|
||||
/* vim: set noet sw=4 sts=4 */
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef FIDDLE_CLOSURE_H
|
||||
#define FIDDLE_CLOSURE_H
|
||||
|
||||
#include <fiddle.h>
|
||||
|
||||
void Init_fiddle_closure();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,126 @@
|
|||
#include <fiddle.h>
|
||||
|
||||
ffi_type *
|
||||
int_to_ffi_type(int type)
|
||||
{
|
||||
int signed_p = 1;
|
||||
|
||||
if (type < 0) {
|
||||
type = -1 * type;
|
||||
signed_p = 0;
|
||||
}
|
||||
|
||||
#define rb_ffi_type_of(t) (signed_p ? &ffi_type_s##t : &ffi_type_u##t)
|
||||
|
||||
switch (type) {
|
||||
case TYPE_VOID:
|
||||
return &ffi_type_void;
|
||||
case TYPE_VOIDP:
|
||||
return &ffi_type_pointer;
|
||||
case TYPE_CHAR:
|
||||
return rb_ffi_type_of(char);
|
||||
case TYPE_SHORT:
|
||||
return rb_ffi_type_of(short);
|
||||
case TYPE_INT:
|
||||
return rb_ffi_type_of(int);
|
||||
case TYPE_LONG:
|
||||
return rb_ffi_type_of(long);
|
||||
#if HAVE_LONG_LONG
|
||||
case TYPE_LONG_LONG:
|
||||
return rb_ffi_type_of(int64);
|
||||
#endif
|
||||
case TYPE_FLOAT:
|
||||
return &ffi_type_float;
|
||||
case TYPE_DOUBLE:
|
||||
return &ffi_type_double;
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "unknown type %d", type);
|
||||
}
|
||||
return &ffi_type_pointer;
|
||||
}
|
||||
|
||||
void
|
||||
value_to_generic(int type, VALUE src, fiddle_generic * dst)
|
||||
{
|
||||
int signed_p = 1;
|
||||
|
||||
if (type < 0) {
|
||||
type = -1 * type;
|
||||
signed_p = 0;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TYPE_VOID:
|
||||
break;
|
||||
case TYPE_VOIDP:
|
||||
dst->pointer = NUM2PTR(rb_Integer(src));
|
||||
break;
|
||||
case TYPE_CHAR:
|
||||
case TYPE_SHORT:
|
||||
case TYPE_INT:
|
||||
dst->sint = NUM2INT(src);
|
||||
break;
|
||||
case TYPE_LONG:
|
||||
if (signed_p)
|
||||
dst->slong = NUM2LONG(src);
|
||||
else
|
||||
dst->ulong = NUM2LONG(src);
|
||||
break;
|
||||
#if HAVE_LONG_LONG
|
||||
case TYPE_LONG_LONG:
|
||||
dst->long_long = rb_big2ull(src);
|
||||
break;
|
||||
#endif
|
||||
case TYPE_FLOAT:
|
||||
dst->ffloat = (float)NUM2DBL(src);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
dst->ddouble = NUM2DBL(src);
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "unknown type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
generic_to_value(VALUE rettype, fiddle_generic retval)
|
||||
{
|
||||
int signed_p = 1;
|
||||
int type = NUM2INT(rettype);
|
||||
VALUE cPointer;
|
||||
|
||||
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
|
||||
|
||||
if (type < 0) {
|
||||
type = -1 * type;
|
||||
signed_p = 0;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TYPE_VOID:
|
||||
return Qnil;
|
||||
case TYPE_VOIDP:
|
||||
return rb_funcall(cPointer, rb_intern("[]"), 1,
|
||||
PTR2NUM((void *)retval.pointer));
|
||||
case TYPE_CHAR:
|
||||
case TYPE_SHORT:
|
||||
case TYPE_INT:
|
||||
return INT2NUM(retval.sint);
|
||||
case TYPE_LONG:
|
||||
if (signed_p) return LONG2NUM(retval.slong);
|
||||
return ULONG2NUM(retval.ulong);
|
||||
#if HAVE_LONG_LONG
|
||||
case TYPE_LONG_LONG:
|
||||
return rb_ll2inum(retval.long_long);
|
||||
break;
|
||||
#endif
|
||||
case TYPE_FLOAT:
|
||||
return rb_float_new(retval.ffloat);
|
||||
case TYPE_DOUBLE:
|
||||
return rb_float_new(retval.ddouble);
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "unknown type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set noet sw=4 sts=4 */
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef FIDDLE_CONVERSIONS_H
|
||||
#define FIDDLE_CONVERSIONS_H
|
||||
|
||||
#include <fiddle.h>
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char uchar; /* ffi_type_uchar */
|
||||
signed char schar; /* ffi_type_schar */
|
||||
unsigned short ushort; /* ffi_type_sshort */
|
||||
signed short sshort; /* ffi_type_ushort */
|
||||
unsigned int uint; /* ffi_type_uint */
|
||||
signed int sint; /* ffi_type_sint */
|
||||
unsigned long ulong; /* ffi_type_ulong */
|
||||
signed long slong; /* ffi_type_slong */
|
||||
float ffloat; /* ffi_type_float */
|
||||
double ddouble; /* ffi_type_double */
|
||||
#if HAVE_LONG_LONG
|
||||
unsigned LONG_LONG long_long; /* ffi_type_uint64 */
|
||||
#endif
|
||||
void * pointer; /* ffi_type_pointer */
|
||||
} fiddle_generic;
|
||||
|
||||
ffi_type * int_to_ffi_type(int type);
|
||||
void value_to_generic(int type, VALUE src, fiddle_generic * dst);
|
||||
VALUE generic_to_value(VALUE rettype, fiddle_generic retval);
|
||||
|
||||
#define VALUE2GENERIC(_type, _src, _dst) value_to_generic(_type, _src, _dst)
|
||||
#define INT2FFI_TYPE(_type) int_to_ffi_type(_type)
|
||||
#define GENERIC2VALUE(_type, _retval) generic_to_value(_type, _retval)
|
||||
|
||||
#if SIZEOF_VOIDP == SIZEOF_LONG
|
||||
# define PTR2NUM(x) (ULONG2NUM((unsigned long)(x)))
|
||||
# define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
|
||||
#else
|
||||
/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */
|
||||
# define PTR2NUM(x) (ULL2NUM((unsigned long long)(x)))
|
||||
# define NUM2PTR(x) ((void*)(NUM2ULL(x)))
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
require 'mkmf'
|
||||
|
||||
# :stopdoc:
|
||||
|
||||
dir_config 'libffi'
|
||||
|
||||
unless have_header('ffi.h')
|
||||
if have_header('ffi/ffi.h')
|
||||
$defs.push(format('-DUSE_HEADER_HACKS'))
|
||||
else
|
||||
abort "ffi.h is missing. Please install libffi."
|
||||
end
|
||||
end
|
||||
|
||||
unless have_library('ffi')
|
||||
abort "libffi is missing. Please install libffi."
|
||||
end
|
||||
|
||||
have_header 'sys/mman.h'
|
||||
|
||||
create_makefile 'fiddle'
|
||||
|
||||
# :startdoc:
|
|
@ -0,0 +1,30 @@
|
|||
#include <fiddle.h>
|
||||
|
||||
VALUE mFiddle;
|
||||
|
||||
void Init_fiddle()
|
||||
{
|
||||
mFiddle = rb_define_module("Fiddle");
|
||||
|
||||
rb_define_const(mFiddle, "TYPE_VOID", INT2NUM(TYPE_VOID));
|
||||
rb_define_const(mFiddle, "TYPE_VOIDP", INT2NUM(TYPE_VOIDP));
|
||||
rb_define_const(mFiddle, "TYPE_CHAR", INT2NUM(TYPE_CHAR));
|
||||
rb_define_const(mFiddle, "TYPE_SHORT", INT2NUM(TYPE_SHORT));
|
||||
rb_define_const(mFiddle, "TYPE_INT", INT2NUM(TYPE_INT));
|
||||
rb_define_const(mFiddle, "TYPE_LONG", INT2NUM(TYPE_LONG));
|
||||
#if HAVE_LONG_LONG
|
||||
rb_define_const(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG));
|
||||
#endif
|
||||
rb_define_const(mFiddle, "TYPE_FLOAT", INT2NUM(TYPE_FLOAT));
|
||||
rb_define_const(mFiddle, "TYPE_DOUBLE", INT2NUM(TYPE_DOUBLE));
|
||||
|
||||
#if defined(HAVE_WINDOWS_H)
|
||||
rb_define_const(mFiddle, "WINDOWS", Qtrue);
|
||||
#else
|
||||
rb_define_const(mFiddle, "WINDOWS", Qfalse);
|
||||
#endif
|
||||
|
||||
Init_fiddle_function();
|
||||
Init_fiddle_closure();
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef FIDDLE_H
|
||||
#define FIDDLE_H
|
||||
|
||||
#include <ruby.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(HAVE_WINDOWS_H)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_HEADER_HACKS
|
||||
#include <ffi/ffi.h>
|
||||
#else
|
||||
#include <ffi.h>
|
||||
#endif
|
||||
|
||||
#include <closure.h>
|
||||
#include <conversions.h>
|
||||
#include <function.h>
|
||||
|
||||
/* FIXME
|
||||
* These constants need to match up with DL. We need to refactor this to use
|
||||
* the DL header files or vice versa.
|
||||
*/
|
||||
|
||||
#define TYPE_VOID 0
|
||||
#define TYPE_VOIDP 1
|
||||
#define TYPE_CHAR 2
|
||||
#define TYPE_SHORT 3
|
||||
#define TYPE_INT 4
|
||||
#define TYPE_LONG 5
|
||||
#if HAVE_LONG_LONG
|
||||
#define TYPE_LONG_LONG 6
|
||||
#endif
|
||||
#define TYPE_FLOAT 7
|
||||
#define TYPE_DOUBLE 8
|
||||
|
||||
extern VALUE mFiddle;
|
||||
|
||||
#endif
|
||||
/* vim: set noet sws=4 sw=4: */
|
|
@ -0,0 +1,155 @@
|
|||
#include <fiddle.h>
|
||||
|
||||
VALUE cFiddleFunction;
|
||||
|
||||
static void
|
||||
deallocate(void *p)
|
||||
{
|
||||
ffi_cif *ptr = p;
|
||||
if (ptr->arg_types) xfree(ptr->arg_types);
|
||||
xfree(ptr);
|
||||
}
|
||||
|
||||
static size_t
|
||||
function_memsize(const void *p)
|
||||
{
|
||||
/* const */ffi_cif *ptr = (ffi_cif *)p;
|
||||
size_t size = 0;
|
||||
|
||||
if (ptr) {
|
||||
size += sizeof(*ptr);
|
||||
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
|
||||
size += ffi_raw_size(ptr);
|
||||
#endif
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
const rb_data_type_t function_data_type = {
|
||||
"fiddle/function",
|
||||
0, deallocate, function_memsize,
|
||||
};
|
||||
|
||||
static VALUE
|
||||
allocate(VALUE klass)
|
||||
{
|
||||
ffi_cif * cif;
|
||||
|
||||
return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
initialize(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
ffi_cif * cif;
|
||||
ffi_type **arg_types;
|
||||
ffi_status result;
|
||||
VALUE ptr, args, ret_type, abi;
|
||||
int i;
|
||||
|
||||
rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
|
||||
if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
|
||||
|
||||
Check_Type(args, T_ARRAY);
|
||||
|
||||
rb_iv_set(self, "@ptr", ptr);
|
||||
rb_iv_set(self, "@args", args);
|
||||
rb_iv_set(self, "@return_type", ret_type);
|
||||
rb_iv_set(self, "@abi", abi);
|
||||
|
||||
TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
|
||||
|
||||
arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
|
||||
|
||||
for (i = 0; i < RARRAY_LEN(args); i++) {
|
||||
int type = NUM2INT(RARRAY_PTR(args)[i]);
|
||||
arg_types[i] = INT2FFI_TYPE(type);
|
||||
}
|
||||
arg_types[RARRAY_LEN(args)] = NULL;
|
||||
|
||||
result = ffi_prep_cif (
|
||||
cif,
|
||||
NUM2INT(abi),
|
||||
RARRAY_LENINT(args),
|
||||
INT2FFI_TYPE(NUM2INT(ret_type)),
|
||||
arg_types);
|
||||
|
||||
if (result)
|
||||
rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
function_call(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
ffi_cif * cif;
|
||||
fiddle_generic retval;
|
||||
fiddle_generic *generic_args;
|
||||
void **values;
|
||||
void * fun_ptr;
|
||||
VALUE cfunc, types, cPointer;
|
||||
int i;
|
||||
|
||||
cfunc = rb_iv_get(self, "@ptr");
|
||||
types = rb_iv_get(self, "@args");
|
||||
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
|
||||
|
||||
if(argc != RARRAY_LENINT(types)) {
|
||||
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
|
||||
argc, RARRAY_LENINT(types));
|
||||
}
|
||||
|
||||
TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
|
||||
|
||||
values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
|
||||
generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
VALUE type = RARRAY_PTR(types)[i];
|
||||
VALUE src = argv[i];
|
||||
|
||||
if(NUM2INT(type) == TYPE_VOIDP) {
|
||||
if(NIL_P(src)) {
|
||||
src = INT2NUM(0);
|
||||
} else if(cPointer != CLASS_OF(src)) {
|
||||
src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
|
||||
}
|
||||
src = rb_Integer(src);
|
||||
}
|
||||
|
||||
VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
|
||||
values[i] = (void *)&generic_args[i];
|
||||
}
|
||||
values[argc] = NULL;
|
||||
|
||||
ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
|
||||
|
||||
rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
|
||||
#if defined(HAVE_WINDOWS_H)
|
||||
rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
|
||||
#endif
|
||||
|
||||
xfree(values);
|
||||
xfree(generic_args);
|
||||
|
||||
return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
|
||||
}
|
||||
|
||||
void
|
||||
Init_fiddle_function(void)
|
||||
{
|
||||
cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
|
||||
|
||||
rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
|
||||
|
||||
#ifdef FFI_STDCALL
|
||||
rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
|
||||
#endif
|
||||
|
||||
rb_define_alloc_func(cFiddleFunction, allocate);
|
||||
|
||||
rb_define_method(cFiddleFunction, "call", function_call, -1);
|
||||
rb_define_method(cFiddleFunction, "initialize", initialize, -1);
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef FIDDLE_FUNCTION_H
|
||||
#define FIDDLE_FUNCTION_H
|
||||
|
||||
#include <fiddle.h>
|
||||
|
||||
void Init_fiddle_function();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
require 'fiddle.so'
|
||||
require 'fiddle/function'
|
||||
require 'fiddle/closure'
|
||||
require 'dl'
|
||||
|
||||
module Fiddle
|
||||
Pointer = DL::CPtr
|
||||
|
||||
if WINDOWS
|
||||
def self.win32_last_error
|
||||
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
|
||||
end
|
||||
|
||||
def self.win32_last_error= error
|
||||
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
|
||||
end
|
||||
end
|
||||
|
||||
def self.last_error
|
||||
Thread.current[:__FIDDLE_LAST_ERROR__]
|
||||
end
|
||||
|
||||
def self.last_error= error
|
||||
Thread.current[:__DL2_LAST_ERROR__] = error
|
||||
Thread.current[:__FIDDLE_LAST_ERROR__] = error
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module Fiddle
|
||||
class Closure
|
||||
attr_reader :ctype
|
||||
attr_reader :args
|
||||
|
||||
class BlockCaller < Fiddle::Closure
|
||||
def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block
|
||||
super(ctype, args, abi)
|
||||
@block = block
|
||||
end
|
||||
|
||||
def call *args
|
||||
@block.call(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
module Fiddle
|
||||
class Function
|
||||
attr_reader :abi
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
require 'minitest/autorun'
|
||||
require 'dl'
|
||||
require 'fiddle'
|
||||
|
||||
# FIXME: this is stolen from DL and needs to be refactored.
|
||||
require_relative '../ruby/envutil'
|
||||
|
||||
libc_so = libm_so = nil
|
||||
|
||||
case RUBY_PLATFORM
|
||||
when /cygwin/
|
||||
libc_so = "cygwin1.dll"
|
||||
libm_so = "cygwin1.dll"
|
||||
when /x86_64-linux/
|
||||
libc_so = "/lib64/libc.so.6"
|
||||
libm_so = "/lib64/libm.so.6"
|
||||
when /linux/
|
||||
libdir = '/lib'
|
||||
case [0].pack('L!').size
|
||||
when 4
|
||||
# 32-bit ruby
|
||||
libdir = '/lib32' if File.directory? '/lib32'
|
||||
when 8
|
||||
# 64-bit ruby
|
||||
libdir = '/lib64' if File.directory? '/lib64'
|
||||
end
|
||||
libc_so = File.join(libdir, "libc.so.6")
|
||||
libm_so = File.join(libdir, "libm.so.6")
|
||||
when /mingw/, /mswin/
|
||||
require "rbconfig"
|
||||
libc_so = libm_so = RbConfig::CONFIG["RUBY_SO_NAME"].split(/-/, 2)[0] + ".dll"
|
||||
when /darwin/
|
||||
libc_so = "/usr/lib/libc.dylib"
|
||||
libm_so = "/usr/lib/libm.dylib"
|
||||
when /kfreebsd/
|
||||
libc_so = "/lib/libc.so.0.1"
|
||||
libm_so = "/lib/libm.so.1"
|
||||
when /bsd|dragonfly/
|
||||
libc_so = "/usr/lib/libc.so"
|
||||
libm_so = "/usr/lib/libm.so"
|
||||
else
|
||||
libc_so = ARGV[0] if ARGV[0] && ARGV[0][0] == ?/
|
||||
libm_so = ARGV[1] if ARGV[1] && ARGV[1][0] == ?/
|
||||
if( !(libc_so && libm_so) )
|
||||
$stderr.puts("libc and libm not found: #{$0} <libc> <libm>")
|
||||
end
|
||||
end
|
||||
|
||||
libc_so = nil if !libc_so || (libc_so[0] == ?/ && !File.file?(libc_so))
|
||||
libm_so = nil if !libm_so || (libm_so[0] == ?/ && !File.file?(libm_so))
|
||||
|
||||
if !libc_so || !libm_so
|
||||
ruby = EnvUtil.rubybin
|
||||
ldd = `ldd #{ruby}`
|
||||
#puts ldd
|
||||
libc_so = $& if !libc_so && %r{/\S*/libc\.so\S*} =~ ldd
|
||||
libm_so = $& if !libm_so && %r{/\S*/libm\.so\S*} =~ ldd
|
||||
#p [libc_so, libm_so]
|
||||
end
|
||||
|
||||
Fiddle::LIBC_SO = libc_so
|
||||
Fiddle::LIBM_SO = libm_so
|
||||
|
||||
module Fiddle
|
||||
class TestCase < MiniTest::Unit::TestCase
|
||||
def setup
|
||||
@libc = DL.dlopen(LIBC_SO)
|
||||
@libm = DL.dlopen(LIBM_SO)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,49 @@
|
|||
require_relative 'helper'
|
||||
|
||||
module Fiddle
|
||||
class TestClosure < Fiddle::TestCase
|
||||
def test_argument_errors
|
||||
assert_raises(TypeError) do
|
||||
Closure.new(TYPE_INT, TYPE_INT)
|
||||
end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
Closure.new('foo', [TYPE_INT])
|
||||
end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
Closure.new(TYPE_INT, ['meow!'])
|
||||
end
|
||||
end
|
||||
|
||||
def test_call
|
||||
closure = Class.new(Closure) {
|
||||
def call
|
||||
10
|
||||
end
|
||||
}.new(TYPE_INT, [])
|
||||
|
||||
func = Function.new(closure, [], TYPE_INT)
|
||||
assert_equal 10, func.call
|
||||
end
|
||||
|
||||
def test_returner
|
||||
closure = Class.new(Closure) {
|
||||
def call thing
|
||||
thing
|
||||
end
|
||||
}.new(TYPE_INT, [TYPE_INT])
|
||||
|
||||
func = Function.new(closure, [TYPE_INT], TYPE_INT)
|
||||
assert_equal 10, func.call(10)
|
||||
end
|
||||
|
||||
def test_block_caller
|
||||
cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
|
||||
one
|
||||
end
|
||||
func = Function.new(cb, [TYPE_INT], TYPE_INT)
|
||||
assert_equal 11, func.call(11)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
require_relative 'helper'
|
||||
|
||||
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,
|
||||
].each do |name|
|
||||
assert_equal(DL.const_get(name), Fiddle.const_get(name))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
require_relative 'helper'
|
||||
|
||||
module Fiddle
|
||||
class TestFunction < Fiddle::TestCase
|
||||
def setup
|
||||
super
|
||||
Fiddle.last_error = nil
|
||||
end
|
||||
|
||||
def test_default_abi
|
||||
func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
|
||||
assert_equal Function::DEFAULT, func.abi
|
||||
end
|
||||
|
||||
def test_argument_errors
|
||||
assert_raises(TypeError) do
|
||||
Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE)
|
||||
end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
Function.new(@libm['sin'], ['foo'], TYPE_DOUBLE)
|
||||
end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
Function.new(@libm['sin'], [TYPE_DOUBLE], 'foo')
|
||||
end
|
||||
end
|
||||
|
||||
def test_call
|
||||
func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
|
||||
assert_in_delta 1.0, func.call(90 * Math::PI / 180), 0.0001
|
||||
end
|
||||
|
||||
def test_argument_count
|
||||
closure = Class.new(Closure) {
|
||||
def call one
|
||||
10 + one
|
||||
end
|
||||
}.new(TYPE_INT, [TYPE_INT])
|
||||
func = Function.new(closure, [TYPE_INT], TYPE_INT)
|
||||
|
||||
assert_raises(ArgumentError) do
|
||||
func.call(1,2,3)
|
||||
end
|
||||
assert_raises(ArgumentError) do
|
||||
func.call
|
||||
end
|
||||
end
|
||||
|
||||
def test_last_error
|
||||
func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||
|
||||
assert_nil Fiddle.last_error
|
||||
str = func.call("000", "123")
|
||||
refute_nil Fiddle.last_error
|
||||
end
|
||||
|
||||
def test_strcpy
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
Загрузка…
Ссылка в новой задаче