crystalruby 0.2.3 → 0.3.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/README.md +389 -193
  4. data/Rakefile +4 -3
  5. data/crystalruby.gemspec +2 -2
  6. data/exe/crystalruby +1 -0
  7. data/lib/crystalruby/adapter.rb +131 -65
  8. data/lib/crystalruby/arc_mutex.rb +47 -0
  9. data/lib/crystalruby/compilation.rb +32 -3
  10. data/lib/crystalruby/config.rb +41 -37
  11. data/lib/crystalruby/function.rb +211 -68
  12. data/lib/crystalruby/library.rb +153 -48
  13. data/lib/crystalruby/reactor.rb +40 -23
  14. data/lib/crystalruby/source_reader.rb +86 -0
  15. data/lib/crystalruby/template.rb +16 -5
  16. data/lib/crystalruby/templates/function.cr +11 -10
  17. data/lib/crystalruby/templates/index.cr +53 -66
  18. data/lib/crystalruby/templates/inline_chunk.cr +1 -1
  19. data/lib/crystalruby/templates/ruby_interface.cr +34 -0
  20. data/lib/crystalruby/templates/top_level_function.cr +62 -0
  21. data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
  22. data/lib/crystalruby/typebuilder.rb +11 -55
  23. data/lib/crystalruby/typemaps.rb +92 -67
  24. data/lib/crystalruby/types/concerns/allocator.rb +80 -0
  25. data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
  26. data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
  27. data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
  28. data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
  29. data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
  30. data/lib/crystalruby/types/fixed_width/tagged_union.rb +109 -0
  31. data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
  32. data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
  33. data/lib/crystalruby/types/fixed_width.cr +138 -0
  34. data/lib/crystalruby/types/fixed_width.rb +205 -0
  35. data/lib/crystalruby/types/primitive.cr +21 -0
  36. data/lib/crystalruby/types/primitive.rb +117 -0
  37. data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
  38. data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
  39. data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
  40. data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
  41. data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
  42. data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
  43. data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
  44. data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
  45. data/lib/crystalruby/types/primitive_types/time.cr +35 -0
  46. data/lib/crystalruby/types/primitive_types/time.rb +25 -0
  47. data/lib/crystalruby/types/type.cr +64 -0
  48. data/lib/crystalruby/types/type.rb +239 -30
  49. data/lib/crystalruby/types/variable_width/array.cr +74 -0
  50. data/lib/crystalruby/types/variable_width/array.rb +88 -0
  51. data/lib/crystalruby/types/variable_width/hash.cr +146 -0
  52. data/lib/crystalruby/types/variable_width/hash.rb +117 -0
  53. data/lib/crystalruby/types/variable_width/string.cr +36 -0
  54. data/lib/crystalruby/types/variable_width/string.rb +18 -0
  55. data/lib/crystalruby/types/variable_width.cr +23 -0
  56. data/lib/crystalruby/types/variable_width.rb +46 -0
  57. data/lib/crystalruby/types.rb +32 -13
  58. data/lib/crystalruby/version.rb +2 -2
  59. data/lib/crystalruby.rb +13 -6
  60. metadata +41 -19
  61. data/lib/crystalruby/types/array.rb +0 -15
  62. data/lib/crystalruby/types/bool.rb +0 -3
  63. data/lib/crystalruby/types/hash.rb +0 -17
  64. data/lib/crystalruby/types/named_tuple.rb +0 -28
  65. data/lib/crystalruby/types/nil.rb +0 -3
  66. data/lib/crystalruby/types/numbers.rb +0 -5
  67. data/lib/crystalruby/types/string.rb +0 -3
  68. data/lib/crystalruby/types/symbol.rb +0 -3
  69. data/lib/crystalruby/types/time.rb +0 -8
  70. data/lib/crystalruby/types/tuple.rb +0 -17
  71. data/lib/crystalruby/types/type_serializer/json.rb +0 -41
  72. data/lib/crystalruby/types/type_serializer.rb +0 -37
  73. data/lib/crystalruby/types/typedef.rb +0 -57
  74. data/lib/crystalruby/types/union_type.rb +0 -43
  75. data/lib/module.rb +0 -3
@@ -0,0 +1,117 @@
1
+ module CrystalRuby
2
+ module Types
3
+ class Primitive < Type
4
+ # Primitives just store the Ruby value directly
5
+ # (Or read it from memory if passed a pointer)
6
+ def initialize(rbval)
7
+ super(rbval)
8
+ self.value = rbval.is_a?(FFI::Pointer) ? rbval.send("read_#{ffi_type}") : rbval
9
+ end
10
+
11
+ # Read a value from a pointer at a given index
12
+ # (Type can be a byte-array, pointer or numeric type)
13
+ def self.fetch_single(pointer, native: false)
14
+ # Nothing to fetch for Nils
15
+ return if memsize.zero?
16
+
17
+ if numeric?
18
+ pointer.send("read_#{ffi_type}")
19
+ elsif primitive?
20
+ single = new(pointer.send("read_#{ffi_type}"))
21
+ if native
22
+ single.value
23
+ else
24
+ single
25
+ end
26
+ end
27
+ end
28
+
29
+ # Write a data type into a pointer at a given index
30
+ # (Type can be a byte-array, pointer or numeric type)
31
+ def self.write_single(pointer, value)
32
+ # Dont need to write nils
33
+ return if memsize.zero?
34
+
35
+ pointer.send("write_#{ffi_type}", to_ffi_repr(value))
36
+ end
37
+
38
+ # Fetch an array of a given data type from a list pointer
39
+ # (Type can be a byte-array, pointer or numeric type)
40
+ def self.fetch_multi(pointer, size, native: false)
41
+ if numeric?
42
+ pointer.send("get_array_of_#{ffi_type}", 0, size)
43
+ elsif primitive?
44
+ pointer.send("get_array_of_#{ffi_type}", 0, size).map(&method(:from_ffi_array_repr))
45
+ end
46
+ end
47
+
48
+ def self.decrement_ref_count!(pointer)
49
+ # Do nothing
50
+ end
51
+
52
+ # Define a new primitive type
53
+ # Primitive types are stored by value
54
+ # and efficiently copied using native FFI types
55
+ # They are written directly into the memory of a container type
56
+ # (No indirection)
57
+ def self.build(
58
+ typename = nil,
59
+ ffi_type: :uint8,
60
+ memsize: FFI.type_size(ffi_type),
61
+ convert_if: [],
62
+ error: nil,
63
+ superclass: Primitive,
64
+ &block
65
+ )
66
+ Class.new(superclass) do
67
+ %w[typename ffi_type memsize convert_if error].each do |name|
68
+ define_singleton_method(name) { binding.local_variable_get("#{name}") }
69
+ define_method(name) { binding.local_variable_get("#{name}") }
70
+ end
71
+
72
+ class_eval(&block) if block_given?
73
+
74
+ # Primitives are stored directly in memory as a raw numeric value
75
+ def self.to_ffi_repr(value)
76
+ new(value).inner_value
77
+ end
78
+
79
+ def self.refsize
80
+ memsize
81
+ end
82
+
83
+ # Primiives are anonymous (Shouldn't be subclassed)
84
+ def self.anonymous?
85
+ true
86
+ end
87
+
88
+ def self.copy_to!(rbval, memory:)
89
+ memory.send("write_#{self.ffi_type}", to_ffi_repr(rbval))
90
+ end
91
+
92
+ def self.primitive?
93
+ true
94
+ end
95
+
96
+ def self.inspect
97
+ inspect_name
98
+ end
99
+
100
+ def memory
101
+ @value
102
+ end
103
+
104
+ def self.crystal_supertype
105
+ "CrystalRuby::Types::Primitive"
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ require_relative "primitive_types/time"
114
+ require_relative "primitive_types/symbol"
115
+ require_relative "primitive_types/numbers"
116
+ require_relative "primitive_types/nil"
117
+ require_relative "primitive_types/bool"
@@ -0,0 +1,34 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(value : ::Bool)
4
+ @value = value ? 1_u8 : 0_u8
5
+ end
6
+
7
+ def initialize(ptr : Pointer(::UInt8))
8
+ @value = ptr[0]
9
+ end
10
+
11
+ def initialize(value : UInt8)
12
+ @value = value
13
+ end
14
+
15
+ def value=(value : ::Bool)
16
+ @value = value ? 1_u8 : 0_u8
17
+ end
18
+
19
+ def value : <%= native_type_expr %>
20
+ @value == 1_u8
21
+ end
22
+
23
+ def ==(other : ::Bool)
24
+ value == other
25
+ end
26
+
27
+ def self.memsize
28
+ <%= memsize %>
29
+ end
30
+
31
+ def self.write_single(pointer : Pointer(::UInt8), value)
32
+ pointer.as(Pointer(::UInt8)).value = value ? 1_u8 : 0_u8
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ module CrystalRuby::Types
2
+ Bool = Primitive.build(:Bool, convert_if: [::TrueClass, ::FalseClass], ffi_type: :uint8, memsize: 1) do
3
+ def value(native: false)
4
+ super == 1
5
+ end
6
+
7
+ def value=(val)
8
+ !!val && val != 0 ? super(1) : super(0)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ property value : ::Nil = nil
4
+
5
+ def initialize(nilval : ::Nil)
6
+ end
7
+
8
+ def initialize(ptr : Pointer(::UInt8))
9
+ end
10
+
11
+ def initialize(raw : UInt8)
12
+ end
13
+
14
+ def value : ::Nil
15
+ nil
16
+ end
17
+
18
+ def ==(other : ::Nil)
19
+ value.nil?
20
+ end
21
+
22
+ def value=(val : ::Nil)
23
+ end
24
+
25
+ def self.memsize
26
+ 0
27
+ end
28
+
29
+ def return_value
30
+ 0_u8
31
+ end
32
+
33
+ def self.write_single(pointer : Pointer(::UInt8), value)
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ module CrystalRuby::Types
2
+ Nil = Primitive.build(:Nil, convert_if: [::NilClass], memsize: 0) do
3
+ def initialize(val = nil)
4
+ super
5
+ @value = 0
6
+ end
7
+
8
+ def nil?
9
+ true
10
+ end
11
+
12
+ def value(native: false)
13
+ nil
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(value : <%= native_type_expr %>)
4
+ @value = value
5
+ end
6
+
7
+ def initialize(ptr : Pointer(::UInt8))
8
+ @value = ptr.as(Pointer( <%= native_type_expr %>))[0]
9
+ end
10
+
11
+ def value
12
+ @value
13
+ end
14
+
15
+ def ==(other : <%= native_type_expr %>)
16
+ value == other
17
+ end
18
+
19
+ def self.memsize
20
+ <%= memsize %>
21
+ end
22
+
23
+ def value=(value : <%= native_type_expr %>)
24
+ @value = value
25
+ end
26
+
27
+ def self.copy_to!(value : <%= native_type_expr %>, ptr : Pointer(::UInt8))
28
+ ptr.as(Pointer( <%= native_type_expr %>))[0] = value
29
+ end
30
+
31
+ # Write a data type into a pointer at a given index
32
+ # (Type can be a byte-array, pointer or numeric type)
33
+ def self.write_single(pointer : Pointer(::UInt8), value)
34
+ pointer.as(Pointer( <%= native_type_expr %>)).value = value
35
+ end
36
+
37
+ end
@@ -0,0 +1,28 @@
1
+ module CrystalRuby::Types
2
+ %i[UInt8 UInt16 UInt32 UInt64 Int8 Int16 Int32 Int64 Float32 Float64].each do |type_name|
3
+ ffi_type = CrystalRuby::Typemaps::FFI_TYPE_MAP.fetch(type_name.to_s)
4
+ const_set(type_name, Primitive.build(type_name, convert_if: [::Numeric], ffi_type: ffi_type) do
5
+ def value=(val)
6
+ raise "Expected a numeric value, got #{val}" unless val.is_a?(::Numeric)
7
+
8
+ super(typename.to_s.start_with?("Float") ? val.to_f : val.to_i)
9
+ end
10
+
11
+ def value(native: false)
12
+ @value
13
+ end
14
+
15
+ def self.from_ffi_array_repr(value)
16
+ value
17
+ end
18
+
19
+ def self.numeric?
20
+ true
21
+ end
22
+
23
+ def self.template_name
24
+ "Numbers"
25
+ end
26
+ end)
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(value : ::Symbol)
4
+ @value = 0.to_u32
5
+ self.value = value
6
+ end
7
+
8
+ def initialize(value : UInt32)
9
+ @value = value
10
+ end
11
+
12
+ def initialize(ptr : Pointer(::UInt8))
13
+ initialize(ptr.as(Pointer(::UInt32))[0])
14
+ end
15
+
16
+ def ==(other : ::Symbol)
17
+ value == other
18
+ end
19
+
20
+ def value=(value : ::Symbol)
21
+ case value
22
+ <% allowed_values.each_with_index do |v, i| %>
23
+ when :<%= v %> then @value = <%= i %>.to_u32
24
+ <% end %>
25
+ else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
26
+ end
27
+ end
28
+
29
+ def value : ::Symbol
30
+ case @value
31
+ <% allowed_values.each_with_index do |v, i| %>
32
+ when <%= i %> then :<%= v %>
33
+ <% end %>
34
+ else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
35
+ end
36
+ end
37
+
38
+ def self.copy_to!(value : ::Symbol, ptr : Pointer(::UInt8))
39
+ ptr.as(Pointer(::UInt32))[0] = new(value).return_value
40
+ end
41
+
42
+ def self.memsize
43
+ <%= memsize %>
44
+ end
45
+
46
+ def self.write_single(pointer : Pointer(::UInt8), value)
47
+ as_uint32 = case value
48
+ <% allowed_values.each_with_index do |v, i| %>
49
+ when :<%= v %> then <%= i %>.to_u32
50
+ <% end %>
51
+ else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
52
+ end
53
+ pointer.as(Pointer(::UInt32)).value = as_uint32
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ module CrystalRuby::Types
2
+ Symbol = Primitive.build(
3
+ error: "Symbol CrystalRuby types should indicate a list of possible values shared between Crystal and Ruby. "\
4
+ "E.g. Symbol(:green, :blue, :orange). If this list is not known at compile time, you should use a String instead."
5
+ )
6
+
7
+ def self.Symbol(*allowed_values)
8
+ raise "Symbol must have at least one value" if allowed_values.empty?
9
+
10
+ allowed_values.flatten!
11
+ raise "Symbol allowed values must all be symbols" unless allowed_values.all? { |v| v.is_a?(::Symbol) }
12
+
13
+ Primitive.build(:Symbol, ffi_type: :uint32, convert_if: [Root::String, Root::Symbol], memsize: 4) do
14
+ bind_local_vars!(%i[allowed_values], binding)
15
+ define_method(:value=) do |val|
16
+ val = allowed_values[val] if val.is_a?(::Integer) && val >= 0 && val < allowed_values.size
17
+ raise "Symbol must be one of #{allowed_values}" unless allowed_values.include?(val)
18
+
19
+ super(allowed_values.index(val))
20
+ end
21
+
22
+ define_singleton_method(:valid_cast?) do |raw|
23
+ super(raw) && allowed_values.include?(raw)
24
+ end
25
+
26
+ define_method(:value) do |native: false|
27
+ allowed_values[super()]
28
+ end
29
+
30
+ define_singleton_method(:type_digest) do
31
+ Digest::MD5.hexdigest(native_type_expr.to_s + allowed_values.map(&:to_s).join(","))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(time : ::Time)
4
+ @value = time.to_unix_ns / 1000_000_000.0
5
+ end
6
+
7
+ def initialize(ptr : Pointer(::UInt8))
8
+ @value = ptr.as(Pointer(::Float64)).value
9
+ end
10
+
11
+ def initialize(@value : ::Float64)
12
+ end
13
+
14
+ def ==(other : ::Time)
15
+ value == other
16
+ end
17
+
18
+ def value=(time : ::Time)
19
+ @value = time.to_unix_ns / 1000_000_000.0
20
+ end
21
+
22
+ def value : ::Time
23
+ ::Time.unix_ns((@value * 1000_000_000).to_i128)
24
+ end
25
+
26
+ def self.memsize
27
+ <%= memsize %>
28
+ end
29
+
30
+
31
+ def self.write_single(pointer : Pointer(::UInt8), time)
32
+ pointer.as(Pointer(::Float64)).value = time.to_unix_ns / 1000_000_000.0
33
+ end
34
+
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ module CrystalRuby::Types
6
+ Time = Primitive.build(:Time, convert_if: [Root::Time, Root::String, DateTime], ffi_type: :double) do
7
+ def initialize(val = Root::Time.now)
8
+ super
9
+ end
10
+
11
+ def value=(val)
12
+ super(
13
+ if val.respond_to?(:to_time)
14
+ val.to_time.to_f
15
+ else
16
+ val.respond_to?(:to_f) ? val.to_f : 0
17
+ end
18
+ )
19
+ end
20
+
21
+ def value(native: false)
22
+ ::Time.at(super)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ module CrystalRuby
2
+ module Types
3
+ class Type
4
+ property memory : Pointer(::UInt8) = Pointer(::UInt8).null
5
+
6
+ macro method_missing(call)
7
+ current_value = self.native
8
+ current_hash = current_value.hash
9
+ return_value = current_value.{{ call }}
10
+
11
+ if current_hash != current_value.hash
12
+ self.value = current_value
13
+ end
14
+ return_value
15
+ end
16
+
17
+ def to_s
18
+ native.to_s
19
+ end
20
+
21
+ def synchronize
22
+ CrystalRuby.synchronize do
23
+ yield
24
+ end
25
+ end
26
+
27
+ def self.synchronize
28
+ yield
29
+ end
30
+
31
+ def variable_width?
32
+ false
33
+ end
34
+
35
+ def return_value
36
+ memory
37
+ end
38
+
39
+ def self.free(memory : Pointer(::UInt8))
40
+ LibC.free(memory)
41
+ end
42
+
43
+ def self.malloc(size : Int) : Pointer(::UInt8)
44
+ LibC.calloc(size, 1).as(Pointer(::UInt8))
45
+ end
46
+
47
+ def malloc(memsize)
48
+ self.class.malloc(memsize)
49
+ end
50
+
51
+ def self.each_child_address(pointer : Pointer(::UInt8), &block : Pointer(::UInt8) -> Nil)
52
+ # Do nothing
53
+ end
54
+
55
+ def self.fetch_multi!(pointer : Pointer(::UInt8), size)
56
+ size.times.map { |i| fetch_single(pointer + i * refsize) }.to_a
57
+ end
58
+
59
+ def self.fetch_multi_native!(pointer : Pointer(::UInt8), size)
60
+ size.times.map { |i| fetch_single(pointer + i * refsize).native }.to_a
61
+ end
62
+ end
63
+ end
64
+ end