crystalruby 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/README.md +391 -195
  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 +33 -4
  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