crystalruby 0.3.2 → 0.3.4
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 +4 -0
- data/lib/crystalruby/adapter.rb +21 -16
- data/lib/crystalruby/function.rb +32 -21
- data/lib/crystalruby/library.rb +5 -5
- data/lib/crystalruby/reactor.rb +78 -21
- data/lib/crystalruby/source_reader.rb +17 -19
- data/lib/crystalruby/templates/index.cr +10 -2
- data/lib/crystalruby/typemaps.rb +39 -29
- data/lib/crystalruby/types/concerns/allocator.rb +14 -2
- data/lib/crystalruby/types/fixed_width/proc.rb +4 -3
- data/lib/crystalruby/types/fixed_width/tagged_union.rb +5 -0
- data/lib/crystalruby/types/fixed_width.cr +18 -0
- data/lib/crystalruby/types/fixed_width.rb +11 -5
- data/lib/crystalruby/types/primitive.rb +2 -1
- data/lib/crystalruby/types/primitive_types/numbers.rb +2 -2
- data/lib/crystalruby/types/primitive_types/symbol.rb +4 -0
- data/lib/crystalruby/types/type.cr +8 -0
- data/lib/crystalruby/types/type.rb +17 -2
- data/lib/crystalruby/types/variable_width/string.rb +1 -1
- data/lib/crystalruby/types/variable_width.rb +2 -1
- data/lib/crystalruby/version.rb +1 -1
- metadata +14 -9
- data/crystalruby.gemspec +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cccd1a9a08608d1973ec2cca190828d8475652337dbe6ec16b9d7cac1646cdab
|
4
|
+
data.tar.gz: 441bf39c0a3627d6df202c54c3440bb69e47c91473c1974b3caa330e0defa7a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fafb4fecf18aa7e5221642fbb956457987b286d50600f0ced48a91eaf10a4e2ccc83546d2632058b765256fc3510ce17cc5a6aa37455583279fb101a0c56fe70
|
7
|
+
data.tar.gz: 77fdc7ebdd02dab88243aec94eb6e06fc0bf2b65685b2eeb0b0304ad3087903879759823045c4ee07c9453715800237c1a230a1fdc7fb0e8eb2bc3636fb90e18
|
data/CHANGELOG.md
CHANGED
data/lib/crystalruby/adapter.rb
CHANGED
@@ -36,7 +36,7 @@ module CrystalRuby
|
|
36
36
|
# @option options [Boolean] :async (false) Mark the method as async (allows multiplexing).
|
37
37
|
# @option options [String] :lib ("crystalruby") The name of the library to compile the Crystal code into.
|
38
38
|
# @option options [Proc] :block An optional wrapper Ruby block that wraps around any invocations of the crystal code
|
39
|
-
def crystallize(
|
39
|
+
def crystallize(returns = :void, raw: false, async: false, lib: "crystalruby", &block)
|
40
40
|
(self == TOPLEVEL_BINDING.receiver ? Object : self).instance_eval do
|
41
41
|
@crystallize_next = {
|
42
42
|
raw: raw,
|
@@ -49,7 +49,7 @@ module CrystalRuby
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Alias for `crystallize`
|
52
|
-
alias
|
52
|
+
alias crystalize crystallize
|
53
53
|
|
54
54
|
# Exposes a Ruby method to one or more Crystal libraries.
|
55
55
|
# Type annotations follow the same rules as the `crystallize` method, but are
|
@@ -58,7 +58,7 @@ module CrystalRuby
|
|
58
58
|
# @param [Hash] options The options hash.
|
59
59
|
# @option options [Boolean] :raw (false) Pass raw Crystal code to the compiler as a string.
|
60
60
|
# @option options [String] :libs (["crystalruby"]) The name of the Crystal librarie(s) to expose the Ruby code to.
|
61
|
-
def expose_to_crystal(
|
61
|
+
def expose_to_crystal(returns = :void, libs: ["crystalruby"])
|
62
62
|
(self == TOPLEVEL_BINDING.receiver ? Object : self).instance_eval do
|
63
63
|
@expose_next_to_crystal = {
|
64
64
|
returns: returns,
|
@@ -70,7 +70,7 @@ module CrystalRuby
|
|
70
70
|
# Define a shard dependency
|
71
71
|
# This dependency will be automatically injected into the shard.yml file for
|
72
72
|
# the given library and installed upon compile if it is not already installed.
|
73
|
-
def shard(shard_name, lib:
|
73
|
+
def shard(shard_name, lib: "crystalruby", **opts)
|
74
74
|
CrystalRuby::Library[lib].require_shard(shard_name, **opts)
|
75
75
|
end
|
76
76
|
|
@@ -78,14 +78,18 @@ module CrystalRuby
|
|
78
78
|
# This is useful for defining classes, modules, performing set-up tasks etc.
|
79
79
|
# See: docs for .crystallize to understand the `raw` and `lib` parameters.
|
80
80
|
def crystal(raw: false, lib: "crystalruby", &block)
|
81
|
-
inline_crystal_body = respond_to?(:name)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
81
|
+
inline_crystal_body = if respond_to?(:name)
|
82
|
+
Template::InlineChunk.render(
|
83
|
+
{
|
84
|
+
module_name: name,
|
85
|
+
body: SourceReader.extract_source_from_proc(block, raw: raw),
|
86
|
+
mod_or_class: is_a?(Class) && self < Types::Type ? "class" : "module",
|
87
|
+
superclass: is_a?(Class) && self < Types::Type ? "< #{crystal_supertype}" : ""
|
88
|
+
}
|
89
|
+
)
|
90
|
+
else
|
91
|
+
SourceReader.extract_source_from_proc(block, raw: raw)
|
92
|
+
end
|
89
93
|
|
90
94
|
CrystalRuby::Library[lib].crystallize_chunk(
|
91
95
|
self,
|
@@ -94,7 +98,6 @@ module CrystalRuby
|
|
94
98
|
)
|
95
99
|
end
|
96
100
|
|
97
|
-
|
98
101
|
# This method provides a useful DSL for defining Crystal types in pure Ruby
|
99
102
|
# MyType = CRType{ Int32 | Hash(String, Array(Bool) | Float65 | Nil) }
|
100
103
|
# @param [Proc] block The block within which we build the type definition.
|
@@ -152,14 +155,16 @@ module CrystalRuby
|
|
152
155
|
|
153
156
|
owner = method.owner.singleton_class? ? method.owner.attached_object : method.owner
|
154
157
|
owner.class_eval(src)
|
155
|
-
|
156
|
-
|
158
|
+
unless method.is_a?(UnboundMethod) && method.owner.ancestors.include?(CrystalRuby::Types::Type)
|
159
|
+
owner.instance_eval(src)
|
160
|
+
end
|
161
|
+
method = owner.send(method.is_a?(UnboundMethod) ? :instance_method : :method, method.name)
|
157
162
|
|
158
163
|
libs.each do |lib|
|
159
164
|
CrystalRuby::Library[lib].expose_method(
|
160
165
|
method,
|
161
166
|
args,
|
162
|
-
returns
|
167
|
+
returns
|
163
168
|
)
|
164
169
|
end
|
165
170
|
end
|
data/lib/crystalruby/function.rb
CHANGED
@@ -66,6 +66,7 @@ module CrystalRuby
|
|
66
66
|
raise ArgumentError, "no block given but function expects block" if !blk && func.takes_block?
|
67
67
|
|
68
68
|
args << blk if blk
|
69
|
+
|
69
70
|
func.map_args!(args)
|
70
71
|
args.unshift(memory) if func.instance_method
|
71
72
|
|
@@ -86,27 +87,35 @@ module CrystalRuby
|
|
86
87
|
def register_callback!
|
87
88
|
return unless ruby
|
88
89
|
|
89
|
-
|
90
|
+
ret_type = ffi_ret_type == :string ? :pointer : ffi_ret_type
|
91
|
+
@callback_func = FFI::Function.new(ret_type, ffi_types) do |*args|
|
90
92
|
receiver = instance_method ? owner.new(args.shift) : owner
|
91
|
-
ret_val =
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
93
|
+
ret_val = \
|
94
|
+
if takes_block?
|
95
|
+
block_arg = arg_type_map[:__yield_to][:crystalruby_type].new(args.pop)
|
96
|
+
receiver.send(name, *unmap_args(args)) do |*args|
|
97
|
+
args = args.map.with_index do |arg, i|
|
98
|
+
arg = block_arg.inner_types[i].new(arg) unless arg.is_a?(block_arg.inner_types[i])
|
99
|
+
arg.memory
|
100
|
+
end
|
101
|
+
return_val = block_arg.invoke(*args)
|
102
|
+
return_val = block_arg.inner_types[-1].new(return_val) unless return_val.is_a?(block_arg.inner_types[-1])
|
103
|
+
block_arg.inner_types[-1].anonymous? ? return_val.value : return_val
|
104
|
+
end
|
105
|
+
else
|
106
|
+
receiver.send(name, *unmap_args(args))
|
107
|
+
end
|
107
108
|
unmap_retval(ret_val)
|
108
109
|
end
|
109
|
-
|
110
|
+
|
111
|
+
Reactor.schedule_work!(
|
112
|
+
lib,
|
113
|
+
:"register_#{name.to_s.gsub("?", "q").gsub("=", "eq").gsub("!", "bang")}_callback",
|
114
|
+
@callback_func,
|
115
|
+
:void,
|
116
|
+
blocking: true,
|
117
|
+
async: false
|
118
|
+
)
|
110
119
|
end
|
111
120
|
|
112
121
|
# Attaches the crystallized FFI functions to their related Ruby modules and classes.
|
@@ -245,7 +254,7 @@ module CrystalRuby
|
|
245
254
|
|
246
255
|
def register_custom_types!(lib)
|
247
256
|
custom_types.each do |crystalruby_type|
|
248
|
-
next unless
|
257
|
+
next unless Types::Type.subclass?(crystalruby_type)
|
249
258
|
|
250
259
|
[*crystalruby_type.nested_types].uniq.each do |type|
|
251
260
|
lib.register_type!(type)
|
@@ -263,7 +272,7 @@ module CrystalRuby
|
|
263
272
|
|
264
273
|
mapped = argmap[args[index]]
|
265
274
|
case mapped
|
266
|
-
when CrystalRuby::Types::Type
|
275
|
+
when CrystalRuby::Types::Type
|
267
276
|
args[index] = mapped.memory
|
268
277
|
(refs ||= []) << mapped
|
269
278
|
else
|
@@ -291,10 +300,12 @@ module CrystalRuby
|
|
291
300
|
end
|
292
301
|
|
293
302
|
def unmap_retval(retval)
|
303
|
+
return FFI::MemoryPointer.from_string(retval) if return_type_map[:ffi_ret_type] == :string
|
294
304
|
return retval unless return_type_map[:arg_mapper]
|
295
305
|
|
296
306
|
retval = return_type_map[:arg_mapper][retval]
|
297
|
-
|
307
|
+
|
308
|
+
retval = retval.memory if retval.is_a?(CrystalRuby::Types::Type)
|
298
309
|
retval
|
299
310
|
end
|
300
311
|
|
data/lib/crystalruby/library.rb
CHANGED
@@ -214,9 +214,7 @@ module CrystalRuby
|
|
214
214
|
unless compiled?
|
215
215
|
FileUtils.rm_f(lib_file)
|
216
216
|
|
217
|
-
if shard_dependencies.any? && shards.empty?
|
218
|
-
rewrite_shards_file!
|
219
|
-
end
|
217
|
+
rewrite_shards_file! if shard_dependencies.any? && shards.empty?
|
220
218
|
|
221
219
|
CrystalRuby::Compilation.install_shards!(src_dir)
|
222
220
|
CrystalRuby::Compilation.compile!(
|
@@ -239,14 +237,16 @@ module CrystalRuby
|
|
239
237
|
singleton_class.class_eval do
|
240
238
|
extend FFI::Library
|
241
239
|
ffi_lib lib_file
|
242
|
-
%i[yield init].each do |method_name|
|
240
|
+
%i[yield init gc].each do |method_name|
|
243
241
|
singleton_class.undef_method(method_name) if singleton_class.method_defined?(method_name)
|
244
242
|
undef_method(method_name) if method_defined?(method_name)
|
245
243
|
end
|
246
244
|
attach_function :init, %i[string pointer pointer], :void
|
247
245
|
attach_function :yield, %i[], :int
|
246
|
+
attach_function :gc, %i[], :void
|
248
247
|
lib_methods.each_value.select(&:ruby).each do |method|
|
249
|
-
attach_function :"register_#{method.name.to_s.gsub("?", "q").gsub("=", "eq").gsub("!", "bang")}_callback",
|
248
|
+
attach_function :"register_#{method.name.to_s.gsub("?", "q").gsub("=", "eq").gsub("!", "bang")}_callback",
|
249
|
+
%i[pointer], :void
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
data/lib/crystalruby/reactor.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
require "json"
|
2
|
+
|
1
3
|
module CrystalRuby
|
2
|
-
require 'json'
|
3
4
|
# The Reactor represents a singleton Thread responsible for running all Ruby/crystal interop code.
|
4
5
|
# Crystal's Fiber scheduler and GC assume all code is run on a single thread.
|
5
6
|
# This class is responsible for multiplexing Ruby and Crystal code onto a single thread.
|
@@ -9,12 +10,21 @@ module CrystalRuby
|
|
9
10
|
module_function
|
10
11
|
|
11
12
|
class SingleThreadViolation < StandardError; end
|
13
|
+
|
12
14
|
class StopReactor < StandardError; end
|
13
15
|
|
16
|
+
@op_count = 0
|
14
17
|
@single_thread_mode = false
|
15
18
|
|
16
19
|
REACTOR_QUEUE = Queue.new
|
17
20
|
|
21
|
+
# Invoke GC every 100 ops
|
22
|
+
GC_OP_THRESHOLD = ENV.fetch("CRYSTAL_GC_OP_THRESHOLD", 100).to_i
|
23
|
+
# Or every 0.05 seconds
|
24
|
+
GC_INTERVAL = ENV.fetch("CRYSTAL_GC_INTERVAL", 0.05).to_f
|
25
|
+
# Or if we've gotten hold of a reference to at least 100KB or more of fresh memory since last GC
|
26
|
+
GC_BYTES_SEEN_THRESHOLD = ENV.fetch("CRYSTAL_GC_BYTES_SEEN_THRESHOLD", 100 * 1024).to_i
|
27
|
+
|
18
28
|
# We maintain a map of threads, each with a mutex, condition variable, and result
|
19
29
|
THREAD_MAP = Hash.new do |h, tid_or_thread, tid = tid_or_thread|
|
20
30
|
if tid_or_thread.is_a?(Thread)
|
@@ -36,7 +46,7 @@ module CrystalRuby
|
|
36
46
|
|
37
47
|
# We memoize callbacks, once per return type
|
38
48
|
CALLBACKS_MAP = Hash.new do |h, rt|
|
39
|
-
h[rt] = FFI::Function.new(:void, [:int, *(rt == :void ? [] : [rt])]) do |tid, ret|
|
49
|
+
h[rt] = FFI::Function.new(:void, [:int, *((rt == :void) ? [] : [rt])]) do |tid, ret|
|
40
50
|
THREAD_MAP[tid][:error] = nil
|
41
51
|
THREAD_MAP[tid][:result] = ret
|
42
52
|
THREAD_MAP[tid][:cond].signal
|
@@ -49,7 +59,7 @@ module CrystalRuby
|
|
49
59
|
error_type = is_exception_type ? Object.const_get(error_type) : RuntimeError
|
50
60
|
error = error_type.new(message)
|
51
61
|
error.set_backtrace(JSON.parse(backtrace))
|
52
|
-
raise error
|
62
|
+
raise error unless THREAD_MAP.key?(tid)
|
53
63
|
|
54
64
|
THREAD_MAP[tid][:error] = error
|
55
65
|
THREAD_MAP[tid][:result] = nil
|
@@ -62,10 +72,12 @@ module CrystalRuby
|
|
62
72
|
|
63
73
|
def await_result!
|
64
74
|
mux, cond, result, err = thread_conditions.values_at(:mux, :cond, :result, :error)
|
65
|
-
cond.wait(mux) unless
|
75
|
+
cond.wait(mux) unless result || err
|
66
76
|
result, err, thread_conditions[:result], thread_conditions[:error] = thread_conditions.values_at(:result, :error)
|
67
77
|
if err
|
68
|
-
combined_backtrace = err.backtrace[0..(err.backtrace.index{|m|
|
78
|
+
combined_backtrace = err.backtrace[0..(err.backtrace.index { |m|
|
79
|
+
m.include?("call_blocking_function")
|
80
|
+
} || 2) - 3] + caller[5..-1]
|
69
81
|
err.set_backtrace(combined_backtrace)
|
70
82
|
raise err
|
71
83
|
end
|
@@ -78,30 +90,74 @@ module CrystalRuby
|
|
78
90
|
end
|
79
91
|
|
80
92
|
def stop!
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
93
|
+
return unless @main_loop
|
94
|
+
|
95
|
+
schedule_work!(self, :halt_loop!, :void, blocking: true, async: false)
|
96
|
+
@main_loop.join
|
97
|
+
@main_loop = nil
|
98
|
+
CrystalRuby.log_info "Reactor loop stopped"
|
87
99
|
end
|
88
100
|
|
89
101
|
def start!
|
102
|
+
@op_count = 0
|
90
103
|
@main_loop ||= Thread.new do
|
91
104
|
@main_thread_id = Thread.current.object_id
|
92
105
|
CrystalRuby.log_debug("Starting reactor")
|
93
106
|
CrystalRuby.log_debug("CrystalRuby initialized")
|
94
107
|
while true
|
95
|
-
handler, *args = REACTOR_QUEUE.pop
|
96
|
-
send(handler, *args)
|
108
|
+
handler, *args, lib = REACTOR_QUEUE.pop
|
109
|
+
send(handler, *args, lib)
|
110
|
+
@op_count += 1
|
111
|
+
invoke_gc_if_due!(lib)
|
97
112
|
end
|
98
|
-
rescue StopReactor
|
99
|
-
rescue
|
113
|
+
rescue StopReactor
|
114
|
+
rescue => e
|
100
115
|
CrystalRuby.log_error "Error: #{e}"
|
101
116
|
CrystalRuby.log_error e.backtrace
|
102
117
|
end
|
103
118
|
end
|
104
119
|
|
120
|
+
def invoke_gc_if_due!(lib)
|
121
|
+
schedule_work!(lib, :gc, :void, blocking: true, async: false, lib: lib) if lib && gc_due?
|
122
|
+
end
|
123
|
+
|
124
|
+
def gc_due?
|
125
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
126
|
+
|
127
|
+
# Initialize state variables if not already set.
|
128
|
+
@last_gc_time ||= now
|
129
|
+
@op_count ||= 0
|
130
|
+
@last_gc_op_count ||= @op_count
|
131
|
+
@last_mem_check_time ||= now
|
132
|
+
|
133
|
+
# Calculate differences based on ops and time.
|
134
|
+
ops_since_last_gc = @op_count - @last_gc_op_count
|
135
|
+
time_since_last_gc = now - @last_gc_time
|
136
|
+
|
137
|
+
# Start with our two “cheap” conditions.
|
138
|
+
due = (ops_since_last_gc >= GC_OP_THRESHOLD) || (time_since_last_gc >= GC_INTERVAL) || Types::Allocator.gc_bytes_seen > GC_BYTES_SEEN_THRESHOLD
|
139
|
+
|
140
|
+
if due
|
141
|
+
# Update the baseline values after GC is scheduled.
|
142
|
+
@last_gc_time = now
|
143
|
+
# If we just did a memory check, use that value; otherwise, fetch one now.
|
144
|
+
@last_gc_op_count = @op_count
|
145
|
+
Types::Allocator.gc_hint_reset!
|
146
|
+
true
|
147
|
+
else
|
148
|
+
false
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def start_gc_thread!(lib)
|
153
|
+
Thread.new do
|
154
|
+
loop do
|
155
|
+
schedule_work!(lib, :gc, :void, blocking: true, async: false, lib: lib) if gc_due?
|
156
|
+
sleep GC_INTERVAL
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
105
161
|
def thread_id
|
106
162
|
Thread.current.object_id
|
107
163
|
end
|
@@ -116,14 +172,14 @@ module CrystalRuby
|
|
116
172
|
yield!(lib: lib, time: 0)
|
117
173
|
end
|
118
174
|
|
119
|
-
def invoke_blocking!(receiver, op_name, *args, tvars)
|
175
|
+
def invoke_blocking!(receiver, op_name, *args, tvars, _lib)
|
120
176
|
tvars[:error] = nil
|
121
177
|
begin
|
122
178
|
tvars[:result] = receiver.send(op_name, *args)
|
123
|
-
rescue StopReactor
|
179
|
+
rescue StopReactor
|
124
180
|
tvars[:cond].signal
|
125
181
|
raise
|
126
|
-
rescue
|
182
|
+
rescue => e
|
127
183
|
tvars[:error] = e
|
128
184
|
end
|
129
185
|
tvars[:cond].signal
|
@@ -138,9 +194,10 @@ module CrystalRuby
|
|
138
194
|
if @single_thread_mode || (Thread.current.object_id == @main_thread_id && op_name != :yield)
|
139
195
|
unless Thread.current.object_id == @main_thread_id
|
140
196
|
raise SingleThreadViolation,
|
141
|
-
|
142
|
-
|
197
|
+
"Single thread mode is enabled, cannot run in multi-threaded mode. " \
|
198
|
+
"Reactor was started from: #{@main_thread_id}, then called from #{Thread.current.object_id}"
|
143
199
|
end
|
200
|
+
invoke_gc_if_due!(lib)
|
144
201
|
return receiver.send(op_name, *args)
|
145
202
|
end
|
146
203
|
|
@@ -149,7 +206,7 @@ module CrystalRuby
|
|
149
206
|
REACTOR_QUEUE.push(
|
150
207
|
case true
|
151
208
|
when async then [:invoke_async!, receiver, op_name, *args, tvars[:thread_id], CALLBACKS_MAP[return_type], lib]
|
152
|
-
when blocking then [:invoke_blocking!, receiver, op_name, *args, tvars]
|
209
|
+
when blocking then [:invoke_blocking!, receiver, op_name, *args, tvars, lib]
|
153
210
|
else [:invoke_await!, receiver, op_name, *args, lib]
|
154
211
|
end
|
155
212
|
)
|
@@ -4,12 +4,12 @@ module CrystalRuby
|
|
4
4
|
|
5
5
|
# Reads code line by line from a given source location and returns the first valid Ruby expression found
|
6
6
|
def extract_expr_from_source_location(source_location)
|
7
|
-
lines = source_location.then{|f,l| IO.readlines(f)[l-1..]}
|
7
|
+
lines = source_location.then { |f, l| IO.readlines(f)[l - 1..] }
|
8
8
|
lines[0] = lines[0][/CRType.*/] if lines[0] =~ /<\s+CRType/ || lines[0] =~ /= CRType/
|
9
9
|
lines.each.with_object([]) do |line, expr_source|
|
10
|
-
break expr_source.join("") if
|
10
|
+
break expr_source.join("") if Prism.parse((expr_source << line).join("")).success?
|
11
11
|
end
|
12
|
-
rescue
|
12
|
+
rescue StandardError
|
13
13
|
raise "Failed to extract expression from source location: #{source_location}. Ensure the file exists and the line number is correct. Extraction from a REPL is not supported"
|
14
14
|
end
|
15
15
|
|
@@ -27,26 +27,25 @@ module CrystalRuby
|
|
27
27
|
block_source = extract_expr_from_source_location(block.source_location)
|
28
28
|
parsed_source = Prism.parse(block_source).value
|
29
29
|
|
30
|
-
node = parsed_source.statements.body[0].arguments&.arguments&.find{|x| search_node(x, Prism::StatementsNode) }
|
30
|
+
node = parsed_source.statements.body[0].arguments&.arguments&.find { |x| search_node(x, Prism::StatementsNode) }
|
31
31
|
node ||= parsed_source.statements.body[0]
|
32
|
-
body_node =
|
32
|
+
body_node = search_node(node, Prism::StatementsNode)
|
33
33
|
|
34
|
-
|
35
|
-
extract_raw_string_node(body_node) :
|
36
|
-
node_to_s(body_node)
|
34
|
+
raw ? extract_raw_string_node(body_node) : node_to_s(body_node)
|
37
35
|
end
|
38
36
|
|
39
37
|
def extract_raw_string_node(node)
|
40
|
-
search_node(node, Prism::InterpolatedStringNode)&.parts&.map
|
41
|
-
|
38
|
+
search_node(node, Prism::InterpolatedStringNode)&.parts&.map do |p|
|
39
|
+
p.respond_to?(:unescaped) ? p.unescaped : p.slice
|
40
|
+
end&.join("") ||
|
41
|
+
search_node(node, Prism::StringNode).unescaped
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
44
|
# Simple helper function to turn a SyntaxTree node back into a Ruby string
|
46
45
|
# The default formatter will turn a break/return of [1,2,3] into a brackless 1,2,3
|
47
46
|
# Can't have that in Crystal as it turns it into a Tuple
|
48
47
|
def node_to_s(node)
|
49
|
-
node&.slice ||
|
48
|
+
node&.slice || ""
|
50
49
|
end
|
51
50
|
|
52
51
|
# Given a method, extracts the source code of the block passed to it
|
@@ -69,14 +68,14 @@ module CrystalRuby
|
|
69
68
|
def extract_args_and_source_from_method(method, raw: false)
|
70
69
|
method_source = extract_expr_from_source_location(method.source_location)
|
71
70
|
parsed_source = Prism.parse(method_source).value
|
72
|
-
params =
|
73
|
-
args = params ? params.keywords.map{|kw| [kw.name, node_to_s(kw.value)] }.to_h : {}
|
74
|
-
body_node =
|
71
|
+
params = search_node(parsed_source, Prism::ParametersNode)
|
72
|
+
args = params ? params.keywords.map { |kw| [kw.name, node_to_s(kw.value)] }.to_h : {}
|
73
|
+
body_node = parsed_source.statements.body[0].body
|
75
74
|
if body_node.respond_to?(:rescue_clause) && body_node.rescue_clause
|
76
|
-
wrapped = %
|
75
|
+
wrapped = %(begin\n#{body_node.statements.slice}\n#{body_node.rescue_clause.slice}\nend)
|
77
76
|
body_node = Prism.parse(wrapped).value
|
78
77
|
end
|
79
|
-
body = raw ?
|
78
|
+
body = raw ? extract_raw_string_node(body_node) : node_to_s(body_node)
|
80
79
|
|
81
80
|
args.transform_values! do |type_exp|
|
82
81
|
if CrystalRuby::Typemaps::CRYSTAL_TYPE_MAP.key?(type_exp[1..-1].to_sym)
|
@@ -85,8 +84,7 @@ module CrystalRuby
|
|
85
84
|
TypeBuilder.build_from_source(type_exp, context: method.owner)
|
86
85
|
end
|
87
86
|
end.to_h
|
88
|
-
|
87
|
+
[args, body]
|
89
88
|
end
|
90
|
-
|
91
89
|
end
|
92
90
|
end
|
@@ -25,9 +25,12 @@ module CrystalRuby
|
|
25
25
|
return if self.initialized
|
26
26
|
self.initialized = true
|
27
27
|
argv_ptr = ARGV1.to_unsafe
|
28
|
+
{%% if compare_versions(Crystal::VERSION, "1.16.0") >= 0 %%}
|
29
|
+
Crystal.init_runtime
|
30
|
+
{%% end %%}
|
28
31
|
Crystal.main_user_code(0, pointerof(argv_ptr))
|
29
32
|
self.libname = String.new(libname)
|
30
|
-
|
33
|
+
GC.init
|
31
34
|
end
|
32
35
|
|
33
36
|
# Explicit error handling (triggers exception within Ruby on the same thread)
|
@@ -57,7 +60,7 @@ module CrystalRuby
|
|
57
60
|
self.callbacks.send(callback)
|
58
61
|
end
|
59
62
|
|
60
|
-
def self.synchronize
|
63
|
+
def self.synchronize(&)
|
61
64
|
LibC.pthread_mutex_lock(self.rc_mux)
|
62
65
|
yield
|
63
66
|
LibC.pthread_mutex_unlock(self.rc_mux)
|
@@ -100,6 +103,11 @@ module GC
|
|
100
103
|
end
|
101
104
|
end
|
102
105
|
|
106
|
+
# Trigger GC
|
107
|
+
fun gc : Void
|
108
|
+
GC.collect
|
109
|
+
end
|
110
|
+
|
103
111
|
# Yield to the Crystal scheduler from Ruby
|
104
112
|
# If there's callbacks to process, we flush them
|
105
113
|
# Otherwise, we yield to the Crystal scheduler and let Ruby know
|
data/lib/crystalruby/typemaps.rb
CHANGED
@@ -3,31 +3,30 @@
|
|
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)
|
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
|
29
|
-
pointer: "Pointer(Void)" # Pointer type
|
30
|
-
|
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
|
+
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
|
29
|
+
pointer: "::Pointer(Void)" # Pointer type
|
31
30
|
}
|
32
31
|
|
33
32
|
FFI_TYPE_MAP = CRYSTAL_TYPE_MAP.invert
|
@@ -61,13 +60,13 @@ module CrystalRuby
|
|
61
60
|
|
62
61
|
C_TYPE_MAP = CRYSTAL_TYPE_MAP.merge(
|
63
62
|
{
|
64
|
-
string: "Pointer(UInt8)"
|
63
|
+
string: "Pointer(::UInt8)"
|
65
64
|
}
|
66
65
|
)
|
67
66
|
|
68
67
|
C_TYPE_CONVERSIONS = {
|
69
68
|
string: {
|
70
|
-
from: "String.new(%s)",
|
69
|
+
from: "::String.new(%s.not_nil!)",
|
71
70
|
to: "%s.to_unsafe"
|
72
71
|
},
|
73
72
|
void: {
|
@@ -86,6 +85,11 @@ module CrystalRuby
|
|
86
85
|
|
87
86
|
def build_type_map(crystalruby_type)
|
88
87
|
crystalruby_type = CRType(&crystalruby_type) if crystalruby_type.is_a?(Proc)
|
88
|
+
|
89
|
+
if Types::Type.subclass?(crystalruby_type) && crystalruby_type.ffi_primitive_type
|
90
|
+
crystalruby_type = crystalruby_type.ffi_primitive_type
|
91
|
+
end
|
92
|
+
|
89
93
|
{
|
90
94
|
ffi_type: ffi_type(crystalruby_type),
|
91
95
|
ffi_ret_type: ffi_type(crystalruby_type),
|
@@ -93,19 +97,25 @@ module CrystalRuby
|
|
93
97
|
crystalruby_type: crystalruby_type,
|
94
98
|
lib_type: lib_type(crystalruby_type),
|
95
99
|
error_value: error_value(crystalruby_type),
|
96
|
-
arg_mapper: if
|
100
|
+
arg_mapper: if Types::Type.subclass?(crystalruby_type)
|
97
101
|
lambda { |arg|
|
98
102
|
arg = crystalruby_type.new(arg.memory) if arg.is_a?(Types::Type) && !arg.is_a?(crystalruby_type)
|
99
103
|
arg = crystalruby_type.new(arg) unless arg.is_a?(Types::Type)
|
104
|
+
|
105
|
+
Types::FixedWidth.increment_ref_count!(arg.memory) if arg.class < Types::FixedWidth
|
106
|
+
|
100
107
|
arg
|
101
108
|
}
|
102
109
|
end,
|
103
|
-
retval_mapper: if
|
110
|
+
retval_mapper: if Types::Type.subclass?(crystalruby_type)
|
104
111
|
lambda { |arg|
|
105
112
|
if arg.is_a?(Types::Type) && !arg.is_a?(crystalruby_type)
|
106
113
|
arg = crystalruby_type.new(arg.memory)
|
107
114
|
end
|
108
115
|
arg = crystalruby_type.new(arg) unless arg.is_a?(Types::Type)
|
116
|
+
|
117
|
+
Types::FixedWidth.decrement_ref_count!(arg.memory) if arg.class < Types::FixedWidth
|
118
|
+
|
109
119
|
crystalruby_type.anonymous? ? arg.native : arg
|
110
120
|
}
|
111
121
|
# Strings in Crystal are UTF-8 encoded by default
|
@@ -2,9 +2,21 @@ module CrystalRuby
|
|
2
2
|
module Types
|
3
3
|
# Module for memory allocation and tracking functionality
|
4
4
|
module Allocator
|
5
|
-
|
6
5
|
# Called when module is included in a class
|
7
6
|
# @param base [Class] The class including this module
|
7
|
+
|
8
|
+
def self.gc_hint!(size)
|
9
|
+
@bytes_seen_since_gc = (@bytes_seen_since_gc || 0) + size
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.gc_bytes_seen
|
13
|
+
@bytes_seen_since_gc ||= 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.gc_hint_reset!
|
17
|
+
@bytes_seen_since_gc = 0
|
18
|
+
end
|
19
|
+
|
8
20
|
def self.included(base)
|
9
21
|
base.class_eval do
|
10
22
|
# Synchronizes a block using mutex
|
@@ -21,7 +33,7 @@ module CrystalRuby
|
|
21
33
|
|
22
34
|
extend FFI::Library
|
23
35
|
ffi_lib "c"
|
24
|
-
attach_function :_calloc, :calloc, [
|
36
|
+
attach_function :_calloc, :calloc, %i[size_t size_t], :pointer
|
25
37
|
attach_function :_free, :free, [:pointer], :void
|
26
38
|
define_singleton_method(:ptr, &FFI::Pointer.method(:new))
|
27
39
|
define_method(:ptr, &FFI::Pointer.method(:new))
|
@@ -4,7 +4,8 @@ module CrystalRuby::Types
|
|
4
4
|
"and a single return type (or Nil if it does not return a value)")
|
5
5
|
|
6
6
|
def self.Proc(*types)
|
7
|
-
proc_type = FixedWidth.build(:Proc, convert_if: [::Proc], inner_types: types,
|
7
|
+
proc_type = FixedWidth.build(:Proc, convert_if: [::Proc], inner_types: types,
|
8
|
+
ffi_type: :pointer) do
|
8
9
|
@data_offset = 4
|
9
10
|
|
10
11
|
def self.cast!(rbval)
|
@@ -57,12 +58,12 @@ module CrystalRuby::Types
|
|
57
58
|
result = nil
|
58
59
|
if Fiber.current == Thread.current.main_fiber
|
59
60
|
block_value = #{inner_types[-1].crystal_class_name}.new(__yield_to.call(#{inner_types.size.-(1).times.map { |i| "v#{i}" }.join(",")}))
|
60
|
-
result = #{inner_types[-1].anonymous? ? "block_value.
|
61
|
+
result = #{inner_types[-1].anonymous? ? "block_value.native_decr" : "block_value"}
|
61
62
|
next #{inner_types.last == CrystalRuby::Types::Nil ? "result" : "result.not_nil!"}
|
62
63
|
else
|
63
64
|
CrystalRuby.queue_callback(->{
|
64
65
|
block_value = #{inner_types[-1].crystal_class_name}.new(__yield_to.call(#{inner_types.size.-(1).times.map { |i| "v#{i}" }.join(",")}))
|
65
|
-
result = #{inner_types[-1].anonymous? ? "block_value.
|
66
|
+
result = #{inner_types[-1].anonymous? ? "block_value.native_decr" : "block_value"}
|
66
67
|
callback_done_channel.send(nil)
|
67
68
|
})
|
68
69
|
end
|
@@ -57,6 +57,11 @@ module CrystalRuby::Types
|
|
57
57
|
union_types
|
58
58
|
end
|
59
59
|
|
60
|
+
def total_memsize
|
61
|
+
type = self.class.union_types[data_pointer.read_uint8]
|
62
|
+
memsize + refsize + (type.primitive? ? type.memsize : value.total_memsize)
|
63
|
+
end
|
64
|
+
|
60
65
|
define_singleton_method(:memsize) do
|
61
66
|
union_types.map(&:refsize).max + 1
|
62
67
|
end
|
@@ -35,6 +35,17 @@ module CrystalRuby
|
|
35
35
|
8
|
36
36
|
end
|
37
37
|
|
38
|
+
def self.new_decr(arg)
|
39
|
+
new_value = self.new(arg)
|
40
|
+
self.decrement_ref_count!(new_value.memory)
|
41
|
+
new_value
|
42
|
+
end
|
43
|
+
|
44
|
+
def native_decr
|
45
|
+
self.class.decrement_ref_count!(@memory)
|
46
|
+
native
|
47
|
+
end
|
48
|
+
|
38
49
|
|
39
50
|
def self.free!(memory)
|
40
51
|
# Decrease ref counts for any data we are pointing to
|
@@ -66,6 +77,13 @@ module CrystalRuby
|
|
66
77
|
memory.as(Pointer(::UInt32))[0] = value
|
67
78
|
end
|
68
79
|
|
80
|
+
# When we pass to Ruby, we increment the ref count
|
81
|
+
# for Ruby to decrement again once it receives.
|
82
|
+
def return_value
|
83
|
+
FixedWidth.increment_ref_count!(memory)
|
84
|
+
memory
|
85
|
+
end
|
86
|
+
|
69
87
|
# Data pointer follows the ref count (and size for variable width types)
|
70
88
|
# In the case of variable width types the data pointer points to the start of a separate data block
|
71
89
|
# So this method is overridden inside variable_width.rb to resolve this pointer.
|
@@ -12,13 +12,14 @@ module CrystalRuby
|
|
12
12
|
else allocate_new_from_value!(rbval)
|
13
13
|
end
|
14
14
|
self.class.increment_ref_count!(memory)
|
15
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(memory))
|
15
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(memory, self.class))
|
16
|
+
Allocator.gc_hint!(total_memsize)
|
16
17
|
end
|
17
18
|
|
18
|
-
def self.finalize(memory)
|
19
|
-
lambda
|
19
|
+
def self.finalize(memory, type)
|
20
|
+
lambda do |_|
|
20
21
|
decrement_ref_count!(memory)
|
21
|
-
|
22
|
+
end
|
22
23
|
end
|
23
24
|
|
24
25
|
def allocate_new_from_value!(rbval)
|
@@ -137,6 +138,10 @@ module CrystalRuby
|
|
137
138
|
memory[size_offset].read_int32
|
138
139
|
end
|
139
140
|
|
141
|
+
def total_memsize
|
142
|
+
memsize + refsize + size
|
143
|
+
end
|
144
|
+
|
140
145
|
def address
|
141
146
|
@memory.address
|
142
147
|
end
|
@@ -180,6 +185,7 @@ module CrystalRuby
|
|
180
185
|
superclass: FixedWidth,
|
181
186
|
size_offset: 4,
|
182
187
|
data_offset: 4,
|
188
|
+
ffi_primitive: false,
|
183
189
|
&block
|
184
190
|
)
|
185
191
|
inner_types&.each(&Type.method(:validate!))
|
@@ -187,7 +193,7 @@ module CrystalRuby
|
|
187
193
|
Class.new(superclass) do
|
188
194
|
bind_local_vars!(
|
189
195
|
%i[typename error inner_types inner_keys ffi_type memsize convert_if size_offset data_offset
|
190
|
-
refsize], binding
|
196
|
+
refsize ffi_primitive], binding
|
191
197
|
)
|
192
198
|
class_eval(&block) if block_given?
|
193
199
|
|
@@ -60,11 +60,12 @@ module CrystalRuby
|
|
60
60
|
memsize: FFI.type_size(ffi_type),
|
61
61
|
convert_if: [],
|
62
62
|
error: nil,
|
63
|
+
ffi_primitive: false,
|
63
64
|
superclass: Primitive,
|
64
65
|
&block
|
65
66
|
)
|
66
67
|
Class.new(superclass) do
|
67
|
-
%w[typename ffi_type memsize convert_if error].each do |name|
|
68
|
+
%w[typename ffi_type memsize convert_if error ffi_primitive].each do |name|
|
68
69
|
define_singleton_method(name) { binding.local_variable_get("#{name}") }
|
69
70
|
define_method(name) { binding.local_variable_get("#{name}") }
|
70
71
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module CrystalRuby::Types
|
2
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
|
4
|
-
const_set(type_name, Primitive.build(type_name, convert_if: [::Numeric], ffi_type: ffi_type) do
|
3
|
+
ffi_type = CrystalRuby::Typemaps::FFI_TYPE_MAP.fetch("::#{type_name}")
|
4
|
+
const_set(type_name, Primitive.build(type_name, convert_if: [::Numeric], ffi_type: ffi_type, ffi_primitive: ffi_type) do
|
5
5
|
def value=(val)
|
6
6
|
raise "Expected a numeric value, got #{val}" unless val.is_a?(::Numeric)
|
7
7
|
|
@@ -47,7 +47,7 @@ module CrystalRuby
|
|
47
47
|
:write_mixed_byte_slices_to_uint8_array, :data_offset, :size_offset,
|
48
48
|
:union_types
|
49
49
|
|
50
|
-
attr_accessor :value, :memory
|
50
|
+
attr_accessor :value, :memory, :ffi_primitive
|
51
51
|
|
52
52
|
def initialize(_rbval)
|
53
53
|
@class = self.class
|
@@ -133,7 +133,7 @@ module CrystalRuby
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def self.pointer_to_crystal_type_conversion(expr)
|
136
|
-
anonymous? ? "#{crystal_class_name}.new(#{expr}).
|
136
|
+
anonymous? ? "#{crystal_class_name}.new(#{expr}).native_decr" : "#{crystal_class_name}.new_decr(#{expr})"
|
137
137
|
end
|
138
138
|
|
139
139
|
def self.crystal_type_to_pointer_type_conversion(expr)
|
@@ -243,6 +243,17 @@ module CrystalRuby
|
|
243
243
|
inner_types.map(&:memsize).sum
|
244
244
|
end
|
245
245
|
|
246
|
+
def total_memsize
|
247
|
+
memsize
|
248
|
+
end
|
249
|
+
|
250
|
+
# For non-container ffi_primitive non-named types,
|
251
|
+
# just use the raw FFI type, as it's much more efficient
|
252
|
+
# due to skipping Arc overhead.
|
253
|
+
def self.ffi_primitive_type
|
254
|
+
respond_to?(:ffi_primitive) && anonymous? ? ffi_primitive : nil
|
255
|
+
end
|
256
|
+
|
246
257
|
def self.crystal_type
|
247
258
|
lib_type(ffi_type)
|
248
259
|
end
|
@@ -265,6 +276,10 @@ module CrystalRuby
|
|
265
276
|
inner_types.first
|
266
277
|
end
|
267
278
|
|
279
|
+
def self.subclass?(type)
|
280
|
+
type.is_a?(Class) && type < Types::Type
|
281
|
+
end
|
282
|
+
|
268
283
|
def self.type_expr
|
269
284
|
if !inner_types
|
270
285
|
inspect_name
|
@@ -19,6 +19,7 @@ module CrystalRuby
|
|
19
19
|
inner_types: nil,
|
20
20
|
inner_keys: nil,
|
21
21
|
ffi_type: :pointer,
|
22
|
+
ffi_primitive: false,
|
22
23
|
size_offset: 4,
|
23
24
|
data_offset: 8,
|
24
25
|
memsize: FFI.type_size(ffi_type),
|
@@ -32,7 +33,7 @@ module CrystalRuby
|
|
32
33
|
Class.new(superclass) do
|
33
34
|
bind_local_vars!(
|
34
35
|
%i[typename error inner_types inner_keys ffi_type memsize convert_if data_offset size_offset
|
35
|
-
refsize], binding
|
36
|
+
refsize ffi_primitive], binding
|
36
37
|
)
|
37
38
|
class_eval(&block) if block_given?
|
38
39
|
end
|
data/lib/crystalruby/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crystalruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: digest
|
@@ -42,30 +42,36 @@ dependencies:
|
|
42
42
|
name: fileutils
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '1.7'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '1.7'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: prism
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.3.0
|
62
|
+
- - "<"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.5.0
|
62
65
|
type: :runtime
|
63
66
|
prerelease: false
|
64
67
|
version_requirements: !ruby/object:Gem::Requirement
|
65
68
|
requirements:
|
66
69
|
- - ">="
|
67
70
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
71
|
+
version: 1.3.0
|
72
|
+
- - "<"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.5.0
|
69
75
|
description: Embed Crystal code directly in Ruby.
|
70
76
|
email:
|
71
77
|
- wc@pico.net.nz
|
@@ -82,7 +88,6 @@ files:
|
|
82
88
|
- LICENSE.txt
|
83
89
|
- README.md
|
84
90
|
- Rakefile
|
85
|
-
- crystalruby.gemspec
|
86
91
|
- examples/adder/adder.rb
|
87
92
|
- exe/crystalruby
|
88
93
|
- lib/crystalruby.rb
|
data/crystalruby.gemspec
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/crystalruby/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "crystalruby"
|
7
|
-
spec.version = CrystalRuby::VERSION
|
8
|
-
spec.authors = ["Wouter Coppieters"]
|
9
|
-
spec.email = ["wc@pico.net.nz"]
|
10
|
-
|
11
|
-
spec.summary = "Embed Crystal code directly in Ruby."
|
12
|
-
spec.description = "Embed Crystal code directly in Ruby."
|
13
|
-
spec.homepage = "https://github.com/wouterken/crystalruby"
|
14
|
-
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = ">= 2.7.2"
|
16
|
-
|
17
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
-
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
|
20
|
-
|
21
|
-
# Specify which files should be added to the gem when it is released.
|
22
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
-
spec.files = Dir.chdir(__dir__) do
|
24
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
-
(File.expand_path(f) == __FILE__) ||
|
26
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
|
27
|
-
end
|
28
|
-
end
|
29
|
-
spec.bindir = "exe"
|
30
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
-
spec.require_paths = ["lib"]
|
32
|
-
|
33
|
-
# Uncomment to register a new dependency of your gem
|
34
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
35
|
-
spec.add_dependency "digest"
|
36
|
-
spec.add_dependency "ffi"
|
37
|
-
spec.add_dependency "fileutils"
|
38
|
-
spec.add_dependency "prism"
|
39
|
-
# For more information and examples about making a new gem, check out our
|
40
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
41
|
-
end
|