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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +1 -0
  3. data/CHANGES.md +7 -0
  4. data/CREDITS.md +2 -0
  5. data/README.md +201 -0
  6. data/UNLICENSE +24 -0
  7. data/VERSION +1 -0
  8. data/bin/ffidb +387 -0
  9. data/etc/mappings/dart.yaml +35 -0
  10. data/etc/mappings/java.yaml +36 -0
  11. data/etc/mappings/lisp.yaml +35 -0
  12. data/etc/mappings/python.yaml +35 -0
  13. data/etc/mappings/ruby.yaml +35 -0
  14. data/etc/templates/c.erb +46 -0
  15. data/etc/templates/cpp.erb +45 -0
  16. data/etc/templates/dart.erb +64 -0
  17. data/etc/templates/go.erb +50 -0
  18. data/etc/templates/java.erb +56 -0
  19. data/etc/templates/lisp.erb +49 -0
  20. data/etc/templates/python.erb +59 -0
  21. data/etc/templates/ruby.erb +48 -0
  22. data/lib/ffidb.rb +34 -0
  23. data/lib/ffidb/enum.rb +37 -0
  24. data/lib/ffidb/errors.rb +64 -0
  25. data/lib/ffidb/exporter.rb +141 -0
  26. data/lib/ffidb/exporters.rb +28 -0
  27. data/lib/ffidb/exporters/c.rb +52 -0
  28. data/lib/ffidb/exporters/cpp.rb +13 -0
  29. data/lib/ffidb/exporters/csharp.rb +6 -0
  30. data/lib/ffidb/exporters/csv.rb +24 -0
  31. data/lib/ffidb/exporters/dart.rb +60 -0
  32. data/lib/ffidb/exporters/go.rb +16 -0
  33. data/lib/ffidb/exporters/haskell.rb +3 -0
  34. data/lib/ffidb/exporters/java.rb +39 -0
  35. data/lib/ffidb/exporters/json.rb +38 -0
  36. data/lib/ffidb/exporters/julia.rb +3 -0
  37. data/lib/ffidb/exporters/lisp.rb +41 -0
  38. data/lib/ffidb/exporters/luajit.rb +3 -0
  39. data/lib/ffidb/exporters/nim.rb +4 -0
  40. data/lib/ffidb/exporters/nodejs.rb +4 -0
  41. data/lib/ffidb/exporters/ocaml.rb +4 -0
  42. data/lib/ffidb/exporters/php.rb +4 -0
  43. data/lib/ffidb/exporters/python.rb +35 -0
  44. data/lib/ffidb/exporters/racket.rb +3 -0
  45. data/lib/ffidb/exporters/ruby.rb +33 -0
  46. data/lib/ffidb/exporters/rust.rb +5 -0
  47. data/lib/ffidb/exporters/yaml.rb +31 -0
  48. data/lib/ffidb/exporters/zig.rb +3 -0
  49. data/lib/ffidb/function.rb +70 -0
  50. data/lib/ffidb/glob.rb +28 -0
  51. data/lib/ffidb/header.rb +19 -0
  52. data/lib/ffidb/header_parser.rb +339 -0
  53. data/lib/ffidb/library.rb +120 -0
  54. data/lib/ffidb/library_parser.rb +132 -0
  55. data/lib/ffidb/location.rb +17 -0
  56. data/lib/ffidb/parameter.rb +35 -0
  57. data/lib/ffidb/registry.rb +87 -0
  58. data/lib/ffidb/release.rb +14 -0
  59. data/lib/ffidb/struct.rb +41 -0
  60. data/lib/ffidb/symbol_table.rb +90 -0
  61. data/lib/ffidb/symbolic.rb +67 -0
  62. data/lib/ffidb/sysexits.rb +21 -0
  63. data/lib/ffidb/type.rb +214 -0
  64. data/lib/ffidb/typedef.rb +38 -0
  65. data/lib/ffidb/union.rb +37 -0
  66. data/lib/ffidb/version.rb +21 -0
  67. 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
@@ -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