ghazel-ffi_gen 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +83 -0
- data/lib/ffi_gen/clang.rb +5932 -0
- data/lib/ffi_gen/empty.h +0 -0
- data/lib/ffi_gen/java_enum.java +3 -0
- data/lib/ffi_gen/java_output.rb +324 -0
- data/lib/ffi_gen/java_pre.java +35 -0
- data/lib/ffi_gen/ruby_output.rb +302 -0
- data/lib/ffi_gen.rb +763 -0
- metadata +67 -0
data/lib/ffi_gen/empty.h
ADDED
File without changes
|
@@ -0,0 +1,324 @@
|
|
1
|
+
class FFIGen
|
2
|
+
def generate_java
|
3
|
+
writer = Writer.new " ", " * ", "/**", " */"
|
4
|
+
writer.puts "// Generated by ffi_gen. Please do not change this file by hand.", "import java.util.*;", "import com.sun.jna.*;", "import java.lang.annotation.*;", "import java.lang.reflect.Method;", "", "public class #{@module_name} {"
|
5
|
+
writer.indent do
|
6
|
+
writer.puts "private static #{@module_name}Interface INSTANCE = #{@module_name}Interface.JnaInstanceCreator.createInstance();"
|
7
|
+
writer.puts "", *IO.readlines(File.join(File.dirname(__FILE__), "java_enum.java")).map(&:rstrip), ""
|
8
|
+
|
9
|
+
declarations.each do |declaration|
|
10
|
+
if declaration.respond_to? :write_static_java
|
11
|
+
declaration.write_static_java writer
|
12
|
+
else
|
13
|
+
declaration.write_java writer
|
14
|
+
end
|
15
|
+
end
|
16
|
+
writer.puts ""
|
17
|
+
|
18
|
+
writer.puts "private interface #{@module_name}Interface extends Library {"
|
19
|
+
writer.indent do
|
20
|
+
writer.puts "", *IO.readlines(File.join(File.dirname(__FILE__), "java_pre.java")).map(&:rstrip), ""
|
21
|
+
writer.puts "static class JnaInstanceCreator {"
|
22
|
+
writer.indent do
|
23
|
+
writer.puts "private static #{@module_name}Interface createInstance() {"
|
24
|
+
writer.indent do
|
25
|
+
writer.puts "DefaultTypeMapper typeMapper = new DefaultTypeMapper();", "typeMapper.addFromNativeConverter(NativeEnum.class, new EnumConverter());", "typeMapper.addToNativeConverter(NativeEnum.class, new EnumConverter());", ""
|
26
|
+
writer.puts "Map<String, Object> options = new HashMap<String, Object>();", "options.put(Library.OPTION_FUNCTION_MAPPER, new NativeNameAnnotationFunctionMapper());", "options.put(Library.OPTION_TYPE_MAPPER, typeMapper);", ""
|
27
|
+
writer.puts "return (#{@module_name}Interface) Native.loadLibrary(\"#{@ffi_lib}\", #{@module_name}Interface.class, options);"
|
28
|
+
end
|
29
|
+
writer.puts "}"
|
30
|
+
end
|
31
|
+
writer.puts "}", ""
|
32
|
+
declarations.each do |declaration|
|
33
|
+
if declaration.is_a? FunctionOrCallback
|
34
|
+
declaration.write_java writer
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
writer.puts "}"
|
39
|
+
end
|
40
|
+
writer.puts "}"
|
41
|
+
writer.output
|
42
|
+
end
|
43
|
+
|
44
|
+
class Name
|
45
|
+
JAVA_KEYWORDS = %w{abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while}
|
46
|
+
|
47
|
+
def to_java_downcase
|
48
|
+
format :camelcase, :initial_downcase, JAVA_KEYWORDS
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_java_classname
|
52
|
+
format :camelcase, JAVA_KEYWORDS
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_java_constant
|
56
|
+
format :upcase, :underscores, JAVA_KEYWORDS
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Type
|
61
|
+
def java_description
|
62
|
+
java_name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Enum
|
67
|
+
def write_java(writer)
|
68
|
+
return if @name.nil?
|
69
|
+
shorten_names
|
70
|
+
|
71
|
+
writer.comment do
|
72
|
+
writer.write_description @description
|
73
|
+
writer.puts "", "<em>This entry is only for documentation and no real method. The FFI::Enum can be accessed via #enum_type(:#{java_name}).</em>"
|
74
|
+
writer.puts "", "=== Options:"
|
75
|
+
@constants.each do |constant|
|
76
|
+
writer.puts "#{constant[:name].to_java_constant} ::"
|
77
|
+
writer.write_description constant[:comment], false, " ", " "
|
78
|
+
end
|
79
|
+
writer.puts "", "@method _enum_#{java_name}_", "@return [Symbol]", "@scope class"
|
80
|
+
end
|
81
|
+
|
82
|
+
writer.puts "public enum #{java_name} implements NativeEnum {"
|
83
|
+
writer.indent do
|
84
|
+
writer.write_array @constants, "," do |constant|
|
85
|
+
"#{constant[:name].to_java_constant}(#{constant[:value]})"
|
86
|
+
end
|
87
|
+
writer.puts ";"
|
88
|
+
|
89
|
+
writer.puts "", "private int nativeInt;", "", "private #{java_name}(int nativeInt) {", " this.nativeInt = nativeInt;", "}", "", "@Override", "public int toNativeInt() {", " return nativeInt;", "}"
|
90
|
+
end
|
91
|
+
writer.puts "}", ""
|
92
|
+
end
|
93
|
+
|
94
|
+
def java_name
|
95
|
+
@java_name ||= @name.to_java_classname
|
96
|
+
end
|
97
|
+
|
98
|
+
def java_jna_type
|
99
|
+
java_name
|
100
|
+
end
|
101
|
+
|
102
|
+
def java_description
|
103
|
+
"Symbol from _enum_#{java_name}_"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class StructOrUnion
|
108
|
+
def write_java(writer)
|
109
|
+
writer.comment do
|
110
|
+
writer.write_description @description
|
111
|
+
unless @fields.empty?
|
112
|
+
writer.puts "", "= Fields:"
|
113
|
+
@fields.each do |field|
|
114
|
+
writer.puts ":#{field[:name].to_java_downcase} ::"
|
115
|
+
writer.write_description field[:comment], false, " (#{field[:type].java_description}) ", " "
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
writer.puts "public static class #{java_name} extends #{@is_union ? 'Union' : (@fields.empty? ? 'PointerType' : 'Structure')} {"
|
121
|
+
writer.indent do
|
122
|
+
@fields.each do |field|
|
123
|
+
writer.puts "public #{field[:type].java_jna_type} #{field[:name].raw};"
|
124
|
+
end
|
125
|
+
writer.puts "// hidden structure" if @fields.empty?
|
126
|
+
end
|
127
|
+
writer.indent do
|
128
|
+
writer.puts "protected List<String> getFieldOrder() {"
|
129
|
+
writer.indent do
|
130
|
+
fs = @fields.map{|f| '"' + f[:name].raw + '"'}.join(", ")
|
131
|
+
writer.puts "return Arrays.asList(new String[] { #{fs} } );"
|
132
|
+
end
|
133
|
+
writer.puts "}"
|
134
|
+
end
|
135
|
+
writer.puts "}", ""
|
136
|
+
|
137
|
+
@written = true
|
138
|
+
end
|
139
|
+
|
140
|
+
def java_name
|
141
|
+
@java_name ||= @name.to_java_classname
|
142
|
+
end
|
143
|
+
|
144
|
+
def java_jna_type
|
145
|
+
@written ? java_name : "Pointer"
|
146
|
+
end
|
147
|
+
|
148
|
+
def java_description
|
149
|
+
@written ? java_name : "FFI::Pointer(*#{java_name})"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class FunctionOrCallback
|
154
|
+
def write_java(writer)
|
155
|
+
return if @is_callback # not yet supported
|
156
|
+
|
157
|
+
jna_signature = "#{@parameters.map{ |parameter| "#{parameter[:type].java_jna_type} #{parameter[:name].to_java_downcase}" }.join(', ')}"
|
158
|
+
if @is_callback
|
159
|
+
writer.puts "callback :#{java_name}, #{jna_signature}", ""
|
160
|
+
else
|
161
|
+
writer.puts "@NativeName(\"#{@name.raw}\")", "#{@return_type.java_jna_type} #{java_name}(#{jna_signature});", ""
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def write_static_java(writer)
|
166
|
+
return if @is_callback # not yet supported
|
167
|
+
|
168
|
+
writer.comment do
|
169
|
+
writer.write_description @function_description
|
170
|
+
writer.puts "", "<em>This entry is only for documentation and no real method.</em>" if @is_callback
|
171
|
+
writer.puts "", "@method #{@is_callback ? "_callback_#{java_name}_" : java_name}(#{@parameters.map{ |parameter| parameter[:name].to_java_downcase }.join(', ')})"
|
172
|
+
@parameters.each do |parameter|
|
173
|
+
writer.write_description parameter[:description], false, "@param [#{parameter[:type].java_description}] #{parameter[:name].to_java_downcase} ", " "
|
174
|
+
end
|
175
|
+
writer.write_description @return_value_description, false, "@return [#{@return_type.java_description}] ", " "
|
176
|
+
writer.puts "@scope class"
|
177
|
+
end
|
178
|
+
|
179
|
+
args = @parameters.map{ |parameter| parameter[:name].to_java_downcase }.join(', ')
|
180
|
+
jna_signature = "#{@parameters.map{ |parameter| "#{parameter[:type].java_jna_type} #{parameter[:name].to_java_downcase}" }.join(', ')}"
|
181
|
+
writer.puts "static #{@return_type.java_jna_type} #{java_name}(#{jna_signature}) {"
|
182
|
+
writer.indent do
|
183
|
+
call = "INSTANCE.#{java_name}(#{args});"
|
184
|
+
if @return_type.respond_to? :no_return and @return_type.no_return
|
185
|
+
writer.puts call
|
186
|
+
elsif @return_type.is_a? StructOrUnion
|
187
|
+
writer.puts "return (#{@return_type.java_jna_type})#{call}"
|
188
|
+
else
|
189
|
+
writer.puts "return #{call}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
writer.puts "}", ""
|
193
|
+
end
|
194
|
+
|
195
|
+
def java_name
|
196
|
+
@java_name ||= @name.to_java_downcase
|
197
|
+
end
|
198
|
+
|
199
|
+
def java_jna_type
|
200
|
+
java_name
|
201
|
+
end
|
202
|
+
|
203
|
+
def java_description
|
204
|
+
"Proc(_callback_#{java_name}_)"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class Define
|
209
|
+
def write_java(writer)
|
210
|
+
parts = @value.map { |v|
|
211
|
+
if v.is_a? Array
|
212
|
+
case v[0]
|
213
|
+
when :method then v[1].to_java_downcase
|
214
|
+
when :constant then v[1].to_java_constant
|
215
|
+
else raise ArgumentError
|
216
|
+
end
|
217
|
+
else
|
218
|
+
v
|
219
|
+
end
|
220
|
+
}
|
221
|
+
if @parameters
|
222
|
+
# not implemented
|
223
|
+
else
|
224
|
+
writer.puts "public static int #{@name.to_java_constant} = #{parts.join};", ""
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class PrimitiveType
|
230
|
+
def java_name
|
231
|
+
case @clang_type
|
232
|
+
when :void
|
233
|
+
"nil"
|
234
|
+
when :bool
|
235
|
+
"Boolean"
|
236
|
+
when :u_char, :u_short, :u_int, :u_long, :u_long_long, :char_s, :s_char, :short, :int, :long, :long_long
|
237
|
+
"Integer"
|
238
|
+
when :float, :double
|
239
|
+
"Float"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def java_jna_type
|
244
|
+
case @clang_type
|
245
|
+
when :void then "void"
|
246
|
+
when :bool then "boolean"
|
247
|
+
when :u_char then "byte"
|
248
|
+
when :u_short then "short"
|
249
|
+
when :u_int then "int"
|
250
|
+
when :u_long then "NativeLong"
|
251
|
+
when :u_long_long then "long"
|
252
|
+
when :char_s, :s_char then "byte"
|
253
|
+
when :short then "short"
|
254
|
+
when :int then "int"
|
255
|
+
when :long then "NativeLong"
|
256
|
+
when :long_long then "long"
|
257
|
+
when :float then "float"
|
258
|
+
when :double then "double"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class StringType
|
264
|
+
def java_name
|
265
|
+
"String"
|
266
|
+
end
|
267
|
+
|
268
|
+
def java_jna_type
|
269
|
+
"String"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class ByValueType
|
274
|
+
def java_name
|
275
|
+
@inner_type.java_name
|
276
|
+
end
|
277
|
+
|
278
|
+
def java_jna_type
|
279
|
+
@inner_type.java_jna_type
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class PointerType
|
284
|
+
def java_name
|
285
|
+
@pointee_name.to_java_downcase
|
286
|
+
end
|
287
|
+
|
288
|
+
def java_jna_type
|
289
|
+
"Pointer"
|
290
|
+
end
|
291
|
+
|
292
|
+
def java_description
|
293
|
+
"FFI::Pointer(#{'*' * @depth}#{@pointee_name ? @pointee_name.to_java_classname : ''})"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
class ArrayType
|
298
|
+
def java_name
|
299
|
+
"array"
|
300
|
+
end
|
301
|
+
|
302
|
+
def java_jna_type
|
303
|
+
if @constant_size
|
304
|
+
raise
|
305
|
+
else
|
306
|
+
"#{@element_type.java_jna_type}[]"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def java_description
|
311
|
+
"Array of #{@element_type.java_description}"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class UnknownType
|
316
|
+
def java_name
|
317
|
+
"unknown"
|
318
|
+
end
|
319
|
+
|
320
|
+
def java_jna_type
|
321
|
+
"byte"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
@Retention(RetentionPolicy.RUNTIME)
|
2
|
+
@Target(ElementType.METHOD)
|
3
|
+
@interface NativeName {
|
4
|
+
String value();
|
5
|
+
}
|
6
|
+
|
7
|
+
class NativeNameAnnotationFunctionMapper implements FunctionMapper {
|
8
|
+
@Override
|
9
|
+
public String getFunctionName(NativeLibrary library, Method method) {
|
10
|
+
return method.getAnnotation(NativeName.class).value();
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
class EnumConverter implements TypeConverter {
|
15
|
+
public Class<?> nativeType() {
|
16
|
+
return Integer.class;
|
17
|
+
}
|
18
|
+
|
19
|
+
public Object fromNative(Object input, FromNativeContext context) {
|
20
|
+
int intValue = (Integer) input;
|
21
|
+
|
22
|
+
Class<?> targetClass = context.getTargetType();
|
23
|
+
for (Object constant : targetClass.getEnumConstants()) {
|
24
|
+
if (((NativeEnum) constant).toNativeInt() == intValue) {
|
25
|
+
return constant;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
throw new IllegalArgumentException("No constant with integer value " + intValue + " in enum " + targetClass.getName() + ".");
|
30
|
+
}
|
31
|
+
|
32
|
+
public Object toNative(Object input, ToNativeContext context) {
|
33
|
+
return ((NativeEnum) input).toNativeInt();
|
34
|
+
}
|
35
|
+
}
|
@@ -0,0 +1,302 @@
|
|
1
|
+
class FFIGen
|
2
|
+
def generate_rb
|
3
|
+
writer = Writer.new " ", "# "
|
4
|
+
writer.puts "# Generated by ffi_gen. Please do not change this file by hand.", "", "require 'ffi'", "", "module #{@module_name}"
|
5
|
+
writer.indent do
|
6
|
+
writer.puts "extend FFI::Library"
|
7
|
+
writer.puts "ffi_lib_flags #{@ffi_lib_flags.map(&:inspect).join(', ')}" if @ffi_lib_flags
|
8
|
+
writer.puts "ffi_lib #{@ffi_lib.inspect}", "" if @ffi_lib
|
9
|
+
writer.puts "def self.attach_function(name, *_)", " begin; super; rescue FFI::NotFoundError => e", " (class << self; self; end).class_eval { define_method(name) { |*_| raise e } }", " end", "end", ""
|
10
|
+
declarations.each do |declaration|
|
11
|
+
declaration.write_ruby writer
|
12
|
+
end
|
13
|
+
end
|
14
|
+
writer.puts "end"
|
15
|
+
writer.output
|
16
|
+
end
|
17
|
+
|
18
|
+
class Name
|
19
|
+
RUBY_KEYWORDS = %w{alias and begin break case class def defined do else elsif end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield BEGIN END}
|
20
|
+
|
21
|
+
def to_ruby_downcase
|
22
|
+
format :downcase, :underscores, RUBY_KEYWORDS
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_ruby_classname
|
26
|
+
format :camelcase, RUBY_KEYWORDS
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_ruby_constant
|
30
|
+
format :upcase, :underscores, RUBY_KEYWORDS
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Type
|
35
|
+
def ruby_description
|
36
|
+
ruby_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Enum
|
41
|
+
def write_ruby(writer)
|
42
|
+
return if @name.nil?
|
43
|
+
shorten_names
|
44
|
+
|
45
|
+
@constants.each do |constant|
|
46
|
+
constant[:symbol] = ":#{constant[:name].to_ruby_downcase}"
|
47
|
+
end
|
48
|
+
|
49
|
+
writer.comment do
|
50
|
+
writer.write_description @description
|
51
|
+
writer.puts "", "<em>This entry is only for documentation and no real method. The FFI::Enum can be accessed via #enum_type(:#{ruby_name}).</em>"
|
52
|
+
writer.puts "", "=== Options:"
|
53
|
+
@constants.each do |constant|
|
54
|
+
writer.puts "#{constant[:symbol]} ::"
|
55
|
+
writer.write_description constant[:comment], false, " ", " "
|
56
|
+
end
|
57
|
+
writer.puts "", "@method _enum_#{ruby_name}_", "@return [Symbol]", "@scope class"
|
58
|
+
end
|
59
|
+
|
60
|
+
writer.puts "enum :#{ruby_name}, ["
|
61
|
+
writer.indent do
|
62
|
+
writer.write_array @constants, "," do |constant|
|
63
|
+
"#{constant[:symbol]}, #{constant[:value]}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
writer.puts "]", ""
|
67
|
+
end
|
68
|
+
|
69
|
+
def ruby_name
|
70
|
+
@ruby_name ||= @name.to_ruby_downcase
|
71
|
+
end
|
72
|
+
|
73
|
+
def ruby_ffi_type
|
74
|
+
":#{ruby_name}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def ruby_description
|
78
|
+
"Symbol from _enum_#{ruby_name}_"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class StructOrUnion
|
83
|
+
def write_ruby(writer)
|
84
|
+
writer.comment do
|
85
|
+
writer.write_description @description
|
86
|
+
unless @fields.empty?
|
87
|
+
writer.puts "", "= Fields:"
|
88
|
+
@fields.each do |field|
|
89
|
+
writer.puts ":#{field[:name].to_ruby_downcase} ::"
|
90
|
+
writer.write_description field[:comment], false, " (#{field[:type].ruby_description}) ", " "
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
@fields << { name: Name.new(["dummy"]), type: PrimitiveType.new(:char_s) } if @fields.empty?
|
96
|
+
|
97
|
+
unless @oo_functions.empty?
|
98
|
+
writer.puts "module #{ruby_name}Wrappers"
|
99
|
+
writer.indent do
|
100
|
+
@oo_functions.each_with_index do |(name, function), index|
|
101
|
+
parameter_names = function.parameters[1..-1].map { |parameter| parameter[:name].to_ruby_downcase }
|
102
|
+
writer.puts "" unless index == 0
|
103
|
+
writer.comment do
|
104
|
+
function.parameters[1..-1].each do |parameter|
|
105
|
+
writer.write_description parameter[:description], false, "@param [#{parameter[:type].ruby_description}] #{parameter[:name].to_ruby_downcase} ", " "
|
106
|
+
end
|
107
|
+
return_type = function.return_type.is_a?(StructOrUnion) ? function.return_type.ruby_name : function.return_type.ruby_description
|
108
|
+
writer.write_description function.return_value_description, false, "@return [#{return_type}] ", " "
|
109
|
+
end
|
110
|
+
writer.puts "def #{name.to_ruby_downcase}(#{parameter_names.join(', ')})"
|
111
|
+
writer.indent do
|
112
|
+
cast = function.return_type.is_a?(StructOrUnion) ? "#{function.return_type.ruby_name}.new " : ""
|
113
|
+
writer.puts "#{cast}#{@generator.module_name}.#{function.ruby_name}(#{(["self"] + parameter_names).join(', ')})"
|
114
|
+
end
|
115
|
+
writer.puts "end"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
writer.puts "end", ""
|
119
|
+
end
|
120
|
+
|
121
|
+
writer.puts "class #{ruby_name} < #{@is_union ? 'FFI::Union' : 'FFI::Struct'}"
|
122
|
+
writer.indent do
|
123
|
+
writer.puts "include #{ruby_name}Wrappers" unless @oo_functions.empty?
|
124
|
+
writer.write_array @fields, ",", "layout ", " " do |field|
|
125
|
+
":#{field[:name].to_ruby_downcase}, #{field[:type].ruby_ffi_type}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
writer.puts "end", ""
|
129
|
+
|
130
|
+
@written = true
|
131
|
+
end
|
132
|
+
|
133
|
+
def ruby_name
|
134
|
+
@ruby_name ||= @name.to_ruby_classname
|
135
|
+
end
|
136
|
+
|
137
|
+
def ruby_ffi_type
|
138
|
+
@written ? ruby_name : ":pointer"
|
139
|
+
end
|
140
|
+
|
141
|
+
def ruby_description
|
142
|
+
@written ? ruby_name : "FFI::Pointer(*#{ruby_name})"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class FunctionOrCallback
|
147
|
+
def write_ruby(writer)
|
148
|
+
writer.puts "@blocking = true" if @blocking
|
149
|
+
writer.comment do
|
150
|
+
writer.write_description @function_description
|
151
|
+
writer.puts "", "<em>This entry is only for documentation and no real method.</em>" if @is_callback
|
152
|
+
writer.puts "", "@method #{@is_callback ? "_callback_#{ruby_name}_" : ruby_name}(#{@parameters.map{ |parameter| parameter[:name].to_ruby_downcase }.join(', ')})"
|
153
|
+
@parameters.each do |parameter|
|
154
|
+
writer.write_description parameter[:description], false, "@param [#{parameter[:type].ruby_description}] #{parameter[:name].to_ruby_downcase} ", " "
|
155
|
+
end
|
156
|
+
writer.write_description @return_value_description, false, "@return [#{@return_type.ruby_description}] ", " "
|
157
|
+
writer.puts "@scope class"
|
158
|
+
end
|
159
|
+
|
160
|
+
ffi_signature = "[#{@parameters.map{ |parameter| parameter[:type].ruby_ffi_type }.join(', ')}], #{@return_type.ruby_ffi_type}"
|
161
|
+
if @is_callback
|
162
|
+
writer.puts "callback :#{ruby_name}, #{ffi_signature}", ""
|
163
|
+
else
|
164
|
+
writer.puts "attach_function :#{ruby_name}, :#{@name.raw}, #{ffi_signature}", ""
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def ruby_name
|
169
|
+
@ruby_name ||= @name.to_ruby_downcase
|
170
|
+
end
|
171
|
+
|
172
|
+
def ruby_ffi_type
|
173
|
+
":#{ruby_name}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def ruby_description
|
177
|
+
"Proc(_callback_#{ruby_name}_)"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Define
|
182
|
+
def write_ruby(writer)
|
183
|
+
parts = @value.map { |v|
|
184
|
+
if v.is_a? Array
|
185
|
+
case v[0]
|
186
|
+
when :method then v[1].to_ruby_downcase
|
187
|
+
when :constant then v[1].to_ruby_constant
|
188
|
+
else raise ArgumentError
|
189
|
+
end
|
190
|
+
else
|
191
|
+
v
|
192
|
+
end
|
193
|
+
}
|
194
|
+
if @parameters
|
195
|
+
writer.puts "def #{@name.to_ruby_downcase}(#{@parameters.join(", ")})"
|
196
|
+
writer.indent do
|
197
|
+
writer.puts parts.join
|
198
|
+
end
|
199
|
+
writer.puts "end", ""
|
200
|
+
else
|
201
|
+
writer.puts "#{@name.to_ruby_constant} = #{parts.join}", ""
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class PrimitiveType
|
207
|
+
def ruby_name
|
208
|
+
case @clang_type
|
209
|
+
when :void
|
210
|
+
"nil"
|
211
|
+
when :bool
|
212
|
+
"Boolean"
|
213
|
+
when :u_char, :u_short, :u_int, :u_long, :u_long_long, :char_s, :s_char, :short, :int, :long, :long_long
|
214
|
+
"Integer"
|
215
|
+
when :float, :double
|
216
|
+
"Float"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def ruby_ffi_type
|
221
|
+
case @clang_type
|
222
|
+
when :void then ":void"
|
223
|
+
when :bool then ":bool"
|
224
|
+
when :u_char then ":uchar"
|
225
|
+
when :u_short then ":ushort"
|
226
|
+
when :u_int then ":uint"
|
227
|
+
when :u_long then ":ulong"
|
228
|
+
when :u_long_long then ":ulong_long"
|
229
|
+
when :char_s, :s_char then ":char"
|
230
|
+
when :short then ":short"
|
231
|
+
when :int then ":int"
|
232
|
+
when :long then ":long"
|
233
|
+
when :long_long then ":long_long"
|
234
|
+
when :float then ":float"
|
235
|
+
when :double then ":double"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
class StringType
|
241
|
+
def ruby_name
|
242
|
+
"String"
|
243
|
+
end
|
244
|
+
|
245
|
+
def ruby_ffi_type
|
246
|
+
":string"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
class ByValueType
|
251
|
+
def ruby_name
|
252
|
+
@inner_type.ruby_name
|
253
|
+
end
|
254
|
+
|
255
|
+
def ruby_ffi_type
|
256
|
+
"#{@inner_type.ruby_ffi_type}.by_value"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
class PointerType
|
261
|
+
def ruby_name
|
262
|
+
@pointee_name.to_ruby_downcase
|
263
|
+
end
|
264
|
+
|
265
|
+
def ruby_ffi_type
|
266
|
+
":pointer"
|
267
|
+
end
|
268
|
+
|
269
|
+
def ruby_description
|
270
|
+
"FFI::Pointer(#{'*' * @depth}#{@pointee_name ? @pointee_name.to_ruby_classname : ''})"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
class ArrayType
|
275
|
+
def ruby_name
|
276
|
+
"array"
|
277
|
+
end
|
278
|
+
|
279
|
+
def ruby_ffi_type
|
280
|
+
if @constant_size
|
281
|
+
"[#{@element_type.ruby_ffi_type}, #{@constant_size}]"
|
282
|
+
else
|
283
|
+
":pointer"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def ruby_description
|
288
|
+
"Array<#{@element_type.ruby_description}>"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
class UnknownType
|
293
|
+
def ruby_name
|
294
|
+
"unknown"
|
295
|
+
end
|
296
|
+
|
297
|
+
def ruby_ffi_type
|
298
|
+
":char"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|