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