ffidb 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|