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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/Dockerfile +23 -2
- data/README.md +395 -198
- data/Rakefile +4 -3
- data/crystalruby.gemspec +2 -2
- data/examples/adder/adder.rb +1 -1
- data/exe/crystalruby +1 -0
- data/lib/crystalruby/adapter.rb +143 -73
- data/lib/crystalruby/arc_mutex.rb +47 -0
- data/lib/crystalruby/compilation.rb +32 -3
- data/lib/crystalruby/config.rb +41 -37
- data/lib/crystalruby/function.rb +216 -73
- data/lib/crystalruby/library.rb +157 -51
- data/lib/crystalruby/reactor.rb +63 -44
- data/lib/crystalruby/source_reader.rb +92 -0
- data/lib/crystalruby/template.rb +16 -5
- data/lib/crystalruby/templates/function.cr +11 -10
- data/lib/crystalruby/templates/index.cr +53 -66
- data/lib/crystalruby/templates/inline_chunk.cr +1 -1
- data/lib/crystalruby/templates/ruby_interface.cr +34 -0
- data/lib/crystalruby/templates/top_level_function.cr +62 -0
- data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
- data/lib/crystalruby/typebuilder.rb +11 -55
- data/lib/crystalruby/typemaps.rb +92 -67
- data/lib/crystalruby/types/concerns/allocator.rb +80 -0
- data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
- data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
- data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
- data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
- data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
- data/lib/crystalruby/types/fixed_width/tagged_union.rb +113 -0
- data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
- data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
- data/lib/crystalruby/types/fixed_width.cr +138 -0
- data/lib/crystalruby/types/fixed_width.rb +205 -0
- data/lib/crystalruby/types/primitive.cr +21 -0
- data/lib/crystalruby/types/primitive.rb +117 -0
- data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
- data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
- data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
- data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
- data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
- data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
- data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
- data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
- data/lib/crystalruby/types/primitive_types/time.cr +35 -0
- data/lib/crystalruby/types/primitive_types/time.rb +25 -0
- data/lib/crystalruby/types/type.cr +64 -0
- data/lib/crystalruby/types/type.rb +249 -30
- data/lib/crystalruby/types/variable_width/array.cr +74 -0
- data/lib/crystalruby/types/variable_width/array.rb +88 -0
- data/lib/crystalruby/types/variable_width/hash.cr +146 -0
- data/lib/crystalruby/types/variable_width/hash.rb +117 -0
- data/lib/crystalruby/types/variable_width/string.cr +36 -0
- data/lib/crystalruby/types/variable_width/string.rb +18 -0
- data/lib/crystalruby/types/variable_width.cr +23 -0
- data/lib/crystalruby/types/variable_width.rb +46 -0
- data/lib/crystalruby/types.rb +32 -13
- data/lib/crystalruby/version.rb +2 -2
- data/lib/crystalruby.rb +13 -6
- metadata +42 -22
- data/lib/crystalruby/types/array.rb +0 -15
- data/lib/crystalruby/types/bool.rb +0 -3
- data/lib/crystalruby/types/hash.rb +0 -17
- data/lib/crystalruby/types/named_tuple.rb +0 -28
- data/lib/crystalruby/types/nil.rb +0 -3
- data/lib/crystalruby/types/numbers.rb +0 -5
- data/lib/crystalruby/types/string.rb +0 -3
- data/lib/crystalruby/types/symbol.rb +0 -3
- data/lib/crystalruby/types/time.rb +0 -8
- data/lib/crystalruby/types/tuple.rb +0 -17
- data/lib/crystalruby/types/type_serializer/json.rb +0 -41
- data/lib/crystalruby/types/type_serializer.rb +0 -37
- data/lib/crystalruby/types/typedef.rb +0 -57
- data/lib/crystalruby/types/union_type.rb +0 -43
- 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
|
-
|
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
|
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
|
32
|
-
|
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
|
-
|
36
|
-
|
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,
|
41
|
-
@@error_callback
|
42
|
-
|
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
|
-
|
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.
|
76
|
-
|
77
|
-
|
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
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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}
|
@@ -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
|
8
|
-
|
9
|
-
|
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
|
-
|
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
|
-
|
36
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
data/lib/crystalruby/typemaps.rb
CHANGED
@@ -3,55 +3,60 @@
|
|
3
3
|
module CrystalRuby
|
4
4
|
module Typemaps
|
5
5
|
CRYSTAL_TYPE_MAP = {
|
6
|
-
char: "Int8",
|
7
|
-
uchar: "UInt8",
|
8
|
-
int8: "Int8",
|
9
|
-
uint8: "UInt8",
|
10
|
-
short: "Int16",
|
11
|
-
ushort: "UInt16",
|
12
|
-
int16: "Int16",
|
13
|
-
uint16: "UInt16",
|
14
|
-
int: "Int32",
|
15
|
-
uint: "UInt32",
|
16
|
-
int32: "Int32",
|
17
|
-
uint32: "UInt32",
|
18
|
-
long: "Int32 | Int64",
|
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",
|
21
|
-
uint64: "UInt64",
|
22
|
-
long_long: "Int64",
|
23
|
-
ulong_long: "UInt64",
|
24
|
-
float: "Float32",
|
25
|
-
double: "Float64",
|
26
|
-
bool: "Bool",
|
27
|
-
void: "Void",
|
28
|
-
string: "String"
|
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: "
|
33
|
-
uchar: "
|
34
|
-
int8: "
|
35
|
-
uint8: "
|
36
|
-
short: "
|
37
|
-
ushort: "
|
38
|
-
int16: "
|
39
|
-
uint16: "
|
40
|
-
int: "
|
41
|
-
uint: "
|
42
|
-
int32: "
|
43
|
-
uint32: "
|
44
|
-
long: "
|
45
|
-
ulong: "
|
46
|
-
int64: "
|
47
|
-
uint64: "
|
48
|
-
long_long: "
|
49
|
-
ulong_long: "
|
50
|
-
float: "0.
|
51
|
-
double: "0.
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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::
|
96
|
+
arg_mapper: if crystalruby_type.is_a?(Class) && crystalruby_type < Types::Type
|
90
97
|
lambda { |arg|
|
91
|
-
crystalruby_type.
|
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::
|
103
|
+
retval_mapper: if crystalruby_type.is_a?(Class) && crystalruby_type < Types::Type
|
95
104
|
lambda { |arg|
|
96
|
-
|
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
|
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::
|
113
|
-
|
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::
|
123
|
-
|
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::
|
133
|
-
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::
|
143
|
-
type.
|
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::
|
151
|
-
type.
|
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
|