1require 'dl'
2require 'dl/func.rb'
3require 'dl/struct.rb'
4require 'dl/cparser.rb'
5
6module DL
7  class CompositeHandler
8    def initialize(handlers)
9      @handlers = handlers
10    end
11
12    def handlers()
13      @handlers
14    end
15
16    def sym(symbol)
17      @handlers.each{|handle|
18        if( handle )
19          begin
20            addr = handle.sym(symbol)
21            return addr
22          rescue DLError
23          end
24        end
25      }
26      return nil
27    end
28
29    def [](symbol)
30      sym(symbol)
31    end
32  end
33
34  # DL::Importer includes the means to dynamically load libraries and build
35  # modules around them including calling extern functions within the C
36  # library that has been loaded.
37  #
38  # == Example
39  #
40  #   require 'dl'
41  #   require 'dl/import'
42  #
43  #   module LibSum
44  #   	extend DL::Importer
45  #   	dlload './libsum.so'
46  #   	extern 'double sum(double*, int)'
47  #   	extern 'double split(double)'
48  #   end
49	#
50  module Importer
51    include DL
52    include CParser
53    extend Importer
54
55    def dlload(*libs)
56      handles = libs.collect{|lib|
57        case lib
58        when nil
59          nil
60        when Handle
61          lib
62        when Importer
63          lib.handlers
64        else
65          begin
66            DL.dlopen(lib)
67          rescue DLError
68            raise(DLError, "can't load #{lib}")
69          end
70        end
71      }.flatten()
72      @handler = CompositeHandler.new(handles)
73      @func_map = {}
74      @type_alias = {}
75    end
76
77    def typealias(alias_type, orig_type)
78      @type_alias[alias_type] = orig_type
79    end
80
81    def sizeof(ty)
82      @type_alias ||= nil
83      case ty
84      when String
85        ty = parse_ctype(ty, @type_alias).abs()
86        case ty
87        when TYPE_CHAR
88          return SIZEOF_CHAR
89        when TYPE_SHORT
90          return SIZEOF_SHORT
91        when TYPE_INT
92          return SIZEOF_INT
93        when TYPE_LONG
94          return SIZEOF_LONG
95        when TYPE_LONG_LONG
96          return SIZEOF_LONG_LON
97        when TYPE_FLOAT
98          return SIZEOF_FLOAT
99        when TYPE_DOUBLE
100          return SIZEOF_DOUBLE
101        when TYPE_VOIDP
102          return SIZEOF_VOIDP
103        else
104          raise(DLError, "unknown type: #{ty}")
105        end
106      when Class
107        if( ty.instance_methods().include?(:to_ptr) )
108          return ty.size()
109        end
110      end
111      return CPtr[ty].size()
112    end
113
114    def parse_bind_options(opts)
115      h = {}
116      while( opt = opts.shift() )
117        case opt
118        when :stdcall, :cdecl
119          h[:call_type] = opt
120        when :carried, :temp, :temporal, :bind
121          h[:callback_type] = opt
122          h[:carrier] = opts.shift()
123        else
124          h[opt] = true
125        end
126      end
127      h
128    end
129    private :parse_bind_options
130
131    def extern(signature, *opts)
132      @type_alias ||= nil
133      symname, ctype, argtype = parse_signature(signature, @type_alias)
134      opt = parse_bind_options(opts)
135      f = import_function(symname, ctype, argtype, opt[:call_type])
136      name = symname.gsub(/@.+/,'')
137      @func_map[name] = f
138      # define_method(name){|*args,&block| f.call(*args,&block)}
139      begin
140        /^(.+?):(\d+)/ =~ caller.first
141        file, line = $1, $2.to_i
142      rescue
143        file, line = __FILE__, __LINE__+3
144      end
145      module_eval(<<-EOS, file, line)
146        def #{name}(*args, &block)
147          @func_map['#{name}'].call(*args,&block)
148        end
149      EOS
150      module_function(name)
151      f
152    end
153
154    def bind(signature, *opts, &blk)
155      @type_alias ||= nil
156      name, ctype, argtype = parse_signature(signature, @type_alias)
157      h = parse_bind_options(opts)
158      case h[:callback_type]
159      when :bind, nil
160        f = bind_function(name, ctype, argtype, h[:call_type], &blk)
161      when :temp, :temporal
162        f = create_temp_function(name, ctype, argtype, h[:call_type])
163      when :carried
164        f = create_carried_function(name, ctype, argtype, h[:call_type], h[:carrier])
165      else
166        raise(RuntimeError, "unknown callback type: #{h[:callback_type]}")
167      end
168      @func_map[name] = f
169      #define_method(name){|*args,&block| f.call(*args,&block)}
170      begin
171        /^(.+?):(\d+)/ =~ caller.first
172        file, line = $1, $2.to_i
173      rescue
174        file, line = __FILE__, __LINE__+3
175      end
176      module_eval(<<-EOS, file, line)
177        def #{name}(*args,&block)
178          @func_map['#{name}'].call(*args,&block)
179        end
180      EOS
181      module_function(name)
182      f
183    end
184
185    # Creates a class to wrap the C struct described by +signature+.
186    #
187    #   MyStruct = struct ['int i', 'char c']
188    def struct(signature)
189      @type_alias ||= nil
190      tys, mems = parse_struct_signature(signature, @type_alias)
191      DL::CStructBuilder.create(CStruct, tys, mems)
192    end
193
194    # Creates a class to wrap the C union described by +signature+.
195    #
196    #   MyUnion = union ['int i', 'char c']
197    def union(signature)
198      @type_alias ||= nil
199      tys, mems = parse_struct_signature(signature, @type_alias)
200      DL::CStructBuilder.create(CUnion, tys, mems)
201    end
202
203    def [](name)
204      @func_map[name]
205    end
206
207    def create_value(ty, val=nil)
208      s = struct([ty + " value"])
209      ptr = s.malloc()
210      if( val )
211        ptr.value = val
212      end
213      return ptr
214    end
215    alias value create_value
216
217    def import_value(ty, addr)
218      s = struct([ty + " value"])
219      ptr = s.new(addr)
220      return ptr
221    end
222
223    def handler
224      defined?(@handler) or raise "call dlload before importing symbols and functions"
225      @handler
226    end
227
228    def import_symbol(name)
229      addr = handler.sym(name)
230      if( !addr )
231        raise(DLError, "cannot find the symbol: #{name}")
232      end
233      CPtr.new(addr)
234    end
235
236    def import_function(name, ctype, argtype, call_type = nil)
237      addr = handler.sym(name)
238      if( !addr )
239        raise(DLError, "cannot find the function: #{name}()")
240      end
241      Function.new(CFunc.new(addr, ctype, name, call_type || :cdecl), argtype)
242    end
243
244    def bind_function(name, ctype, argtype, call_type = nil, &block)
245      if DL.fiddle?
246        klass = Function.instance_eval { class_fiddle_closure_cfunc }
247        abi = Function.instance_eval { call_type_to_abi(call_type) }
248        closure = Class.new(klass) {
249          define_method(:call, block)
250        }.new(ctype, argtype, abi, name)
251
252        Function.new(closure, argtype, abi)
253      else
254        f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
255        f.bind(&block)
256        f
257      end
258    end
259
260    def create_temp_function(name, ctype, argtype, call_type = nil)
261      TempFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
262    end
263
264    def create_carried_function(name, ctype, argtype, call_type = nil, n = 0)
265      CarriedFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype, n)
266    end
267  end
268end
269