ffidb 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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