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,120 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
require_relative 'library_parser'
|
4
|
+
require_relative 'release'
|
5
|
+
require_relative 'symbol_table'
|
6
|
+
|
7
|
+
require 'pathname'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
module FFIDB
|
11
|
+
class Library
|
12
|
+
include SymbolTable
|
13
|
+
include Comparable
|
14
|
+
|
15
|
+
attr_reader :name
|
16
|
+
attr_reader :version
|
17
|
+
attr_reader :path
|
18
|
+
|
19
|
+
attr_reader :summary
|
20
|
+
attr_reader :website
|
21
|
+
attr_reader :source
|
22
|
+
attr_reader :packages
|
23
|
+
attr_reader :dlopen
|
24
|
+
attr_reader :objects
|
25
|
+
attr_reader :headers
|
26
|
+
|
27
|
+
##
|
28
|
+
# @param [Library] other
|
29
|
+
# @return [Integer]
|
30
|
+
def <=>(other) self.name <=> other&.name end # FIXME
|
31
|
+
|
32
|
+
##
|
33
|
+
# @param [String, #to_s] name
|
34
|
+
# @param [String, #to_s] version
|
35
|
+
# @param [Pathname, #to_s] path
|
36
|
+
def initialize(name, version, path)
|
37
|
+
@name, @version = name.to_s.freeze, (version || :stable).to_s.freeze
|
38
|
+
@path = Pathname(path).freeze
|
39
|
+
if (metadata_path = @path.join('library.yaml')).exist?
|
40
|
+
metadata = YAML.load(metadata_path.read).transform_keys(&:to_sym)
|
41
|
+
metadata.delete(:name)
|
42
|
+
@summary = metadata.delete(:summary).freeze
|
43
|
+
@website = metadata.delete(:website).freeze
|
44
|
+
@source = metadata.delete(:source).freeze
|
45
|
+
@packages = metadata.delete(:packages).transform_keys(&:to_sym).freeze
|
46
|
+
dlopen = metadata.delete(:dlopen).freeze
|
47
|
+
@dlopen = dlopen.is_a?(Array) ? dlopen : [dlopen]
|
48
|
+
@objects = (metadata.delete(:objects) || []).freeze
|
49
|
+
@headers = (metadata.delete(:headers) || []).freeze
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# @return [String]
|
55
|
+
def soname() self.objects&.first end
|
56
|
+
|
57
|
+
##
|
58
|
+
# @yield [release]
|
59
|
+
# @yieldparam [release] Release
|
60
|
+
# @return [Enumerator]
|
61
|
+
def each_release(&block)
|
62
|
+
return self.to_enum(:each_release) unless block_given?
|
63
|
+
# TODO
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# @yield [typedef]
|
68
|
+
# @yieldparam [symbol] Symbolic
|
69
|
+
# @return [Enumerator]
|
70
|
+
def each_typedef(&block)
|
71
|
+
return self.to_enum(:each_typedef) unless block_given?
|
72
|
+
self.each_symbol.filter { |symbol| symbol.typedef? }.each(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# @yield [enum]
|
77
|
+
# @yieldparam [enum] Enum
|
78
|
+
# @return [Enumerator]
|
79
|
+
def each_enum(&block)
|
80
|
+
return self.to_enum(:each_enum) unless block_given?
|
81
|
+
self.each_symbol.filter { |symbol| symbol.enum? }.each(&block)
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# @yield [struct]
|
86
|
+
# @yieldparam [struct] Struct
|
87
|
+
# @return [Enumerator]
|
88
|
+
def each_struct(&block)
|
89
|
+
return self.to_enum(:each_struct) unless block_given?
|
90
|
+
self.each_symbol.filter { |symbol| symbol.struct? }.each(&block)
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# @yield [union]
|
95
|
+
# @yieldparam [union] Union
|
96
|
+
# @return [Enumerator]
|
97
|
+
def each_union(&block)
|
98
|
+
return self.to_enum(:each_union) unless block_given?
|
99
|
+
self.each_symbol.filter { |symbol| symbol.union? }.each(&block)
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# @yield [function]
|
104
|
+
# @yieldparam [function] Function
|
105
|
+
# @return [Enumerator]
|
106
|
+
def each_function(&block)
|
107
|
+
return self.to_enum(:each_function) unless block_given?
|
108
|
+
self.each_symbol.filter { |symbol| symbol.function? }.each(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# @yield [symbol]
|
113
|
+
# @yieldparam [symbol] Symbolic
|
114
|
+
# @return [Enumerator]
|
115
|
+
def each_symbol(&block)
|
116
|
+
return self.to_enum(:each_symbol) unless block_given?
|
117
|
+
LibraryParser.new(self.path.join(self.version)).each_symbol(&block)
|
118
|
+
end
|
119
|
+
end # Library
|
120
|
+
end # FFIDB
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
require_relative 'symbol_table'
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
require 'psych'
|
7
|
+
|
8
|
+
module FFIDB
|
9
|
+
class LibraryParser
|
10
|
+
include SymbolTable
|
11
|
+
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
##
|
15
|
+
# @param [Pathname, #to_s] path
|
16
|
+
def initialize(path)
|
17
|
+
@path = Pathname(path).freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# @yield [typedef]
|
22
|
+
# @yieldparam [symbol] Symbolic
|
23
|
+
# @return [Enumerator]
|
24
|
+
def each_typedef(&block)
|
25
|
+
return self.to_enum(:each_typedef) unless block_given?
|
26
|
+
self.parse_yaml_dir(self.path, [:typedef], &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# @yield [enum]
|
31
|
+
# @yieldparam [enum] Enum
|
32
|
+
# @return [Enumerator]
|
33
|
+
def each_enum(&block)
|
34
|
+
return self.to_enum(:each_enum) unless block_given?
|
35
|
+
self.parse_yaml_dir(self.path, [:enum], &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# @yield [struct]
|
40
|
+
# @yieldparam [struct] Struct
|
41
|
+
# @return [Enumerator]
|
42
|
+
def each_struct(&block)
|
43
|
+
return self.to_enum(:each_struct) unless block_given?
|
44
|
+
self.parse_yaml_dir(self.path, [:struct], &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# @yield [union]
|
49
|
+
# @yieldparam [union] Union
|
50
|
+
# @return [Enumerator]
|
51
|
+
def each_union(&block)
|
52
|
+
return self.to_enum(:each_union) unless block_given?
|
53
|
+
self.parse_yaml_dir(self.path, [:union], &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @yield [function]
|
58
|
+
# @yieldparam [function] Function
|
59
|
+
# @return [Enumerator]
|
60
|
+
def each_function(&block)
|
61
|
+
return self.to_enum(:each_function) unless block_given?
|
62
|
+
self.parse_yaml_dir(self.path, [:function], &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# @yield [symbol]
|
67
|
+
# @yieldparam [symbol] Symbolic
|
68
|
+
# @return [Enumerator]
|
69
|
+
def each_symbol(&block)
|
70
|
+
return self.to_enum(:each_symbol) unless block_given?
|
71
|
+
self.parse_yaml_dir(self.path, nil, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# @param [Pathname] path
|
76
|
+
# @param [Array<Symbol>] kind_filter
|
77
|
+
# @yield [symbol]
|
78
|
+
# @yieldparam [symbol] Symbolic
|
79
|
+
# @return [void]
|
80
|
+
def parse_yaml_dir(path, kind_filter = nil, &block)
|
81
|
+
self.path.glob('*.yaml') do |path|
|
82
|
+
path.open do |file|
|
83
|
+
self.parse_yaml_file(file, kind_filter, &block)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# @param [IO] file
|
90
|
+
# @param [Array<Symbol>] kind_filter
|
91
|
+
# @yield [symbol]
|
92
|
+
# @yieldparam [symbol] Symbolic
|
93
|
+
# @return [void]
|
94
|
+
def parse_yaml_file(file, kind_filter = nil, &block)
|
95
|
+
Psych.parse_stream(file) do |yaml_doc|
|
96
|
+
kind = yaml_doc.children.first.tag[1..-1].to_sym
|
97
|
+
next if kind_filter && !kind_filter.include?(kind)
|
98
|
+
yaml = yaml_doc.to_ruby.transform_keys!(&:to_sym)
|
99
|
+
case kind
|
100
|
+
when :typedef
|
101
|
+
yield Typedef.new(yaml[:name], Type.for(yaml[:type]), yaml[:comment])
|
102
|
+
when :enum
|
103
|
+
yield Enum.new(yaml[:name], yaml[:values] || {}, yaml[:comment])
|
104
|
+
when :struct
|
105
|
+
fields = (yaml[:fields] || {}).inject({}) do |fs, (k, v)|
|
106
|
+
fs[k.to_sym] = Type.for(v)
|
107
|
+
fs
|
108
|
+
end
|
109
|
+
yield Struct.new(yaml[:name], fields, yaml[:comment])
|
110
|
+
when :union
|
111
|
+
yield Union.new(yaml[:name], yaml[:fields] || {}, yaml[:comment])
|
112
|
+
when :function
|
113
|
+
parameters = (yaml[:parameters] || {}).inject({}) do |ps, (k, v)|
|
114
|
+
k = k.to_sym
|
115
|
+
ps[k] = Parameter.new(k, Type.for(v))
|
116
|
+
ps
|
117
|
+
end
|
118
|
+
yield Function.new(
|
119
|
+
name: yaml[:name],
|
120
|
+
type: Type.for(yaml[:type]),
|
121
|
+
parameters: parameters,
|
122
|
+
definition: !yaml.has_key?(:definition) ? nil : Location.new(
|
123
|
+
file: yaml.dig(:definition, 'file'),
|
124
|
+
line: yaml.dig(:definition, 'line'),
|
125
|
+
),
|
126
|
+
comment: yaml[:comment],
|
127
|
+
)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end # LibraryParser
|
132
|
+
end # FFIDB
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
module FFIDB
|
4
|
+
class Location < ::Struct.new(:file, :line, keyword_init: true)
|
5
|
+
##
|
6
|
+
# @return [String]
|
7
|
+
def to_s
|
8
|
+
"#{self.file}:#{self.line}"
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# @return [Hash<Symbol, Object>]
|
13
|
+
def to_h
|
14
|
+
{file: self.file, line: self.line}
|
15
|
+
end
|
16
|
+
end # Location
|
17
|
+
end # FFIDB
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
require_relative 'type'
|
4
|
+
|
5
|
+
module FFIDB
|
6
|
+
class Parameter < ::Struct.new(:name, :type)
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
##
|
10
|
+
# @param [Symbol, #to_sym] name
|
11
|
+
# @param [Type] type
|
12
|
+
def initialize(name, type = nil)
|
13
|
+
super(name.to_sym, type ? Type.for(type) : nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# @param [Parameter] other
|
18
|
+
# @return [Integer]
|
19
|
+
def <=>(other)
|
20
|
+
self.name <=> other.name
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# @return [String]
|
25
|
+
def to_s
|
26
|
+
"#{self.name}: #{self.type}"
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# @return [Hash<Symbol, Type>]
|
31
|
+
def to_h
|
32
|
+
{self.name => self.type}
|
33
|
+
end
|
34
|
+
end # Parameter
|
35
|
+
end # FFIDB
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
require_relative 'errors'
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module FFIDB
|
8
|
+
class Registry
|
9
|
+
GIT_HTTPS_URL = 'https://github.com/ffidb/ffidb.git'.freeze
|
10
|
+
|
11
|
+
attr_reader :path
|
12
|
+
|
13
|
+
##
|
14
|
+
# @return [Pathname]
|
15
|
+
def self.default_path
|
16
|
+
Pathname(ENV['HOME']).join('.ffidb')
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# @param [Pathname, #to_s] path
|
21
|
+
def self.open(path = nil, &block)
|
22
|
+
registry = self.new(path)
|
23
|
+
block_given? ? block.call(registry) : registry
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# @param [Pathname, #to_s] path
|
28
|
+
# @raise [RegistryVersionMismatch] if this version of FFIDB.rb is unable to open the registry
|
29
|
+
def initialize(path = nil)
|
30
|
+
@path = Pathname(path || self.class.default_path)
|
31
|
+
|
32
|
+
if (version_file = @path.join('.cli-version')).exist?
|
33
|
+
min_version = version_file.read.chomp.split('.').map(&:to_i)
|
34
|
+
if (FFIDB::VERSION.to_a <=> min_version).negative?
|
35
|
+
raise RegistryVersionMismatch, "FFIDB.rb #{min_version.join('.')}+ is required for the registry directory #{@path}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# @yield [library]
|
42
|
+
# @return [Enumerator]
|
43
|
+
def each_library(&block)
|
44
|
+
return self.to_enum(:each_library) unless block_given?
|
45
|
+
library_names = self.path.glob('*')
|
46
|
+
.select { |path| path.directory? && !(path.symlink?) }
|
47
|
+
.map { |path| path.basename.to_s }
|
48
|
+
.sort
|
49
|
+
library_names.each do |library_name|
|
50
|
+
yield self.open_library(library_name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# @param [String, #to_s] library_name
|
56
|
+
# @param [String, #to_s] library_version
|
57
|
+
# @yield [library]
|
58
|
+
# @return [Library]
|
59
|
+
def open_library(library_name, library_version = nil, &block)
|
60
|
+
library_path = self.path.join(library_name.to_s)
|
61
|
+
return nil unless library_path.directory?
|
62
|
+
library = Library.new(library_name, library_version, library_path)
|
63
|
+
block_given? ? block.call(library) : library
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# @param [Glob, #===] matcher
|
68
|
+
# @param [Symbol] kind
|
69
|
+
# @yield [function]
|
70
|
+
# @yield [library]
|
71
|
+
# @return [Enumerator]
|
72
|
+
def find_symbols(matcher, kind: nil, &block)
|
73
|
+
return self.to_enum(:find_symbols) unless block_given?
|
74
|
+
count = 0
|
75
|
+
self.each_library do |library|
|
76
|
+
library.each_symbol do |symbol|
|
77
|
+
next if kind && kind != symbol.kind
|
78
|
+
if matcher === symbol.name.to_s
|
79
|
+
count += 1
|
80
|
+
yield symbol, library
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
count > 0 ? count : nil
|
85
|
+
end
|
86
|
+
end # Registry
|
87
|
+
end # FFIDB
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
module FFIDB
|
4
|
+
class Release < ::Struct.new(:version, :headers)
|
5
|
+
##
|
6
|
+
# @yield [header]
|
7
|
+
# @yieldparam [header] Header
|
8
|
+
# @return [Enumerator]
|
9
|
+
def each_header(&block)
|
10
|
+
return self.to_enum(:each_header) unless block_given?
|
11
|
+
self.headers(&block) if self.headers
|
12
|
+
end
|
13
|
+
end # Release
|
14
|
+
end # FFIDB
|
data/lib/ffidb/struct.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
require_relative 'symbolic'
|
4
|
+
|
5
|
+
module FFIDB
|
6
|
+
class Struct < ::Struct.new(:name, :fields, :comment)
|
7
|
+
include Symbolic
|
8
|
+
|
9
|
+
##
|
10
|
+
# @param [Symbol, #to_sym] name
|
11
|
+
# @param [Map<Symbol, Type>] fields
|
12
|
+
# @param [String, #to_s] comment
|
13
|
+
def initialize(name, fields = {}, comment = nil)
|
14
|
+
super(name.to_sym, fields || {}, comment&.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# @return [Boolean]
|
19
|
+
def struct?() return true end
|
20
|
+
|
21
|
+
##
|
22
|
+
# @return [Boolean]
|
23
|
+
def opaque?() !self.fields || self.fields.empty? end
|
24
|
+
|
25
|
+
##
|
26
|
+
# @return [String]
|
27
|
+
def to_s
|
28
|
+
"struct #{self.name}"
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# @return [Hash<Symbol, Type>]
|
33
|
+
def to_h
|
34
|
+
{
|
35
|
+
name: self.name.to_s,
|
36
|
+
comment: self.comment,
|
37
|
+
fields: self.opaque? ? nil : self.fields&.transform_values { |t| t.to_s },
|
38
|
+
}.delete_if { |k, v| v.nil? }
|
39
|
+
end
|
40
|
+
end # Struct
|
41
|
+
end # FFIDB
|