ffidb 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/CHANGES.md +7 -0
- data/CREDITS.md +2 -0
- data/README.md +201 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/bin/ffidb +387 -0
- data/etc/mappings/dart.yaml +35 -0
- data/etc/mappings/java.yaml +36 -0
- data/etc/mappings/lisp.yaml +35 -0
- data/etc/mappings/python.yaml +35 -0
- data/etc/mappings/ruby.yaml +35 -0
- data/etc/templates/c.erb +46 -0
- data/etc/templates/cpp.erb +45 -0
- data/etc/templates/dart.erb +64 -0
- data/etc/templates/go.erb +50 -0
- data/etc/templates/java.erb +56 -0
- data/etc/templates/lisp.erb +49 -0
- data/etc/templates/python.erb +59 -0
- data/etc/templates/ruby.erb +48 -0
- data/lib/ffidb.rb +34 -0
- data/lib/ffidb/enum.rb +37 -0
- data/lib/ffidb/errors.rb +64 -0
- data/lib/ffidb/exporter.rb +141 -0
- data/lib/ffidb/exporters.rb +28 -0
- data/lib/ffidb/exporters/c.rb +52 -0
- data/lib/ffidb/exporters/cpp.rb +13 -0
- data/lib/ffidb/exporters/csharp.rb +6 -0
- data/lib/ffidb/exporters/csv.rb +24 -0
- data/lib/ffidb/exporters/dart.rb +60 -0
- data/lib/ffidb/exporters/go.rb +16 -0
- data/lib/ffidb/exporters/haskell.rb +3 -0
- data/lib/ffidb/exporters/java.rb +39 -0
- data/lib/ffidb/exporters/json.rb +38 -0
- data/lib/ffidb/exporters/julia.rb +3 -0
- data/lib/ffidb/exporters/lisp.rb +41 -0
- data/lib/ffidb/exporters/luajit.rb +3 -0
- data/lib/ffidb/exporters/nim.rb +4 -0
- data/lib/ffidb/exporters/nodejs.rb +4 -0
- data/lib/ffidb/exporters/ocaml.rb +4 -0
- data/lib/ffidb/exporters/php.rb +4 -0
- data/lib/ffidb/exporters/python.rb +35 -0
- data/lib/ffidb/exporters/racket.rb +3 -0
- data/lib/ffidb/exporters/ruby.rb +33 -0
- data/lib/ffidb/exporters/rust.rb +5 -0
- data/lib/ffidb/exporters/yaml.rb +31 -0
- data/lib/ffidb/exporters/zig.rb +3 -0
- data/lib/ffidb/function.rb +70 -0
- data/lib/ffidb/glob.rb +28 -0
- data/lib/ffidb/header.rb +19 -0
- data/lib/ffidb/header_parser.rb +339 -0
- data/lib/ffidb/library.rb +120 -0
- data/lib/ffidb/library_parser.rb +132 -0
- data/lib/ffidb/location.rb +17 -0
- data/lib/ffidb/parameter.rb +35 -0
- data/lib/ffidb/registry.rb +87 -0
- data/lib/ffidb/release.rb +14 -0
- data/lib/ffidb/struct.rb +41 -0
- data/lib/ffidb/symbol_table.rb +90 -0
- data/lib/ffidb/symbolic.rb +67 -0
- data/lib/ffidb/sysexits.rb +21 -0
- data/lib/ffidb/type.rb +214 -0
- data/lib/ffidb/typedef.rb +38 -0
- data/lib/ffidb/union.rb +37 -0
- data/lib/ffidb/version.rb +21 -0
- metadata +197 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# See: https://api.dart.dev/dev/dart-ffi/dart-ffi-library.html
|
2
|
+
---
|
3
|
+
_Bool: Int8 # TODO
|
4
|
+
char *: Pointer<Int8> # TODO: Utf8
|
5
|
+
char: Int8
|
6
|
+
const char *: Pointer<Int8> # TODO: Utf8
|
7
|
+
double: Double
|
8
|
+
float: Float
|
9
|
+
int16_t: Int16
|
10
|
+
int32_t: Int32
|
11
|
+
int64_t: Int64
|
12
|
+
int8_t: Int8
|
13
|
+
int: Int32 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
14
|
+
intptr_t: IntPtr
|
15
|
+
long double: # not supported
|
16
|
+
long long: Int64
|
17
|
+
long: Int64 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
18
|
+
off_t: Uint64 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
19
|
+
short: Int16
|
20
|
+
size_t: Uint64 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
21
|
+
ssize_t: Int64 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
22
|
+
uint16_t: Uint16
|
23
|
+
uint32_t: Uint32
|
24
|
+
uint64_t: Uint64
|
25
|
+
uint8_t: Uint8
|
26
|
+
uintptr_t: IntPtr
|
27
|
+
unsigned char: Uint8
|
28
|
+
unsigned int: Uint32 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
29
|
+
unsigned long long: Uint64
|
30
|
+
unsigned long: Uint64 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
31
|
+
unsigned short: Uint16
|
32
|
+
va_list: Pointer<Void>
|
33
|
+
void *: Pointer<Void>
|
34
|
+
void: Void
|
35
|
+
wchar_t: Int32 # TODO: https://github.com/dart-lang/sdk/issues/36140
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# See: https://github.com/java-native-access/jna/blob/master/www/Mappings.md
|
2
|
+
# See: https://java-native-access.github.io/jna/5.5.0/javadoc/overview-summary.html#marshalling
|
3
|
+
---
|
4
|
+
_Bool: boolean
|
5
|
+
char *: String
|
6
|
+
char: byte
|
7
|
+
const char *: String
|
8
|
+
double: double
|
9
|
+
float: float
|
10
|
+
int16_t: short
|
11
|
+
int32_t: int
|
12
|
+
int64_t: long
|
13
|
+
int8_t: byte
|
14
|
+
int: int
|
15
|
+
intptr_t: Pointer
|
16
|
+
long double: # https://github.com/java-native-access/jna/issues/860
|
17
|
+
long long: long
|
18
|
+
long: NativeLong
|
19
|
+
off_t: size_t # TODO
|
20
|
+
short: short
|
21
|
+
size_t: size_t # https://github.com/java-native-access/jna/issues/1113
|
22
|
+
ssize_t: ssize_t
|
23
|
+
uint16_t: short
|
24
|
+
uint32_t: int
|
25
|
+
uint64_t: long
|
26
|
+
uint8_t: byte
|
27
|
+
uintptr_t: Pointer
|
28
|
+
unsigned char: byte
|
29
|
+
unsigned int: int
|
30
|
+
unsigned long long: long
|
31
|
+
unsigned long: NativeLong
|
32
|
+
unsigned short: short
|
33
|
+
va_list: Pointer
|
34
|
+
void *: Pointer
|
35
|
+
void: void
|
36
|
+
wchar_t: char
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# See: https://common-lisp.net/project/cffi/manual/html_node/Foreign-Types.html
|
2
|
+
---
|
3
|
+
_Bool: bool
|
4
|
+
char *: string
|
5
|
+
char: char
|
6
|
+
const char *: string
|
7
|
+
double: double
|
8
|
+
float: float
|
9
|
+
int16_t: int16
|
10
|
+
int32_t: int32
|
11
|
+
int64_t: int64
|
12
|
+
int8_t: int8
|
13
|
+
int: int
|
14
|
+
intptr_t: pointer
|
15
|
+
long double: long-double
|
16
|
+
long long: llong
|
17
|
+
long: long
|
18
|
+
off_t: ulong # TODO
|
19
|
+
short: short
|
20
|
+
size_t: size-t
|
21
|
+
ssize_t: long # TODO
|
22
|
+
uint16_t: uint16
|
23
|
+
uint32_t: uint32
|
24
|
+
uint64_t: uint64
|
25
|
+
uint8_t: uint8
|
26
|
+
uintptr_t: pointer
|
27
|
+
unsigned char: uchar
|
28
|
+
unsigned int: uint
|
29
|
+
unsigned long long: ullong
|
30
|
+
unsigned long: ulong
|
31
|
+
unsigned short: ushort
|
32
|
+
va_list: pointer
|
33
|
+
void *: pointer
|
34
|
+
void: void
|
35
|
+
wchar_t: int # TODO: https://stackoverflow.com/a/13510080
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# See: https://docs.python.org/3/library/ctypes.html
|
2
|
+
---
|
3
|
+
_Bool: c_bool
|
4
|
+
char *: c_char_p
|
5
|
+
char: c_char
|
6
|
+
const char *: c_char_p
|
7
|
+
double: c_double
|
8
|
+
float: c_float
|
9
|
+
int16_t: c_int16
|
10
|
+
int32_t: c_int32
|
11
|
+
int64_t: c_int64
|
12
|
+
int8_t: c_int8
|
13
|
+
int: c_int
|
14
|
+
intptr_t: c_void_p
|
15
|
+
long double: c_longdouble
|
16
|
+
long long: c_longlong
|
17
|
+
long: c_long
|
18
|
+
off_t: c_size_t # TODO: https://stackoverflow.com/q/43671524
|
19
|
+
short: c_short
|
20
|
+
size_t: c_size_t
|
21
|
+
ssize_t: c_ssize_t
|
22
|
+
uint16_t: c_uint16
|
23
|
+
uint32_t: c_uint32
|
24
|
+
uint64_t: c_uint64
|
25
|
+
uint8_t: c_uint8
|
26
|
+
uintptr_t: c_void_p
|
27
|
+
unsigned char: c_ubyte
|
28
|
+
unsigned int: c_uint
|
29
|
+
unsigned long long: c_ulonglong
|
30
|
+
unsigned long: c_ulong
|
31
|
+
unsigned short: c_ushort
|
32
|
+
va_list: c_void_p
|
33
|
+
void *: c_void_p
|
34
|
+
void: None
|
35
|
+
wchar_t: c_wchar_t
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# See: https://github.com/ffi/ffi/wiki/Types
|
2
|
+
---
|
3
|
+
_Bool: bool
|
4
|
+
char *: string
|
5
|
+
char: char
|
6
|
+
const char *: string
|
7
|
+
double: double
|
8
|
+
float: float
|
9
|
+
int16_t: int16
|
10
|
+
int32_t: int32
|
11
|
+
int64_t: int64
|
12
|
+
int8_t: int8
|
13
|
+
int: int
|
14
|
+
intptr_t: pointer
|
15
|
+
long double: long_double
|
16
|
+
long long: long_long
|
17
|
+
long: long
|
18
|
+
off_t: off_t
|
19
|
+
short: short
|
20
|
+
size_t: size_t
|
21
|
+
ssize_t: ssize_t
|
22
|
+
uint16_t: uint16
|
23
|
+
uint32_t: uint32
|
24
|
+
uint64_t: uint64
|
25
|
+
uint8_t: uint8
|
26
|
+
uintptr_t: pointer
|
27
|
+
unsigned char: uchar
|
28
|
+
unsigned int: uint
|
29
|
+
unsigned long long: ulong_long
|
30
|
+
unsigned long: ulong
|
31
|
+
unsigned short: ushort
|
32
|
+
va_list: pointer
|
33
|
+
void *: pointer
|
34
|
+
void: void
|
35
|
+
wchar_t: wchar_t
|
data/etc/templates/c.erb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
<% if header? %>
|
2
|
+
// <%= FFIDB.header %>
|
3
|
+
|
4
|
+
<% end %>
|
5
|
+
#include <stdarg.h> // for va_list
|
6
|
+
#include <stdbool.h> // for _Bool
|
7
|
+
#include <stddef.h> // for size_t, wchar_t
|
8
|
+
#include <stdint.h> // for {,u}int*_t
|
9
|
+
#include <sys/types.h> // for off_t, ssize_t
|
10
|
+
<% for library in @libraries %>
|
11
|
+
<% if library %>
|
12
|
+
|
13
|
+
// <%= library.name %> API
|
14
|
+
<% end %>
|
15
|
+
<% for enum in @enums[library] || [] %>
|
16
|
+
|
17
|
+
<% if enum.comment %>
|
18
|
+
// <%= enum.comment %>
|
19
|
+
<% end %>
|
20
|
+
enum <%= enum.name %> {
|
21
|
+
<% for name, value in enum.values || {} %>
|
22
|
+
<%= name %> = <%= value %>,
|
23
|
+
<% end %>
|
24
|
+
};
|
25
|
+
<% end %>
|
26
|
+
<% for struct in @structs[library] || [] %>
|
27
|
+
|
28
|
+
<% if struct.comment %>
|
29
|
+
// <%= struct.comment %>
|
30
|
+
<% end %>
|
31
|
+
struct <%= struct.name %> {
|
32
|
+
<% for name, type in struct.fields || {} %>
|
33
|
+
<%= type %> <%= name %>;
|
34
|
+
<% end %>
|
35
|
+
};
|
36
|
+
<% end %>
|
37
|
+
<% for function in @functions[library] || [] %>
|
38
|
+
|
39
|
+
<% if function.comment %>
|
40
|
+
// <%= function.comment %>
|
41
|
+
<% end %>
|
42
|
+
extern <%= function.type %> <%= function.name %>(<%=
|
43
|
+
function.parameters.each_value.map { |p| "#{p.type} #{p.name}" }.join(', ')
|
44
|
+
%>);
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<% if header? %>
|
2
|
+
// <%= FFIDB.header %>
|
3
|
+
|
4
|
+
<% end %>
|
5
|
+
#include <cstdarg> // for va_list
|
6
|
+
#include <cstdbool> // for bool
|
7
|
+
#include <cstddef> // for size_t
|
8
|
+
#include <cstdint> // for {,u}int*_t
|
9
|
+
#include <sys/types.h> // for off_t, ssize_t
|
10
|
+
<% for library in @libraries %>
|
11
|
+
|
12
|
+
namespace <%= self.options[:module] || library&.name || :lib %> {
|
13
|
+
<% for enum in @enums[library] || [] %>
|
14
|
+
|
15
|
+
<% if enum.comment %>
|
16
|
+
// <%= enum.comment %>
|
17
|
+
<% end %>
|
18
|
+
enum <%= enum.name %> {
|
19
|
+
<% for name, value in enum.values || {} %>
|
20
|
+
<%= name %> = <%= value %>,
|
21
|
+
<% end %>
|
22
|
+
};
|
23
|
+
<% end %>
|
24
|
+
<% for struct in @structs[library] || [] %>
|
25
|
+
|
26
|
+
<% if struct.comment %>
|
27
|
+
// <%= struct.comment %>
|
28
|
+
<% end %>
|
29
|
+
struct <%= struct.name %> {
|
30
|
+
<% for name, type in struct.fields || {} %>
|
31
|
+
<%= type %> <%= name %>;
|
32
|
+
<% end %>
|
33
|
+
};
|
34
|
+
<% end %>
|
35
|
+
<% for function in @functions[library] || [] %>
|
36
|
+
|
37
|
+
<% if function.comment %>
|
38
|
+
// <%= function.comment %>
|
39
|
+
<% end %>
|
40
|
+
extern "C" <%= function.type %> <%= function.name %>(<%=
|
41
|
+
function.parameters.each_value.map { |p| "#{p.type} #{p.name}" }.join(', ')
|
42
|
+
%>);
|
43
|
+
<% end %>
|
44
|
+
} // <%= self.options[:module] || library&.name || :lib %>
|
45
|
+
<% end %>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<% if header? %>
|
2
|
+
// <%= FFIDB.header %>
|
3
|
+
|
4
|
+
<% end %>
|
5
|
+
<% if options[:module] %>
|
6
|
+
library <%= options[:module] %>;
|
7
|
+
|
8
|
+
<% end %>
|
9
|
+
import 'dart:ffi';
|
10
|
+
import 'dart:io' as io;
|
11
|
+
<% for library in @libraries %>
|
12
|
+
<% if library && @functions[library] %>
|
13
|
+
|
14
|
+
final <%= library&.name || :lib %> = DynamicLibrary.open('<%= dlopen_paths_for(library).first %>');
|
15
|
+
<% end %>
|
16
|
+
<% for enum in @enums[library] || [] %>
|
17
|
+
|
18
|
+
<% if enum.comment %>
|
19
|
+
/// <%= enum.comment %>
|
20
|
+
<% end %>
|
21
|
+
abstract class <%= enum.name %> {
|
22
|
+
<%= enum.name %>._();
|
23
|
+
|
24
|
+
<% for name, value in enum.values || {} %>
|
25
|
+
static const <%= name %> = <%= value %>;
|
26
|
+
<% end %>
|
27
|
+
}
|
28
|
+
<% end %>
|
29
|
+
<% for struct in @structs[library] || [] %>
|
30
|
+
|
31
|
+
<% if struct.comment %>
|
32
|
+
/// <%= struct.comment %>
|
33
|
+
<% end %>
|
34
|
+
<% if struct.opaque? %>
|
35
|
+
class <%= struct.name %> extends Struct {
|
36
|
+
Int8 _opaque;
|
37
|
+
<% else %>
|
38
|
+
class <%= struct.name %> extends Struct {
|
39
|
+
<% for (name, type), i in (struct.fields || {}).each_with_index %>
|
40
|
+
<%= i.zero? ? '' : "\n" -%>
|
41
|
+
<% unless type.pointer? %>
|
42
|
+
@<%= ffi_struct_type(type) %>() // <%= type %>
|
43
|
+
<% else %>
|
44
|
+
// <%= type %>
|
45
|
+
<% end %>
|
46
|
+
<%= dart_struct_type(type) %> <%= name %>;
|
47
|
+
<% end %>
|
48
|
+
<% end %>
|
49
|
+
}
|
50
|
+
<% end %>
|
51
|
+
<% for function in @functions[library] || [] %>
|
52
|
+
|
53
|
+
<% if function.comment %>
|
54
|
+
/// <%= function.comment %>
|
55
|
+
<% end %>
|
56
|
+
final <%= dart_param_type(function.type) %> Function(<%=
|
57
|
+
function.parameters.each_value.map { |p| dart_param_type(p.type) }.join(', ')
|
58
|
+
%>) <%= function.name %> = <%= library&.name || :lib %>
|
59
|
+
.lookup<NativeFunction<<%= ffi_param_type(function.type) %> Function(<%=
|
60
|
+
function.parameters.each_value.map { |p| ffi_param_type(p.type) }.join(', ')
|
61
|
+
%>)>>('<%= function.name %>')
|
62
|
+
.asFunction();
|
63
|
+
<% end %>
|
64
|
+
<% end %>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<% if header? %>
|
2
|
+
// <%= FFIDB.header %>
|
3
|
+
|
4
|
+
<% end %>
|
5
|
+
/*
|
6
|
+
#include <stdarg.h> // for va_list
|
7
|
+
#include <stdbool.h> // for _Bool
|
8
|
+
#include <stddef.h> // for size_t, wchar_t
|
9
|
+
#include <stdint.h> // for {,u}int*_t
|
10
|
+
#include <sys/types.h> // for off_t, ssize_t
|
11
|
+
<% for library in @libraries %>
|
12
|
+
<% if library %>
|
13
|
+
|
14
|
+
// <%= library.name %> API
|
15
|
+
#cgo LDFLAGS: -l<%= library.dlopen.first %>
|
16
|
+
<% end %>
|
17
|
+
<% for enum in @enums[library] || [] %>
|
18
|
+
|
19
|
+
<% if enum.comment %>
|
20
|
+
// <%= enum.comment %>
|
21
|
+
<% end %>
|
22
|
+
enum <%= enum.name %> {
|
23
|
+
<% for name, value in enum.values || {} %>
|
24
|
+
<%= name %> = <%= value %>,
|
25
|
+
<% end %>
|
26
|
+
};
|
27
|
+
<% end %>
|
28
|
+
<% for struct in @structs[library] || [] %>
|
29
|
+
|
30
|
+
<% if struct.comment %>
|
31
|
+
// <%= struct.comment %>
|
32
|
+
<% end %>
|
33
|
+
struct <%= struct.name %> {
|
34
|
+
<% for name, type in struct.fields || {} %>
|
35
|
+
<%= type %> <%= name %>;
|
36
|
+
<% end %>
|
37
|
+
};
|
38
|
+
<% end %>
|
39
|
+
<% for function in @functions[library] || [] %>
|
40
|
+
|
41
|
+
<% if function.comment %>
|
42
|
+
// <%= function.comment %>
|
43
|
+
<% end %>
|
44
|
+
extern <%= function.type %> <%= function.name %>(<%=
|
45
|
+
function.parameters.each_value.map { |p| "#{p.type} #{p.name}" }.join(', ')
|
46
|
+
%>);
|
47
|
+
<% end %>
|
48
|
+
<% end %>
|
49
|
+
*/
|
50
|
+
import "C"
|
@@ -0,0 +1,56 @@
|
|
1
|
+
<% if header? %>
|
2
|
+
// <%= FFIDB.header %>
|
3
|
+
|
4
|
+
<% end %>
|
5
|
+
import com.sun.jna.Library;
|
6
|
+
import com.sun.jna.Native;
|
7
|
+
import com.sun.jna.NativeLong;
|
8
|
+
import com.sun.jna.Pointer;
|
9
|
+
import com.sun.jna.Structure.FFIType.size_t;
|
10
|
+
import com.sun.jna.platform.linux.XAttr.ssize_t;
|
11
|
+
<% for library in @libraries %>
|
12
|
+
|
13
|
+
public interface <%= library&.interface_name || :FFI %> extends Library {
|
14
|
+
<% if library && @functions[library] %>
|
15
|
+
<%= library&.interface_name || :FFI %> INSTANCE = (<%= library&.interface_name || :FFI %>)Native.load("<%= dlopen_paths_for(library).first %>", <%= library&.interface_name || :FFI %>.class);
|
16
|
+
<% end %>
|
17
|
+
<% for enum in @enums[library] || [] %>
|
18
|
+
|
19
|
+
<% if enum.comment %>
|
20
|
+
// <%= enum.comment %>
|
21
|
+
<% end %>
|
22
|
+
public static class <%= enum.name %> {
|
23
|
+
<% for name, value in enum.values || {} %>
|
24
|
+
public static final int <%= name %> = <%= value %>;
|
25
|
+
<% end %>
|
26
|
+
}
|
27
|
+
<% end %>
|
28
|
+
<% for struct in @structs[library] || [] %>
|
29
|
+
|
30
|
+
<% if struct.comment %>
|
31
|
+
// <%= struct.comment %>
|
32
|
+
<% end %>
|
33
|
+
<% if struct.opaque? %>
|
34
|
+
@FieldOrder({ "_opaque" })
|
35
|
+
public static class <%= struct.name %> extends Structure {
|
36
|
+
public byte _opaque;
|
37
|
+
<% else %>
|
38
|
+
@FieldOrder({ <%= (struct.fields || {}).keys.map(&:to_s).map(&:inspect).join(', ') %> })
|
39
|
+
public static class <%= struct.name %> extends Structure {
|
40
|
+
<% for name, type in struct.fields || {} %>
|
41
|
+
public <%= struct_type(type) %> <%= name %>; // <%= type %>
|
42
|
+
<% end %>
|
43
|
+
<% end %>
|
44
|
+
}
|
45
|
+
<% end %>
|
46
|
+
<% for function in @functions[library] || [] %>
|
47
|
+
|
48
|
+
<% if function.comment %>
|
49
|
+
// <%= function.comment %>
|
50
|
+
<% end %>
|
51
|
+
<%= param_type(function.type) %> <%= function.name %>(<%=
|
52
|
+
function.parameters.each_value.map { |p| [param_type(p.type), p.name].join(' ') }.join(', ')
|
53
|
+
%>);
|
54
|
+
<% end %>
|
55
|
+
} // <%= library&.interface_name || :FFI %>
|
56
|
+
<% end %>
|