crystalruby 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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}")
|