crystalruby 0.2.3 → 0.3.1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/Dockerfile +23 -2
  4. data/README.md +395 -198
  5. data/Rakefile +4 -3
  6. data/crystalruby.gemspec +2 -2
  7. data/examples/adder/adder.rb +1 -1
  8. data/exe/crystalruby +1 -0
  9. data/lib/crystalruby/adapter.rb +143 -73
  10. data/lib/crystalruby/arc_mutex.rb +47 -0
  11. data/lib/crystalruby/compilation.rb +32 -3
  12. data/lib/crystalruby/config.rb +41 -37
  13. data/lib/crystalruby/function.rb +216 -73
  14. data/lib/crystalruby/library.rb +157 -51
  15. data/lib/crystalruby/reactor.rb +63 -44
  16. data/lib/crystalruby/source_reader.rb +92 -0
  17. data/lib/crystalruby/template.rb +16 -5
  18. data/lib/crystalruby/templates/function.cr +11 -10
  19. data/lib/crystalruby/templates/index.cr +53 -66
  20. data/lib/crystalruby/templates/inline_chunk.cr +1 -1
  21. data/lib/crystalruby/templates/ruby_interface.cr +34 -0
  22. data/lib/crystalruby/templates/top_level_function.cr +62 -0
  23. data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
  24. data/lib/crystalruby/typebuilder.rb +11 -55
  25. data/lib/crystalruby/typemaps.rb +92 -67
  26. data/lib/crystalruby/types/concerns/allocator.rb +80 -0
  27. data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
  28. data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
  29. data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
  30. data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
  31. data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
  32. data/lib/crystalruby/types/fixed_width/tagged_union.rb +113 -0
  33. data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
  34. data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
  35. data/lib/crystalruby/types/fixed_width.cr +138 -0
  36. data/lib/crystalruby/types/fixed_width.rb +205 -0
  37. data/lib/crystalruby/types/primitive.cr +21 -0
  38. data/lib/crystalruby/types/primitive.rb +117 -0
  39. data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
  40. data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
  41. data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
  42. data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
  43. data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
  44. data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
  45. data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
  46. data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
  47. data/lib/crystalruby/types/primitive_types/time.cr +35 -0
  48. data/lib/crystalruby/types/primitive_types/time.rb +25 -0
  49. data/lib/crystalruby/types/type.cr +64 -0
  50. data/lib/crystalruby/types/type.rb +249 -30
  51. data/lib/crystalruby/types/variable_width/array.cr +74 -0
  52. data/lib/crystalruby/types/variable_width/array.rb +88 -0
  53. data/lib/crystalruby/types/variable_width/hash.cr +146 -0
  54. data/lib/crystalruby/types/variable_width/hash.rb +117 -0
  55. data/lib/crystalruby/types/variable_width/string.cr +36 -0
  56. data/lib/crystalruby/types/variable_width/string.rb +18 -0
  57. data/lib/crystalruby/types/variable_width.cr +23 -0
  58. data/lib/crystalruby/types/variable_width.rb +46 -0
  59. data/lib/crystalruby/types.rb +32 -13
  60. data/lib/crystalruby/version.rb +2 -2
  61. data/lib/crystalruby.rb +13 -6
  62. metadata +42 -22
  63. data/lib/crystalruby/types/array.rb +0 -15
  64. data/lib/crystalruby/types/bool.rb +0 -3
  65. data/lib/crystalruby/types/hash.rb +0 -17
  66. data/lib/crystalruby/types/named_tuple.rb +0 -28
  67. data/lib/crystalruby/types/nil.rb +0 -3
  68. data/lib/crystalruby/types/numbers.rb +0 -5
  69. data/lib/crystalruby/types/string.rb +0 -3
  70. data/lib/crystalruby/types/symbol.rb +0 -3
  71. data/lib/crystalruby/types/time.rb +0 -8
  72. data/lib/crystalruby/types/tuple.rb +0 -17
  73. data/lib/crystalruby/types/type_serializer/json.rb +0 -41
  74. data/lib/crystalruby/types/type_serializer.rb +0 -37
  75. data/lib/crystalruby/types/typedef.rb +0 -57
  76. data/lib/crystalruby/types/union_type.rb +0 -43
  77. data/lib/module.rb +0 -3
@@ -1,48 +1,40 @@
1
1
  module CrystalRuby
2
-
3
2
  ARGV1 = "crystalruby"
4
- CALLBACK_MUX = Mutex.new
5
3
 
6
- alias ErrorCallback = (Pointer(UInt8), Pointer(UInt8), UInt32 -> Void)
4
+ alias ErrorCallback = (Pointer(::UInt8), Pointer(::UInt8), Pointer(::UInt8), ::UInt32 -> Void)
5
+
6
+ class_property libname : String = "crystalruby"
7
+ class_property callbacks : Channel(Proc(Nil)) = Channel(Proc(Nil)).new
8
+ class_property rc_mux : Pointer(Void) = Pointer(Void).null
9
+ class_property task_counter : Atomic(Int32) = Atomic(Int32).new(0)
7
10
 
8
11
  # Initializing Crystal Ruby invokes init on the Crystal garbage collector.
9
12
  # We need to be sure to only do this once.
10
- @@initialized = false
11
-
12
- @@libname = "crystalruby"
13
- # Our Ruby <-> Crystal Reactor uses Fibers, with callbacks to allow
14
- # multiple concurrent Crystal operations to be queued
15
- @@callbacks = [] of Proc(Nil)
16
-
17
- # We only continue to yield to the Crystal scheduler from Ruby
18
- # while there are outstanding tasks.
19
- @@task_counter : Atomic(Int32) = Atomic.new(0)
13
+ class_property initialized : Bool = false
20
14
 
21
15
  # We can override the error callback to catch errors in Crystal,
22
16
  # and explicitly expose them to Ruby.
23
- @@error_callback : ErrorCallback = ->(t : UInt8* , s : UInt8*, tid : UInt32){ puts "Error: #{t}:#{s}" }
17
+ @@error_callback : ErrorCallback?
24
18
 
25
19
  # This is the entry point for instantiating CrystalRuby
26
20
  # We:
27
21
  # 1. Initialize the Crystal garbage collector
28
22
  # 2. Set the error callback
29
23
  # 3. Call the Crystal main function
30
- def self.init(libname : Pointer(UInt8), error_callback : ErrorCallback)
31
- return if @@initialized
32
- @@initialized = true
24
+ def self.init(libname : Pointer(::UInt8), @@error_callback : ErrorCallback, @@rc_mux : Pointer(Void))
25
+ return if self.initialized
26
+ self.initialized = true
33
27
  argv_ptr = ARGV1.to_unsafe
34
28
  Crystal.main_user_code(0, pointerof(argv_ptr))
35
- @@error_callback = error_callback
36
- @@libname = String.new(libname)
29
+ self.libname = String.new(libname)
30
+ LibGC.set_finalize_on_demand(1)
37
31
  end
38
32
 
39
33
  # Explicit error handling (triggers exception within Ruby on the same thread)
40
- def self.report_error(error_type : String, str : String, thread_id : UInt32, )
41
- @@error_callback.call(error_type.to_unsafe, str.to_unsafe, thread_id)
42
- end
43
-
44
- def self.error_callback : ErrorCallback
45
- @@error_callback
34
+ def self.report_error(error_type : String, message : String, backtrace : String, thread_id : UInt32)
35
+ if error_reporter = @@error_callback
36
+ error_reporter.call(error_type.to_unsafe, message.to_unsafe, backtrace.to_unsafe, thread_id)
37
+ end
46
38
  end
47
39
 
48
40
  # New async task started
@@ -57,51 +49,40 @@ module CrystalRuby
57
49
 
58
50
  # Get number of outstanding tasks
59
51
  def self.get_task_counter : Int32
60
- @@task_counter.get()
52
+ @@task_counter.get
61
53
  end
62
54
 
63
55
  # Queue a callback for an async task
64
56
  def self.queue_callback(callback : Proc(Nil))
65
- CALLBACK_MUX.synchronize do
66
- @@callbacks << callback
67
- end
68
- end
69
-
70
- # Get number of queued callbacks
71
- def self.count_callbacks : Int32
72
- @@callbacks.size
57
+ self.callbacks.send(callback)
73
58
  end
74
59
 
75
- def self.libname : String
76
- @@libname
77
- end
78
-
79
- # Flush all callbacks
80
- def self.flush_callbacks : Int32
81
- CALLBACK_MUX.synchronize do
82
- count = @@callbacks.size
83
- @@callbacks.each do |callback|
84
- result = callback.call()
85
- end
86
- @@callbacks.clear
87
- end
88
- get_task_counter
60
+ def self.synchronize
61
+ LibC.pthread_mutex_lock(self.rc_mux)
62
+ yield
63
+ LibC.pthread_mutex_unlock(self.rc_mux)
89
64
  end
90
65
  end
91
66
 
92
67
  # Initialize CrystalRuby
93
- fun init(libname : Pointer(UInt8), cb : CrystalRuby::ErrorCallback): Void
94
- CrystalRuby.init(libname, cb)
68
+ fun init(libname : Pointer(::UInt8), cb : CrystalRuby::ErrorCallback, rc_mux : Pointer(Void)) : Void
69
+ CrystalRuby.init(libname, cb, rc_mux)
95
70
  end
96
71
 
97
72
  fun stop : Void
98
- LibGC.deinit()
73
+ LibGC.deinit
99
74
  end
100
75
 
101
76
  @[Link("gc")]
102
77
  lib LibGC
103
78
  $stackbottom = GC_stackbottom : Void*
104
79
  fun deinit = GC_deinit
80
+ fun set_finalize_on_demand = GC_set_finalize_on_demand(Int32)
81
+ fun invoke_finalizers = GC_invoke_finalizers : Int
82
+ end
83
+
84
+ lib LibC
85
+ fun calloc = calloc(Int32, Int32) : Void*
105
86
  end
106
87
 
107
88
  module GC
@@ -112,6 +93,11 @@ module GC
112
93
  def self.set_stackbottom(stack_bottom : Void*)
113
94
  LibGC.stackbottom = stack_bottom
114
95
  end
96
+
97
+ def self.collect
98
+ LibGC.collect
99
+ LibGC.invoke_finalizers
100
+ end
115
101
  end
116
102
 
117
103
  # Yield to the Crystal scheduler from Ruby
@@ -119,24 +105,25 @@ end
119
105
  # Otherwise, we yield to the Crystal scheduler and let Ruby know
120
106
  # how many outstanding tasks still remain (it will stop yielding to Crystal
121
107
  # once this figure reaches 0).
122
- fun yield() : Int32
123
- if CrystalRuby.count_callbacks == 0
124
- Fiber.yield
125
-
126
- # TODO: We should apply backpressure here to prevent busy waiting if the number of outstanding tasks is not decreasing.
127
- # Use a simple exponential backoff strategy, to increase the time between each yield up to a maximum of 1 second.
128
-
129
- CrystalRuby.get_task_counter
130
- else
131
- CrystalRuby.flush_callbacks()
108
+ fun yield : Int32
109
+ Fiber.yield
110
+ loop do
111
+ select
112
+ when callback = CrystalRuby.callbacks.receive
113
+ callback.call
114
+ else
115
+ break
116
+ end
132
117
  end
118
+ CrystalRuby.get_task_counter
133
119
  end
134
120
 
121
+ class Array(T)
122
+ def initialize(size : Int32, @buffer : Pointer(T))
123
+ @size = size.to_i32
124
+ @capacity = @size
125
+ end
126
+ end
135
127
 
136
- # This is where we define all our Crystal modules and types
137
- # derived from their Ruby counterparts.
138
- %{type_modules}
139
-
140
- # Require all generated crystal files
141
128
  require "json"
142
129
  %{requires}
@@ -1,6 +1,6 @@
1
1
  # This is the template used for writing inline chunks of Crystal code without direct
2
2
  # Ruby integration
3
3
 
4
- module %{module_name}
4
+ %{mod_or_class} %{module_name} %{superclass}
5
5
  %{body}
6
6
  end
@@ -0,0 +1,34 @@
1
+ # This is the template used for all CrystalRuby functions
2
+ # Calls to this method *from ruby* are first transformed through the lib function.
3
+ # Crystal code can simply call this method directly, enabling generated crystal code
4
+ # to call other generated crystal code without overhead.
5
+
6
+ %{module_or_class} %{module_name} %{superclass}
7
+ def %{fn_scope}%{fn_name}(%{fn_args}) : %{fn_ret_type}
8
+ %{convert_lib_args}
9
+ cb = %{module_name}.%{callback_name}
10
+ unless cb.nil?
11
+ callback_done_channel = Channel(Nil).new
12
+ return_value = nil
13
+ if Fiber.current == Thread.current.main_fiber
14
+ return_value = cb.call(%{lib_fn_arg_names})
15
+ return %{convert_return_type}
16
+ else
17
+ CrystalRuby.queue_callback(->{
18
+ return_value = cb.call(%{lib_fn_arg_names})
19
+ callback_done_channel.send(nil)
20
+ })
21
+ end
22
+ callback_done_channel.receive
23
+ return %{convert_return_type}
24
+ end
25
+ raise "No callback registered for %{fn_name}"
26
+
27
+ end
28
+
29
+ class_property %{callback_name} : Proc(%{lib_fn_types} %{lib_fn_ret_type})?
30
+ end
31
+
32
+ fun register_%{callback_name}(callback : Proc(%{lib_fn_types} %{lib_fn_ret_type})) : Void
33
+ %{module_name}.%{callback_name} = callback
34
+ end
@@ -0,0 +1,62 @@
1
+ # This is the template used for all CrystalRuby functions
2
+ # Calls to this method *from ruby* are first transformed through the lib function.
3
+ # Crystal code can simply call this method directly, enabling generated crystal code
4
+ # to call other generated crystal code without overhead.
5
+
6
+ def %{fn_name}(%{fn_args}) : %{fn_ret_type}
7
+ %{fn_body}
8
+ end
9
+
10
+ # This function is the entry point for the CrystalRuby code, exposed through FFI.
11
+ # We apply some basic error handling here, and convert the arguments and return values
12
+ # to ensure that we are using Crystal native types.
13
+ fun %{lib_fn_name}(%{lib_fn_args}): %{lib_fn_ret_type}
14
+ begin
15
+ %{convert_lib_args}
16
+ begin
17
+ return_value = %{fn_name}(%{arg_names})%{block_converter}
18
+ return %{convert_return_type}
19
+ rescue ex
20
+ CrystalRuby.report_error("RuntimeError", ex.message.to_s, ex.backtrace.to_json, 0)
21
+ end
22
+ rescue ex
23
+ CrystalRuby.report_error("ArgumentError", ex.message.to_s, ex.backtrace.to_json, 0)
24
+ end
25
+ return %{error_value}
26
+ end
27
+
28
+
29
+ # This function is the async entry point for the CrystalRuby code, exposed through FFI.
30
+ # We apply some basic error handling here, and convert the arguments and return values
31
+ # to ensure that we are using Crystal native types.
32
+ fun %{lib_fn_name}_async(%{lib_fn_args} thread_id: UInt32, callback : %{callback_type}): Void
33
+ begin
34
+ %{convert_lib_args}
35
+ CrystalRuby.increment_task_counter
36
+ spawn do
37
+ begin
38
+ return_value = %{fn_name}(%{arg_names})%{block_converter}
39
+ CrystalRuby.queue_callback(->{
40
+ converted = %{convert_return_type}
41
+ %{callback_call}
42
+ CrystalRuby.decrement_task_counter
43
+ })
44
+ rescue ex
45
+ exception = ex.message.to_s
46
+ backtrace = ex.backtrace.to_json
47
+ CrystalRuby.queue_callback(->{
48
+ CrystalRuby.report_error("RuntimeError", exception, backtrace, thread_id)
49
+ CrystalRuby.decrement_task_counter
50
+ })
51
+ end
52
+ end
53
+ rescue ex
54
+
55
+ exception = ex.message.to_s
56
+ backtrace = ex.backtrace.to_json
57
+ CrystalRuby.queue_callback(->{
58
+ CrystalRuby.report_error("RuntimeError", ex.message.to_s, backtrace, thread_id)
59
+ CrystalRuby.decrement_task_counter
60
+ })
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ # This is the template used for all CrystalRuby functions
2
+ # Calls to this method *from ruby* are first transformed through the lib function.
3
+ # Crystal code can simply call this method directly, enabling generated crystal code
4
+ # to call other generated crystal code without overhead.
5
+
6
+ module TopLevelCallbacks
7
+ class_property %{fn_name}_callback : Proc(%{lib_fn_types} %{lib_fn_ret_type})?
8
+ end
9
+
10
+ def %{fn_scope}%{fn_name}(%{fn_args}) : %{fn_ret_type}
11
+ %{convert_lib_args}
12
+ cb = TopLevelCallbacks.%{fn_name}_callback
13
+ unless cb.nil?
14
+ callback_done_channel = Channel(Nil).new
15
+ return_value = nil
16
+ if Fiber.current == Thread.current.main_fiber
17
+ return_value = cb.call(%{lib_fn_arg_names})
18
+ return %{convert_return_type}
19
+ else
20
+ CrystalRuby.queue_callback(->{
21
+ return_value = cb.call(%{lib_fn_arg_names})
22
+ callback_done_channel.send(nil)
23
+ })
24
+ end
25
+ callback_done_channel.receive
26
+ return %{convert_return_type}
27
+ end
28
+ raise "No callback registered for %{fn_name}"
29
+ end
30
+
31
+ fun register_%{fn_name}_callback(callback : Proc(%{lib_fn_types} %{lib_fn_ret_type})) : Void
32
+ TopLevelCallbacks.%{fn_name}_callback = callback
33
+ end
@@ -4,66 +4,22 @@ module CrystalRuby
4
4
  module TypeBuilder
5
5
  module_function
6
6
 
7
- def with_injected_type_dsl(context, &block)
8
- with_constants(context) do
9
- with_methods(context, &block)
7
+ def build_from_source(src, context: )
8
+ source_type = Types.with_binding_fallback(context) do |binding|
9
+ eval(src.is_a?(String) ? src : SourceReader.extract_source_from_proc(src), binding)
10
10
  end
11
- end
12
11
 
13
- def with_methods(context)
14
- restores = []
15
- %i[Array Hash NamedTuple Tuple].each do |method_name|
16
- old_method = begin
17
- context.instance_method(method_name)
18
- rescue StandardError
19
- nil
20
- end
21
- restores << [context, method_name, old_method]
22
- context.singleton_class.undef_method(method_name) if old_method
23
- context.define_singleton_method(method_name) do |*args|
24
- Types.send(method_name, *args)
25
- end
26
- end
27
- yield
28
- ensure
29
- restores.each do |context, method_name, old_method|
30
- context.singleton_class.undef_method(method_name)
31
- context.define_singleton_method(method_name, old_method) if old_method
32
- end
33
- end
12
+ return source_type if source_type.is_a?(Types::Root::Symbol)
34
13
 
35
- def with_constants(context)
36
- previous_const_pairs = CrystalRuby::Types.constants.map do |type|
37
- [type, begin
38
- context.const_get(type)
39
- rescue StandardError
40
- nil
41
- end]
42
- end
43
- CrystalRuby::Types.constants.each do |type|
44
- begin
45
- context.send(:remove_const, type)
46
- rescue StandardError
47
- nil
48
- end
49
- context.const_set(type, CrystalRuby::Types.const_get(type))
14
+ unless source_type.kind_of?(Class) && source_type < Types::Type
15
+ raise "Invalid type #{source_type.inspect}"
50
16
  end
51
- yield
52
- ensure
53
- previous_const_pairs.each do |const_name, const_value|
54
- begin
55
- context.send(:remove_const, const_name)
56
- rescue StandardError
57
- nil
58
- end
59
- context.const_set(const_name, const_value)
60
- end
61
- end
62
17
 
63
- def build
64
- result = yield
65
- Types::Type.validate!(result)
66
- Types::Typedef(result)
18
+ return source_type unless source_type.anonymous?
19
+
20
+ source_type.tap do |new_type|
21
+ Types::Type.validate!(new_type)
22
+ end
67
23
  end
68
24
  end
69
25
  end
@@ -3,55 +3,60 @@
3
3
  module CrystalRuby
4
4
  module Typemaps
5
5
  CRYSTAL_TYPE_MAP = {
6
- char: "Int8", # In Crystal, :char is typically represented as Int8
7
- uchar: "UInt8", # Unsigned char
8
- int8: "Int8", # Same as :char
9
- uint8: "UInt8", # Same as :uchar
10
- short: "Int16", # Short integer
11
- ushort: "UInt16", # Unsigned short integer
12
- int16: "Int16", # Same as :short
13
- uint16: "UInt16", # Same as :ushort
14
- int: "Int32", # Integer, Crystal defaults to 32 bits
15
- uint: "UInt32", # Unsigned integer
16
- int32: "Int32", # 32-bit integer
17
- uint32: "UInt32", # 32-bit unsigned integer
18
- long: "Int32 | Int64", # Long integer, size depends on the platform (32 or 64 bits)
6
+ char: "Int8", # In Crystal, :char is typically represented as Int8
7
+ uchar: "UInt8", # Unsigned char
8
+ int8: "Int8", # Same as :char
9
+ uint8: "UInt8", # Same as :uchar
10
+ short: "Int16", # Short integer
11
+ ushort: "UInt16", # Unsigned short integer
12
+ int16: "Int16", # Same as :short
13
+ uint16: "UInt16", # Same as :ushort
14
+ int: "Int32", # Integer, Crystal defaults to 32 bits
15
+ uint: "UInt32", # Unsigned integer
16
+ int32: "Int32", # 32-bit integer
17
+ uint32: "UInt32", # 32-bit unsigned integer
18
+ long: "Int32 | Int64", # Long integer, size depends on the platform (32 or 64 bits)
19
19
  ulong: "UInt32 | UInt64", # Unsigned long integer, size depends on the platform
20
- int64: "Int64", # 64-bit integer
21
- uint64: "UInt64", # 64-bit unsigned integer
22
- long_long: "Int64", # Same as :int64
23
- ulong_long: "UInt64", # Same as :uint64
24
- float: "Float32", # Floating point number (single precision)
25
- double: "Float64", # Double precision floating point number
26
- bool: "Bool", # Boolean type
27
- void: "Void", # Void type
28
- string: "String" # String type
20
+ int64: "Int64", # 64-bit integer
21
+ uint64: "UInt64", # 64-bit unsigned integer
22
+ long_long: "Int64", # Same as :int64
23
+ ulong_long: "UInt64", # Same as :uint64
24
+ float: "Float32", # Floating point number (single precision)
25
+ double: "Float64", # Double precision floating point number
26
+ bool: "Bool", # Boolean type
27
+ void: "Void", # Void type
28
+ string: "String", # String type
29
+ pointer: "Pointer(Void)" # Pointer type
30
+
29
31
  }
30
32
 
33
+ FFI_TYPE_MAP = CRYSTAL_TYPE_MAP.invert
34
+
31
35
  ERROR_VALUE = {
32
- char: "0", # In Crystal, :char is typically represented as Int8
33
- uchar: "0", # Unsigned char
34
- int8: "0", # Same as :char
35
- uint8: "0", # Same as :uchar
36
- short: "0", # Short integer
37
- ushort: "0", # Unsigned short integer
38
- int16: "0", # Same as :short
39
- uint16: "0", # Same as :ushort
40
- int: "0", # Integer, Crystal defaults to 32 bits
41
- uint: "0", # Unsigned integer
42
- int32: "0", # 32-bit integer
43
- uint32: "0", # 32-bit unsigned integer
44
- long: "0", # Long integer, size depends on the platform (32 or 64 bits)
45
- ulong: "0", # Unsigned long integer, size depends on the platform
46
- int64: "0", # 64-bit integer
47
- uint64: "0", # 64-bit unsigned integer
48
- long_long: "0", # Same as :int64
49
- ulong_long: "0", # Same as :uint64
50
- float: "0.0", # Floating point number (single precision)
51
- double: "0.0", # Double precision floating point number
36
+ char: "0i8", # In Crystal, :char is typically represented as Int8
37
+ uchar: "0u8", # Unsigned char
38
+ int8: "0i8", # Same as :char
39
+ uint8: "0u8", # Same as :uchar
40
+ short: "0i16", # Short integer
41
+ ushort: "0u16", # Unsigned short integer
42
+ int16: "0i16", # Same as :short
43
+ uint16: "0u16", # Same as :ushort
44
+ int: "0i32", # Integer, Crystal defaults to 32 bits
45
+ uint: "0u32", # Unsigned integer
46
+ int32: "0i32", # 32-bit integer
47
+ uint32: "0u32", # 32-bit unsigned integer
48
+ long: "0i64", # Long integer, size depends on the platform (32 or 64 bits)
49
+ ulong: "0u64", # Unsigned long integer, size depends on the platform
50
+ int64: "0_i64", # 64-bit integer
51
+ uint64: "0_u64", # 64-bit unsigned integer
52
+ long_long: "0_i64", # Same as :int64
53
+ ulong_long: "0_u64", # Same as :uint64
54
+ float: "0.0f32", # Floating point number (single precision)
55
+ double: "0.0f64", # Double precision floating point number
52
56
  bool: "false", # Boolean type
53
57
  void: "Void", # Void type
54
- string: '"".to_unsafe' # String type
58
+ string: '"".to_unsafe', # String type
59
+ pointer: "Pointer(Void).null" # Pointer type
55
60
  }
56
61
 
57
62
  C_TYPE_MAP = CRYSTAL_TYPE_MAP.merge(
@@ -68,32 +73,40 @@ module CrystalRuby
68
73
  void: {
69
74
  to: "nil"
70
75
  }
71
- }.tap do |hash|
72
- hash.define_singleton_method(:convert) do |type, dir, expr|
73
- if hash.key?(type)
74
- conversion_string = hash[type][dir]
75
- conversion_string =~ /%/ ? conversion_string % expr : conversion_string
76
- else
77
- expr
78
- end
76
+ }.tap do |hash|
77
+ hash.define_singleton_method(:convert) do |type, dir, expr|
78
+ if hash.key?(type)
79
+ conversion_string = hash[type][dir]
80
+ conversion_string =~ /%/ ? conversion_string % expr : conversion_string
81
+ else
82
+ expr
79
83
  end
80
84
  end
85
+ end
81
86
 
82
87
  def build_type_map(crystalruby_type)
88
+ crystalruby_type = CRType(&crystalruby_type) if crystalruby_type.is_a?(Proc)
83
89
  {
84
90
  ffi_type: ffi_type(crystalruby_type),
85
91
  ffi_ret_type: ffi_type(crystalruby_type),
86
92
  crystal_type: crystal_type(crystalruby_type),
93
+ crystalruby_type: crystalruby_type,
87
94
  lib_type: lib_type(crystalruby_type),
88
95
  error_value: error_value(crystalruby_type),
89
- arg_mapper: if crystalruby_type.is_a?(Types::TypeSerializer)
96
+ arg_mapper: if crystalruby_type.is_a?(Class) && crystalruby_type < Types::Type
90
97
  lambda { |arg|
91
- crystalruby_type.prepare_argument(arg)
98
+ arg = crystalruby_type.new(arg.memory) if arg.is_a?(Types::Type) && !arg.is_a?(crystalruby_type)
99
+ arg = crystalruby_type.new(arg) unless arg.is_a?(Types::Type)
100
+ arg
92
101
  }
93
102
  end,
94
- retval_mapper: if crystalruby_type.is_a?(Types::TypeSerializer)
103
+ retval_mapper: if crystalruby_type.is_a?(Class) && crystalruby_type < Types::Type
95
104
  lambda { |arg|
96
- crystalruby_type.prepare_retval(arg)
105
+ if arg.is_a?(Types::Type) && !arg.is_a?(crystalruby_type)
106
+ arg = crystalruby_type.new(arg.memory)
107
+ end
108
+ arg = crystalruby_type.new(arg) unless arg.is_a?(Types::Type)
109
+ crystalruby_type.anonymous? ? arg.native : arg
97
110
  }
98
111
  end,
99
112
  convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
@@ -104,13 +117,20 @@ module CrystalRuby
104
117
  def ffi_type(type)
105
118
  case type
106
119
  when Symbol then type
107
- when Types::TypeSerializer then type.ffi_type
120
+ when Class
121
+ if type < Types::FixedWidth
122
+ :pointer
123
+ elsif type < Types::Primitive
124
+ type.ffi_type
125
+ end
108
126
  end
109
127
  end
110
128
 
111
129
  def lib_type(type)
112
- if type.is_a?(Types::TypeSerializer)
113
- type.lib_type
130
+ if type.is_a?(Class) && type < Types::FixedWidth
131
+ "Pointer(::UInt8)"
132
+ elsif type.is_a?(Class) && type < Types::Type
133
+ C_TYPE_MAP.fetch(type.ffi_type)
114
134
  else
115
135
  C_TYPE_MAP.fetch(type)
116
136
  end
@@ -119,8 +139,10 @@ module CrystalRuby
119
139
  end
120
140
 
121
141
  def error_value(type)
122
- if type.is_a?(Types::TypeSerializer)
123
- type.error_value
142
+ if type.is_a?(Class) && type < Types::FixedWidth
143
+ "Pointer(::UInt8).null"
144
+ elsif type.is_a?(Class) && type < Types::Type
145
+ ERROR_VALUE.fetch(type.ffi_type)
124
146
  else
125
147
  ERROR_VALUE.fetch(type)
126
148
  end
@@ -129,8 +151,8 @@ module CrystalRuby
129
151
  end
130
152
 
131
153
  def crystal_type(type)
132
- if type.is_a?(Types::TypeSerializer)
133
- type.crystal_type
154
+ if type.is_a?(Class) && type < Types::Type
155
+ type.anonymous? ? type.native_type_expr : type.inspect
134
156
  else
135
157
  CRYSTAL_TYPE_MAP.fetch(type)
136
158
  end
@@ -139,16 +161,19 @@ module CrystalRuby
139
161
  end
140
162
 
141
163
  def convert_lib_to_crystal_type(expr, type)
142
- if type.is_a?(Types::TypeSerializer)
143
- type.lib_to_crystal_type_expr(expr)
164
+ if type.is_a?(Class) && type < Types::Type
165
+ expr = "#{expr}.not_nil!" unless type.nil?
166
+ type.pointer_to_crystal_type_conversion(expr)
167
+ elsif type == :void
168
+ "nil"
144
169
  else
145
- C_TYPE_CONVERSIONS.convert(type, :from, expr)
170
+ "#{C_TYPE_CONVERSIONS.convert(type, :from, expr)}.not_nil!"
146
171
  end
147
172
  end
148
173
 
149
174
  def convert_crystal_to_lib_type(expr, type)
150
- if type.is_a?(Types::TypeSerializer)
151
- type.crystal_to_lib_type_expr(expr)
175
+ if type.is_a?(Class) && type < Types::Type
176
+ type.crystal_type_to_pointer_type_conversion(expr)
152
177
  else
153
178
  C_TYPE_CONVERSIONS.convert(type, :to, expr)
154
179
  end