ext/dl/lib/dl/import.rb


DEFINITIONS

This source file includes following functions.


   1  # -*- ruby -*-
   2  
   3  require 'dl'
   4  require 'dl/types'
   5  
   6  module DL
   7    module Importable
   8      LIB_MAP = {}
   9  
  10      module Internal
  11        def init_types()
  12          if( !@types )
  13            @types = ::DL::Types.new
  14          end
  15        end
  16  
  17        def init_sym()
  18          if( !@SYM )
  19            @SYM = {}
  20          end
  21        end
  22  
  23        def [](name)
  24          return @SYM[name.to_s][0]
  25        end
  26  
  27        def dlload(*libnames)
  28          if( !defined?(@LIBS) )
  29            @LIBS = []
  30          end
  31          libnames.each{|libname|
  32            if( !LIB_MAP[libname] )
  33              LIB_MAP[libname] = DL.dlopen(libname)
  34            end
  35            @LIBS.push(LIB_MAP[libname])
  36          }
  37        end
  38        alias dllink :dlload
  39  
  40        def parse_cproto(proto)
  41          proto = proto.gsub(/\s+/, " ").strip
  42          case proto
  43          when /^([\d\w\*_\s]+)\(([\d\w\*_\s\,\[\]]*)\)$/
  44            ret = $1
  45            args = $2
  46            ret = ret.split(/\s+/)
  47            args = args.split(/\s*,\s*/)
  48            func = ret.pop
  49            if( func =~ /^\*/ )
  50              func.gsub!(/^\*+/,"")
  51              ret.push("*")
  52            end
  53            ret  = ret.join(" ")
  54            return [func, ret, args]
  55          else
  56            raise(RuntimeError,"can't parse the function prototype: #{proto}")
  57          end
  58        end
  59  
  60        # example:
  61        #   extern "int strlen(char*)"
  62        #
  63        def extern(proto)
  64          func,ret,args = parse_cproto(proto)
  65          return import(func, ret, args)
  66        end
  67  
  68        # example:
  69        #   callback "int method_name(int, char*)"
  70        #
  71        def callback(proto)
  72          func,ret,args = parse_cproto(proto)
  73  
  74          init_types()
  75          init_sym()
  76  
  77          rty,_,rdec = @types.encode_type(ret)
  78          ty,enc,dec = encode_types(args)
  79          symty = rty + ty
  80  
  81          module_eval("module_function :#{func}")
  82          sym = module_eval [
  83            "DL::callback(\"#{symty}\"){|*args|",
  84            "  sym,rdec,enc,dec  = @SYM['#{func}']",
  85            "  args = enc.call(args) if enc",
  86            "  r,rs = #{func}(*args)",
  87            "  r  = rdec.call(r) if rdec",
  88            "  rs = dec.call(rs) if dec",
  89            "  @retval = r",
  90            "  @args   = rs",
  91            "  @retval",
  92            "}",
  93          ].join("\n")
  94  
  95          @SYM[func] = [sym,rdec,enc,dec]
  96  
  97          return sym
  98        end
  99  
 100        # example:
 101        #  typealias("uint", "unsigned int")
 102        #
 103        def typealias(*args)
 104          init_types()
 105          @types.typealias(*args)
 106        end
 107  
 108        # example:
 109        #  symbol "foo_value"
 110        #  symbol "foo_func", "IIP"
 111        #
 112        def symbol(name, ty = nil)
 113          sym = nil
 114          @LIBS.each{|lib|
 115            begin
 116              if( ty )
 117                sym = lib[name, ty]
 118              else
 119                sym = lib[name]
 120              end
 121            rescue
 122              next
 123            end
 124          }
 125          if( !sym )
 126            raise(RuntimeError, "can't find the symbol `#{name}'")
 127          end
 128          return sym
 129        end
 130  
 131        # example:
 132        #   import("get_length", "int", ["void*", "int"])
 133        #
 134        def import(name, rettype, argtypes = nil)
 135          init_types()
 136          init_sym()
 137  
 138          rty,_,rdec = @types.encode_type(rettype)
 139          ty,enc,dec = encode_types(argtypes)
 140          symty = rty + ty
 141  
 142          sym = symbol(name, symty)
 143  
 144          mname = name.dup
 145          if( ?A <= mname[0] && mname[0] <= ?Z )
 146            mname[0,1] = mname[0,1].downcase
 147          end
 148          @SYM[mname] = [sym,rdec,enc,dec]
 149          
 150          module_eval [
 151            "def #{mname}(*args)",
 152            "  sym,rdec,enc,dec  = @SYM['#{mname}']",
 153            "  args = enc.call(args) if enc",
 154            if( $DEBUG )
 155              "  p \"[DL] call #{mname} with \#{args.inspect}\""
 156            else
 157              ""
 158            end,
 159            "  r,rs = sym.call(*args)",
 160            if( $DEBUG )
 161              "  p \"[DL] retval=\#{r.inspect} args=\#{rs.inspect}\""
 162            else
 163              ""
 164            end,
 165            "  r  = rdec.call(r) if rdec",
 166            "  rs = dec.call(rs) if dec",
 167            "  @retval = r",
 168            "  @args   = rs",
 169            "  return @retval",
 170            "end",
 171            "module_function :#{mname}",
 172          ].join("\n")
 173  
 174          return sym
 175        end
 176  
 177        def _args_
 178          return @args
 179        end
 180  
 181        def _retval_
 182          return @retval
 183        end
 184  
 185        def encode_types(tys)
 186          init_types()
 187          encty = []
 188          enc = nil
 189          dec = nil
 190          tys.each_with_index{|ty,idx|
 191            ty,c1,c2,_,_ = @types.encode_type(ty)
 192            encty.push(ty)
 193            if( enc )
 194              if( c1 )
 195                conv1 = enc
 196                enc = proc{|v| v = conv1.call(v); v[idx] = c1.call(v[idx]); v}
 197              end
 198            else
 199              if( c1 )
 200                enc = proc{|v| v[idx] = c1.call(v[idx]); v}
 201              end
 202            end
 203            if( dec )
 204              if( c2 )
 205                conv2 = dec
 206                dec = proc{|v| v = conv2.call(v); v[idx] = c2.call(v[idx]); v}
 207              end
 208            else
 209              if( c2 )
 210                dec = proc{|v| v[idx] = c2.call(v[idx]); v}
 211              end
 212            end
 213          }
 214          return [encty.join, enc, dec]
 215        end
 216      end # end of Internal
 217      include Internal
 218    end # end of Importable
 219  end