summaryrefslogtreecommitdiff
path: root/lib/ffi/tools/types_generator.rb
blob: ff24ec5167083c95213d7db1d35625a1ab5bafc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
require 'tempfile'

module FFI

  # @private
  class TypesGenerator

    ##
    # Maps different C types to the C type representations we use

    TYPE_MAP = {
               "char" => :char,
        "signed char" => :char,
      "__signed char" => :char,
      "unsigned char" => :uchar,

               "short"     => :short,
        "signed short"     => :short,
        "signed short int" => :short,
      "unsigned short"     => :ushort,
      "unsigned short int" => :ushort,

               "int" => :int,
        "signed int" => :int,
      "unsigned int" => :uint,

               "long" => :long,
               "long int" => :long,
        "signed long" => :long,
        "signed long int" => :long,
      "unsigned long" => :ulong,
      "unsigned long int" => :ulong,
      "long unsigned int" => :ulong,

               "long long"     => :long_long,
               "long long int" => :long_long,
        "signed long long"     => :long_long,
        "signed long long int" => :long_long,
      "unsigned long long"     => :ulong_long,
      "unsigned long long int" => :ulong_long,

      "char *" => :string,
      "void *" => :pointer,
    }

    def self.generate(options = {})
      typedefs = nil
      Tempfile.open 'ffi_types_generator' do |io|
        io.puts <<-C
#include <sys/types.h>
#if !(defined(WIN32))
#include <sys/socket.h>
#include <sys/resource.h>
#endif
        C

        io.close
        cc = ENV['CC'] || 'gcc'
        cmd = "#{cc} -E -x c #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c"
        if options[:input]
          typedefs = File.read(options[:input])
        elsif options[:remote]
          typedefs = `ssh #{options[:remote]} #{cmd} - < #{io.path}`
        else
          typedefs = `#{cmd} #{io.path}`
        end
      end

      code = ""

      typedefs.each_line do |type|
        # We only care about single line typedef
        next unless type =~ /typedef/
        # Ignore unions or structs
        next if type =~ /union|struct/

        # strip off the starting typedef and ending ;
        type.gsub!(/^(.*typedef\s*)/, "")
        type.gsub!(/\s*;\s*$/, "")

        parts = type.split(/\s+/)
        def_type   = parts.join(" ")

        # GCC does mapping with __attribute__ stuf, also see
        # http://hal.cs.berkeley.edu/cil/cil016.html section 16.2.7.  Problem
        # with this is that the __attribute__ stuff can either occur before or
        # after the new type that is defined...
        if type =~ /__attribute__/
          if parts.last =~ /__QI__|__HI__|__SI__|__DI__|__word__/

            # In this case, the new type is BEFORE __attribute__ we need to
            # find the final_type as the type before the part that starts with
            # __attribute__
            final_type = ""
            parts.each do |p|
              break if p =~ /__attribute__/
              final_type = p
            end
          else
            final_type = parts.pop
          end

          def_type = case type
                     when /__QI__/   then "char"
                     when /__HI__/   then "short"
                     when /__SI__/   then "int"
                     when /__DI__/   then "long long"
                     when /__word__/ then "long"
                     else                 "int"
                     end

          def_type = "unsigned #{def_type}" if type =~ /unsigned/
        else
          final_type = parts.pop
          def_type   = parts.join(" ")
        end

        if type = TYPE_MAP[def_type]
          code << "rbx.platform.typedef.#{final_type} = #{type}\n"
          TYPE_MAP[final_type] = TYPE_MAP[def_type]
        else
          # Fallback to an ordinary pointer if we don't know the type
          if def_type =~ /\*/
            code << "rbx.platform.typedef.#{final_type} = pointer\n"
            TYPE_MAP[final_type] = :pointer
          end
        end
      end

      code
    end
  end
end