crystalruby 0.1.6 → 0.1.7
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/README.md +2 -3
- data/lib/crystalruby/templates/function.cr +9 -0
- data/lib/crystalruby/templates/index.cr +27 -19
- data/lib/crystalruby/version.rb +1 -1
- data/lib/crystalruby.rb +42 -27
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe68e58781b8314afb53b268ba9314472ee93c611565f7398d0ff29b445f8ece
|
4
|
+
data.tar.gz: acd5e80d5709cc6f831daa39f58f74fe95642e16a4c44d25c8fab24106b47bcd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85516d1372b2467147cb5c61e6d33dc27e6e2c0d11876b32dbee1007152096fd54551926177b56e066fa9bc8e4329b8ef13d133280305018428775267eb4a24e
|
7
|
+
data.tar.gz: 207284740bef726d1a8c5df35b21ebe33b8611725363acac451bbadc3b7c714585513027be2398dd4cdd6ff94761c00585c94546fb198a4b90047d91edbef2ce
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@ require 'crystalruby'
|
|
25
25
|
module MyTestModule
|
26
26
|
# The below method will be replaced by a compiled Crystal version
|
27
27
|
# linked using FFI.
|
28
|
-
crystalize [:int, :int] => :int
|
28
|
+
crystalize [a: :int, b: :int] => :int
|
29
29
|
def add(a, b)
|
30
30
|
a + b
|
31
31
|
end
|
@@ -151,7 +151,7 @@ require 'crystalruby'
|
|
151
151
|
module Adder
|
152
152
|
crystalize [a: :int, b: :int] => :int
|
153
153
|
def add(a, b)
|
154
|
-
a + b
|
154
|
+
a + b
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
@@ -349,7 +349,6 @@ It should support escape hatches to allow it to coexist with code that performs
|
|
349
349
|
|
350
350
|
The library is currently in its infancy. Planned additions are:
|
351
351
|
|
352
|
-
- Replace existing checksum process, with one that combines results of inline and external crystal to more accurately detect when recompilation is necessary.
|
353
352
|
- Simple mixin/concern that utilises `FFI::Struct` for bi-directional passing of Ruby objects and Crystal objects (by value).
|
354
353
|
- Install command to generate a sample build script, and supports build command (which simply verifies then invokes this script)
|
355
354
|
- Call Ruby from Crystal using FFI callbacks (implement `.expose_to_crystal`)
|
@@ -1,9 +1,18 @@
|
|
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
|
+
|
1
6
|
module %{module_name}
|
2
7
|
def self.%{fn_name}(%{fn_args}) : %{fn_ret_type}
|
3
8
|
%{fn_body}
|
4
9
|
end
|
5
10
|
end
|
6
11
|
|
12
|
+
# This function is the entry point for the CrystalRuby code, exposed through FFI.
|
13
|
+
# We apply some basic error handling here, and convert the arguments and return values
|
14
|
+
# to ensure that we are using Crystal native types.
|
15
|
+
|
7
16
|
fun %{lib_fn_name}(%{lib_fn_args}): %{lib_fn_ret_type}
|
8
17
|
begin
|
9
18
|
%{convert_lib_args}
|
@@ -1,38 +1,46 @@
|
|
1
1
|
FAKE_ARG = "crystal"
|
2
|
-
|
2
|
+
|
3
|
+
alias ErrorCallback = (Pointer(UInt8), Pointer(UInt8) -> Void)
|
3
4
|
|
4
5
|
module CrystalRuby
|
6
|
+
# Initializing Crystal Ruby invokes init on the Crystal garbage collector.
|
7
|
+
# We need to be sure to only do this once.
|
5
8
|
@@initialized = false
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
|
10
|
+
# We won't natively handle Crystal Exceptions in Ruby
|
11
|
+
# Instead, we'll catch them in Crystal, and explicitly expose them to Ruby via
|
12
|
+
# the error_callback.
|
13
|
+
@@error_callback
|
14
|
+
|
15
|
+
# This is the entry point for instantiating CrystalRuby
|
16
|
+
# We:
|
17
|
+
# 1. Initialize the Crystal garbage collector
|
18
|
+
# 2. Set the error callback
|
19
|
+
# 3. Call the Crystal main function
|
20
|
+
def self.init(error_callback : ErrorCallback)
|
21
|
+
return if @@initialized
|
11
22
|
GC.init
|
23
|
+
@@initialized = true
|
24
|
+
@@error_callback = error_callback
|
12
25
|
ptr = FAKE_ARG.to_unsafe
|
13
26
|
LibCrystalMain.__crystal_main(1, pointerof(ptr))
|
14
27
|
end
|
15
28
|
|
16
|
-
|
17
|
-
@@rb_error_handler = cb
|
18
|
-
end
|
19
|
-
|
29
|
+
# Explicit error handling (triggers exception within Ruby on the same thread)
|
20
30
|
def self.report_error(error_type : String, str : String)
|
21
|
-
handler = @@
|
22
|
-
if handler
|
31
|
+
if handler = @@error_callback
|
23
32
|
handler.call(error_type.to_unsafe, str.to_unsafe)
|
24
33
|
end
|
25
34
|
end
|
26
35
|
end
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
CrystalRuby.init
|
31
|
-
end
|
32
|
-
|
33
|
-
fun attach_rb_error_handler(cb : Callback) : Void
|
34
|
-
CrystalRuby.attach_rb_error_handler(cb)
|
37
|
+
fun init(cb : ErrorCallback): Void
|
38
|
+
CrystalRuby.init(cb)
|
35
39
|
end
|
36
40
|
|
41
|
+
# This is where we define all our Crystal modules and types
|
42
|
+
# derived from their Ruby counterparts.
|
37
43
|
%{type_modules}
|
44
|
+
|
45
|
+
# Require all generated crystal files
|
38
46
|
%{requires}
|
data/lib/crystalruby/version.rb
CHANGED
data/lib/crystalruby.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "ffi"
|
2
4
|
require "digest"
|
3
5
|
require "fileutils"
|
@@ -10,6 +12,7 @@ require_relative "crystalruby/typebuilder"
|
|
10
12
|
require_relative "crystalruby/template"
|
11
13
|
|
12
14
|
module CrystalRuby
|
15
|
+
CR_SRC_FILES_PATTERN = "./**/*.cr"
|
13
16
|
# Define a method to set the @crystalize proc if it doesn't already exist
|
14
17
|
def crystalize(type = :src, **options, &block)
|
15
18
|
(args,), returns = options.first
|
@@ -29,14 +32,6 @@ module CrystalRuby
|
|
29
32
|
crtype(&block).serialize_as(:json)
|
30
33
|
end
|
31
34
|
|
32
|
-
def with_temporary_constant(constant_name, new_value)
|
33
|
-
old_value = const_get(constant_name)
|
34
|
-
const_set(constant_name, new_value)
|
35
|
-
yield
|
36
|
-
ensure
|
37
|
-
const_set(constant_name, old_value)
|
38
|
-
end
|
39
|
-
|
40
35
|
def method_added(method_name)
|
41
36
|
if @crystalize_next
|
42
37
|
attach_crystalized_method(method_name)
|
@@ -64,8 +59,7 @@ module CrystalRuby
|
|
64
59
|
CrystalRuby.write_function(self, name: function[:name], body: function[:body]) do
|
65
60
|
extend FFI::Library
|
66
61
|
ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
|
67
|
-
attach_function
|
68
|
-
attach_function "init!", "init", [], :void
|
62
|
+
attach_function method_name, fname, function[:ffi_types], function[:return_ffi_type]
|
69
63
|
if block
|
70
64
|
[singleton_class, self].each do |receiver|
|
71
65
|
receiver.prepend(Module.new do
|
@@ -73,8 +67,6 @@ module CrystalRuby
|
|
73
67
|
end)
|
74
68
|
end
|
75
69
|
end
|
76
|
-
|
77
|
-
init!
|
78
70
|
end
|
79
71
|
|
80
72
|
[singleton_class, self].each do |receiver|
|
@@ -219,6 +211,9 @@ module CrystalRuby
|
|
219
211
|
"require \"./#{config.crystal_codegen_dir}/index\"\n"
|
220
212
|
)
|
221
213
|
end
|
214
|
+
|
215
|
+
attach_crystal_ruby_lib! if compiled?
|
216
|
+
|
222
217
|
return if File.exist?("#{config.crystal_src_dir}/shard.yml")
|
223
218
|
|
224
219
|
IO.write("#{config.crystal_src_dir}/shard.yml", <<~CRYSTAL)
|
@@ -227,11 +222,25 @@ module CrystalRuby
|
|
227
222
|
CRYSTAL
|
228
223
|
end
|
229
224
|
|
225
|
+
def attach_crystal_ruby_lib!
|
226
|
+
extend FFI::Library
|
227
|
+
ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
|
228
|
+
attach_function "init!", :init, [:pointer], :void
|
229
|
+
const_set(:ErrorCallback, FFI::Function.new(:void, %i[string string]) do |error_type, message|
|
230
|
+
error_type = error_type.to_sym
|
231
|
+
is_exception_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception)
|
232
|
+
error_type = is_exception_type ? Object.const_get(error_type) : RuntimeError
|
233
|
+
raise error_type.new(message)
|
234
|
+
end)
|
235
|
+
init!(ErrorCallback)
|
236
|
+
end
|
237
|
+
|
230
238
|
def self.instantiated?
|
231
239
|
@instantiated
|
232
240
|
end
|
233
241
|
|
234
242
|
def self.compiled?
|
243
|
+
@compiled = get_current_crystal_lib_digest == get_cr_src_files_digest unless defined?(@compiled)
|
235
244
|
@compiled
|
236
245
|
end
|
237
246
|
|
@@ -293,21 +302,14 @@ module CrystalRuby
|
|
293
302
|
end
|
294
303
|
|
295
304
|
unless result = system(cmd)
|
296
|
-
File.delete(
|
305
|
+
File.delete(digest_file_name) if File.exist?(digest_file_name)
|
297
306
|
raise "Error compiling crystal code"
|
298
307
|
end
|
299
|
-
@compiled = true
|
300
308
|
end
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
error_type = error_type.to_sym
|
306
|
-
is_exception_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception)
|
307
|
-
error_type = is_exception_type ? Object.const_get(error_type) : RuntimeError
|
308
|
-
raise error_type.new(message)
|
309
|
-
end)
|
310
|
-
attach_rb_error_handler(ErrorCallback)
|
309
|
+
|
310
|
+
IO.write(digest_file_name, get_cr_src_files_digest)
|
311
|
+
@compiled = true
|
312
|
+
attach_crystal_ruby_lib!
|
311
313
|
end
|
312
314
|
|
313
315
|
def self.attach!
|
@@ -317,10 +319,23 @@ module CrystalRuby
|
|
317
319
|
@attached = true
|
318
320
|
end
|
319
321
|
|
322
|
+
def self.get_cr_src_files_digest
|
323
|
+
file_digests = Dir.glob(CR_SRC_FILES_PATTERN).sort.map do |file_path|
|
324
|
+
content = File.read(file_path)
|
325
|
+
Digest::MD5.hexdigest(content)
|
326
|
+
end.join
|
327
|
+
Digest::MD5.hexdigest(file_digests)
|
328
|
+
end
|
329
|
+
|
330
|
+
def self.digest_file_name
|
331
|
+
@digest_file_name ||= "#{config.crystal_lib_dir}/#{config.crystal_lib_name}.digest"
|
332
|
+
end
|
333
|
+
|
334
|
+
def self.get_current_crystal_lib_digest
|
335
|
+
File.read(digest_file_name) if File.exist?(digest_file_name)
|
336
|
+
end
|
337
|
+
|
320
338
|
def self.write_function(owner, name:, body:, &compile_callback)
|
321
|
-
unless defined?(@compiled)
|
322
|
-
@compiled = File.exist?("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/index.cr")
|
323
|
-
end
|
324
339
|
@block_store ||= []
|
325
340
|
@block_store << { owner: owner, name: name, body: body, compile_callback: compile_callback }
|
326
341
|
FileUtils.mkdir_p("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}")
|