polycrystal 0.1.0-x86_64-darwin-21

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 227d59d440d83faa17b29c863d8803a81b030a1c1a3ad79f3a2998832dea8eef
4
+ data.tar.gz: c41911fafd5bf34ed1ec4d34918830eb8818ac55c2d6aa545f215e774f087673
5
+ SHA512:
6
+ metadata.gz: 34eee7c114470c57db1b612e42b5033d95ffde963119c17b552fbb935803d210bf193c8c46afdbf0f248e8ec85ba51f2c9b9f38f1d93b151fbce97f08e6c63d1
7
+ data.tar.gz: 72a84a0759b5f67ffbe05b335c2c225169eee942c688d61016d955dc2157e7793792abb826fd1e8a7ed312b71e8cf7d0e9a59c049205755cc0276f6825d70812
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ *_bak
13
+ *.bak
14
+ ext/polycrystal/*.o
15
+ ext/polycrystal/*.bundle
16
+ ext/polycrystal/mkmf.log
17
+ ext/polycrystal/Makefile
18
+ .byebug_history
19
+ /.vscode
20
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+ NewCops: enable
4
+ Exclude:
5
+ - spec/**/*
6
+ - sample_project/build/**/*
7
+ - ext/polycrystal/extconf.rb
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.8
7
+ before_install: gem install bundler -v 1.17.2
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in polycrystal.gemspec
8
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Andrii Hrushetskyi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Polycrystal
2
+
3
+ Integrate Crystal code into ruby sing the [Anyolite](https://github.com/Anyolite/anyolite)
4
+
5
+ Right now it uses a forked with tiny modifications to linking and initialization: https://github.com/ahrushetskyi/anyolite
6
+
7
+ ## Installation
8
+
9
+ ### Now (tested only on MacOS and ruby 3.0.4)
10
+
11
+ Clone repo with submodules
12
+
13
+ ```bash
14
+ git clone git@github.com:ahrushetskyi/polycrystal.git
15
+ # or
16
+ git clone https://github.com/ahrushetskyi/polycrystal.git
17
+ ```
18
+
19
+ Compile C extension and Anyolite glue objects
20
+
21
+ ```bash
22
+ cd ext/polycrystal
23
+ ruby extconf.rb
24
+ make
25
+ ```
26
+
27
+ Check out sample app:
28
+
29
+ ```bash
30
+ # from repository root
31
+ cd sample_project
32
+ irb -r ./sample
33
+ ```
34
+
35
+ then following should work in the irb console:
36
+
37
+ ```ruby
38
+ CrystalModule::CrystalClass.new.crystal_method
39
+ CrystalModule::CrystalClass.new.return_object
40
+ CrystalModule::CrystalClass.new.return_object.some_method
41
+ CrystalModule::CrystalClass.new.recv_arg(arg: 42)
42
+ ```
43
+
44
+ ### Planned
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ ```ruby
49
+ gem 'polycrystal'
50
+ ```
51
+
52
+ And then execute:
53
+
54
+ $ bundle
55
+
56
+ Or install it yourself as:
57
+
58
+ $ gem install polycrystal
59
+
60
+ ## Usage
61
+
62
+ ```ruby
63
+ require 'polycrystal'
64
+
65
+ Polycrystal::Registry.register(
66
+ # directory with crystal files, added to crystal CRYSTAL_PATH
67
+ path: File.expand_path("#{__dir__}/crystal"),
68
+ # file, that should be loaded. Transformed into `require "sample"` in crystal entrypoint, should be accessible from :path
69
+ file: 'sample.cr',
70
+ # This gets wrapped by Anyolite
71
+ modules: ['CrystalModule']
72
+ )
73
+
74
+ # directory for entrypoint and compiled library.
75
+ build_path = File.expand_path("#{__dir__}/build")
76
+ FileUtils.mkdir_p(build_path)
77
+
78
+ # cpmpile and load
79
+ # should be called once, after all Polycrystal::Registry#register calls
80
+ Polycrystal::Loader.new(build_path: build_path).load
81
+ ```
82
+
83
+ ## TODO
84
+
85
+ 1. Test suite
86
+ 2. Code should not be recompiled if it was not changed
87
+ 3. Ability to precompile all code on demand, e.g. for Docker images
88
+ 4. make shards work. Anyolite may be loaded from shards, if my patches are merged, or better alternatives implemented
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ git clone git@github.com:ahrushetskyi/polycrystal.git
94
+ # or
95
+ git clone https://github.com/ahrushetskyi/polycrystal.git
96
+ ```
97
+
98
+ ## Contributing
99
+
100
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/polycrystal.
101
+
102
+ ## License
103
+
104
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,288 @@
1
+ #include <ruby.h>
2
+
3
+ extern VALUE rb_define_class_under_helper(void* rb, VALUE under, const char* name, VALUE superclass) {
4
+
5
+ return rb_define_class_under(under, name, superclass);
6
+
7
+ }
8
+
9
+ extern VALUE rb_define_class_helper(void* rb, const char* name, VALUE superclass) {
10
+
11
+ return rb_define_class(name, superclass);
12
+
13
+ }
14
+
15
+ extern VALUE rb_define_module_under_helper(void* rb, VALUE under, const char* name) {
16
+
17
+ return rb_define_module_under(under, name);
18
+
19
+ }
20
+
21
+ extern VALUE rb_define_module_helper(void* rb, const char* name) {
22
+
23
+ return rb_define_module(name);
24
+
25
+ }
26
+
27
+ extern void rb_define_const_helper(void* rb, VALUE under, const char* name, VALUE value) {
28
+
29
+ return rb_define_const(under, name, value);
30
+
31
+ }
32
+
33
+ extern void set_instance_tt_as_data(VALUE ruby_class) {
34
+
35
+ //! TODO: Is this function required?
36
+ //MRB_SET_INSTANCE_TT(ruby_class, MRB_TT_DATA);
37
+
38
+ }
39
+
40
+ extern bool rb_obj_is_kind_of_helper(void* rb, VALUE object, VALUE ruby_class) {
41
+
42
+ return rb_obj_is_kind_of(object, ruby_class) == Qtrue ? true : false;
43
+
44
+ }
45
+
46
+ extern VALUE rb_obj_class_helper(void* rb, VALUE object) {
47
+
48
+ return rb_obj_class(object);
49
+
50
+ }
51
+
52
+ extern const char* rb_class_name_helper(void* rb, VALUE ruby_class) {
53
+
54
+ VALUE class_name_value = rb_class_name(ruby_class);
55
+ return rb_string_value_cstr(&class_name_value);
56
+
57
+ }
58
+
59
+ extern void* get_data_ptr(VALUE ruby_object) {
60
+
61
+ return DATA_PTR(ruby_object);
62
+
63
+ }
64
+
65
+ //! About the following method...
66
+ //! It is highly hacky and just modifies a newly creates Ruby object, but that is okay.
67
+ //! It tells the Ruby GC that this object is a pointer and how to free it.
68
+ //! Crystal owns the pointer, so Ruby does not have to do anything else besides calling dfree.
69
+
70
+ extern void set_data_ptr_and_type(VALUE ruby_object, void* data, struct rb_data_type_struct* data_type) {
71
+
72
+ DATA_PTR(ruby_object) = data;
73
+ RDATA(ruby_object)->basic.flags = T_DATA;
74
+ RDATA(ruby_object)->dmark = data_type->function.dmark;
75
+ RDATA(ruby_object)->dfree = data_type->function.dfree;
76
+
77
+ }
78
+
79
+ extern VALUE new_empty_object(void* rb, VALUE ruby_class, void* data_ptr, struct rb_data_type_struct* data_type) {
80
+
81
+ return rb_data_object_wrap(ruby_class, data_ptr, data_type->function.dmark, data_type->function.dfree);
82
+
83
+ }
84
+
85
+ extern void rb_define_method_helper(void* rb, VALUE ruby_class, const char* name, VALUE (*func)(int argc, VALUE* argv, VALUE self), int aspec) {
86
+
87
+ return rb_define_method(ruby_class, name, func, -1);
88
+
89
+ }
90
+
91
+ extern void rb_define_class_method_helper(void* rb, VALUE ruby_class, const char* name, VALUE (*func)(int argc, VALUE* argv, VALUE self), int aspec) {
92
+
93
+ return rb_define_module_function(ruby_class, name, func, -1);
94
+
95
+ }
96
+
97
+ extern void rb_define_module_function_helper(void* rb, VALUE ruby_module, const char* name, VALUE (*func)(int argc, VALUE* argv, VALUE self), int aspec) {
98
+
99
+ return rb_define_module_function(ruby_module, name, func, -1);
100
+
101
+ }
102
+
103
+ extern VALUE rb_inspect_helper(void* rb, VALUE value) {
104
+
105
+ return rb_inspect(value);
106
+
107
+ }
108
+
109
+ extern VALUE rb_hash_new_helper(void* rb) {
110
+
111
+ return rb_hash_new();
112
+
113
+ }
114
+
115
+ extern void rb_hash_set_helper(void* rb, VALUE hash, VALUE key, VALUE value) {
116
+
117
+ rb_hash_aset(hash, key, value);
118
+
119
+ }
120
+
121
+ extern VALUE rb_hash_get_helper(void* rb, VALUE hash, VALUE key) {
122
+
123
+ return rb_hash_aref(hash, key);
124
+
125
+ }
126
+
127
+ extern VALUE rb_hash_keys_helper(void* rb, VALUE hash) {
128
+
129
+ //! NOTE: For some reason rb_hash_keys is not marked as extern, so for now this is a workaround
130
+ return rb_funcall(hash, rb_intern("keys"), 0);
131
+
132
+ }
133
+
134
+ extern int rb_hash_size_helper(void* rb, VALUE hash) {
135
+
136
+ return (int) rb_hash_size(hash);
137
+
138
+ }
139
+
140
+ extern VALUE convert_to_rb_sym_helper(void* rb, const char* value) {
141
+
142
+ return rb_intern(value);
143
+
144
+ }
145
+
146
+ extern VALUE rb_ary_ref_helper(void* rb, VALUE ary, int pos) {
147
+
148
+ return rb_ary_entry(ary, pos);
149
+
150
+ }
151
+
152
+ extern size_t rb_ary_length_helper(VALUE ary) {
153
+
154
+ size_t return_value = (size_t) RARRAY_LEN(ary);
155
+ return return_value;
156
+ }
157
+
158
+ extern VALUE rb_ary_new_from_values_helper(void* rb, int size, VALUE* values) {
159
+
160
+ return rb_ary_new_from_values(size, values);
161
+
162
+ }
163
+
164
+ extern void rb_gc_register_helper(void* rb, VALUE value) {
165
+
166
+ return rb_gc_register_address(&value);
167
+
168
+ }
169
+
170
+ extern void rb_gc_unregister_helper(void* rb, VALUE value) {
171
+
172
+ return rb_gc_unregister_address(&value);
173
+
174
+ }
175
+
176
+ extern VALUE rb_yield_helper(void* rb, VALUE value, VALUE arg) {
177
+
178
+ return rb_yield(arg);
179
+
180
+ }
181
+
182
+ extern VALUE rb_yield_argv_helper(void* rb, VALUE value, int argc, VALUE* argv) {
183
+
184
+ return rb_yield_values2(argc, argv);
185
+
186
+ }
187
+
188
+ extern VALUE rb_call_block_helper(void* rb, VALUE value, VALUE arg) {
189
+
190
+ return rb_proc_call(value, rb_ary_new_from_values(1, &arg));
191
+
192
+ }
193
+
194
+ extern VALUE rb_call_block_with_args_helper(void* rb, VALUE value, int argc, VALUE* argv) {
195
+
196
+ return rb_proc_call(value, rb_ary_new_from_values(argc, argv));
197
+
198
+ }
199
+
200
+ extern bool rb_respond_to_helper(void* rb, VALUE obj, ID name) {
201
+
202
+ return rb_respond_to(obj, name);
203
+
204
+ }
205
+
206
+ extern VALUE get_rb_obj_value(VALUE obj) {
207
+
208
+ return obj;
209
+
210
+ }
211
+
212
+ extern VALUE rb_funcall_argv_helper(void *rb, VALUE value, ID name, int argc, VALUE* argv) {
213
+
214
+ return rb_funcallv(value, name, argc, argv);
215
+
216
+ }
217
+
218
+ extern VALUE rb_funcall_argv_with_block_helper(void *rb, VALUE value, ID name, int argc, VALUE* argv, VALUE block) {
219
+
220
+ return rb_funcall_with_block(value, name, argc, argv, block);
221
+
222
+ }
223
+
224
+ extern VALUE rb_iv_get_helper(void* rb, VALUE obj, ID sym) {
225
+
226
+ return rb_ivar_get(obj, sym);
227
+
228
+ }
229
+
230
+ extern void rb_iv_set_helper(void* rb, VALUE obj, ID sym, VALUE value) {
231
+
232
+ rb_ivar_set(obj, sym, value);
233
+
234
+ }
235
+
236
+ extern VALUE rb_cv_get_helper(void* rb, VALUE mod, ID sym) {
237
+
238
+ return rb_cvar_get(mod, sym);
239
+
240
+ }
241
+
242
+ extern void rb_cv_set_helper(void* rb, VALUE mod, ID sym, VALUE value) {
243
+
244
+ return rb_cvar_set(mod, sym, value);
245
+
246
+ }
247
+
248
+ extern VALUE rb_gv_get_helper(void* rb, const char* name) {
249
+
250
+ return rb_gv_get(name);
251
+
252
+ }
253
+
254
+ extern void rb_gv_set_helper(void* rb, const char* name, VALUE value) {
255
+
256
+ rb_gv_set(name, value);
257
+
258
+ }
259
+
260
+ extern bool does_constant_exist_under(void* rb, VALUE under, const char* name) {
261
+
262
+ return rb_const_defined_at(under, rb_intern(name)) == Qtrue ? 1 : 0;
263
+
264
+ }
265
+
266
+ extern bool does_constant_exist(void* rb, const char* name) {
267
+
268
+ return rb_const_defined(rb_cObject, rb_intern(name)) == Qtrue ? 1 : 0;
269
+
270
+ }
271
+
272
+ extern VALUE get_constant_under(void* rb, VALUE under, const char* name) {
273
+
274
+ return rb_const_get_at(under, rb_intern(name));
275
+
276
+ }
277
+
278
+ extern VALUE get_constant(void* rb, const char* name) {
279
+
280
+ return rb_const_get(rb_cObject, rb_intern(name));
281
+
282
+ }
283
+
284
+ extern void rb_undef_method_helper(void* rb, VALUE mod, const char* name) {
285
+
286
+ return rb_undef_method(mod, name);
287
+
288
+ }
@@ -0,0 +1,61 @@
1
+ #include <ruby.h>
2
+
3
+ extern void rb_raise_runtime_error(void* rb, const char* msg) {
4
+
5
+ rb_raise(rb_eRuntimeError, msg);
6
+
7
+ }
8
+
9
+ extern void rb_raise_type_error(void* rb, const char* msg) {
10
+
11
+ rb_raise(rb_eTypeError, msg);
12
+
13
+ }
14
+
15
+ extern void rb_raise_argument_error(void* rb, const char* msg) {
16
+
17
+ rb_raise(rb_eArgError, msg);
18
+
19
+ }
20
+
21
+ extern void rb_raise_index_error(void* rb, const char* msg) {
22
+
23
+ rb_raise(rb_eIndexError, msg);
24
+
25
+ }
26
+
27
+ extern void rb_raise_range_error(void* rb, const char* msg) {
28
+
29
+ rb_raise(rb_eRangeError, msg);
30
+
31
+ }
32
+
33
+ extern void rb_raise_name_error(void* rb, const char* msg) {
34
+
35
+ rb_raise(rb_eNameError, msg);
36
+
37
+ }
38
+
39
+ extern void rb_raise_script_error(void* rb, const char* msg) {
40
+
41
+ rb_raise(rb_eScriptError, msg);
42
+
43
+ }
44
+
45
+ extern void rb_raise_not_implemented_error(void* rb, const char* msg) {
46
+
47
+ rb_raise(rb_eNotImpError, msg);
48
+
49
+ }
50
+
51
+ extern void rb_raise_key_error(void* rb, const char* msg) {
52
+
53
+ rb_raise(rb_eKeyError, msg);
54
+
55
+ }
56
+
57
+ extern void rb_raise_helper(void* rb, VALUE exc, const char* msg) {
58
+
59
+ rb_raise(exc, msg);
60
+
61
+ }
@@ -0,0 +1,3 @@
1
+ #ifndef EXTCONF_H
2
+ #define EXTCONF_H
3
+ #endif
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf'
4
+
5
+ $LDFLAGS << ' -framework AppKit' if RUBY_PLATFORM =~ /darwin/
6
+
7
+ MakeMakefile::LINK_SO.sub!('$(OBJS)', 'polycrystal.o')
8
+
9
+ create_header
10
+ create_makefile 'polycrystal/polycrystal'
@@ -0,0 +1,38 @@
1
+ #include <ruby.h>
2
+ #include <dlfcn.h>
3
+ #include "extconf.h"
4
+
5
+ static void (*__polycrystal_init)() = NULL;
6
+ static void (*__polycrystal_module_run)() = NULL;
7
+
8
+ static VALUE load_library(VALUE _self, VALUE path){
9
+ char * cpath = StringValueCStr(path);
10
+ void * handle = dlopen(cpath, RTLD_NOW|RTLD_LOCAL);
11
+ if(handle != NULL){
12
+ __polycrystal_init = (void (*)())dlsym(handle, "__polycrystal_init");
13
+ if(__polycrystal_init != NULL){
14
+ __polycrystal_init();
15
+ }else{
16
+ rb_raise(rb_eRuntimeError, "Load __polycrystal_init failed from %s: %s", cpath, dlerror());
17
+ return Qnil;
18
+ }
19
+ __polycrystal_module_run = (void (*)())dlsym(handle, "__polycrystal_module_run");
20
+ if(__polycrystal_module_run != NULL){
21
+ __polycrystal_module_run();
22
+ }else{
23
+ rb_raise(rb_eRuntimeError, "Load __polycrystal_module_run failed from %s: %s", cpath, dlerror());
24
+ return Qnil;
25
+ }
26
+ }else{
27
+ rb_raise(rb_eRuntimeError, "Can't load library at %s: %s", cpath, dlerror());
28
+ return Qnil;
29
+ }
30
+ return Qtrue;
31
+ }
32
+
33
+
34
+ void Init_polycrystal() {
35
+ VALUE mod = rb_define_module("Polycrystal");
36
+ VALUE class = rb_define_class_under(mod, "Loader", rb_cObject);
37
+ rb_define_private_method(class, "load_library", load_library, 1);
38
+ }
@@ -0,0 +1,146 @@
1
+ #include <ruby.h>
2
+
3
+ extern VALUE get_object_class(void* rb) {
4
+
5
+ return rb_cObject;
6
+
7
+ }
8
+
9
+ extern VALUE get_nil_value() {
10
+
11
+ return Qnil;
12
+
13
+ }
14
+
15
+ extern VALUE get_false_value() {
16
+
17
+ return Qfalse;
18
+
19
+ }
20
+
21
+ extern VALUE get_true_value() {
22
+
23
+ return Qtrue;
24
+
25
+ }
26
+
27
+ extern VALUE get_fixnum_value(long value) {
28
+
29
+ return INT2FIX(value);
30
+
31
+ }
32
+
33
+ extern VALUE get_bool_value(bool value) {
34
+
35
+ return (value ? Qtrue : Qfalse);
36
+
37
+ }
38
+
39
+ extern VALUE get_float_value(void* mrb, double value) {
40
+
41
+ return DBL2NUM(value);
42
+
43
+ }
44
+
45
+ extern VALUE get_string_value(void* mrb, char* value) {
46
+
47
+ return rb_utf8_str_new(value, strlen(value));
48
+
49
+ }
50
+
51
+ extern VALUE get_symbol_value_of_string(void* mrb, char* value) {
52
+
53
+ ID sym = rb_intern(value);
54
+ return ID2SYM(sym);
55
+
56
+ }
57
+
58
+ extern int check_rb_fixnum(VALUE value) {
59
+
60
+ return FIXNUM_P(value);
61
+
62
+ }
63
+
64
+ extern int check_rb_float(VALUE value) {
65
+
66
+ return RB_FLOAT_TYPE_P(value);
67
+
68
+ }
69
+
70
+ extern int check_rb_true(VALUE value) {
71
+
72
+ return (value == Qtrue);
73
+
74
+ }
75
+
76
+ extern int check_rb_false(VALUE value) {
77
+
78
+ return (value == Qfalse);
79
+
80
+ }
81
+
82
+ extern int check_rb_nil(VALUE value) {
83
+
84
+ return NIL_P(value);
85
+
86
+ }
87
+
88
+ extern int check_rb_undef(VALUE value) {
89
+
90
+ return RB_TYPE_P(value, T_UNDEF);
91
+
92
+ }
93
+
94
+ extern int check_rb_string(VALUE value) {
95
+
96
+ return RB_TYPE_P(value, T_STRING);
97
+
98
+ }
99
+
100
+ extern int check_rb_symbol(VALUE value) {
101
+
102
+ return RB_TYPE_P(value, T_SYMBOL);
103
+
104
+ }
105
+
106
+ extern int check_rb_array(VALUE value) {
107
+
108
+ return RB_TYPE_P(value, T_ARRAY);
109
+
110
+ }
111
+
112
+ extern int check_rb_hash(VALUE value) {
113
+
114
+ return RB_TYPE_P(value, T_HASH);
115
+
116
+ }
117
+
118
+ extern int check_rb_data(VALUE value) {
119
+
120
+ return RB_TYPE_P(value, T_DATA);
121
+
122
+ }
123
+
124
+ extern long get_rb_fixnum(VALUE value) {
125
+
126
+ return FIX2INT(value);
127
+
128
+ }
129
+
130
+ extern double get_rb_float(VALUE value) {
131
+
132
+ return NUM2DBL(value);
133
+
134
+ }
135
+
136
+ extern bool get_rb_bool(VALUE value) {
137
+
138
+ return (value != Qfalse);
139
+
140
+ }
141
+
142
+ extern const char* get_rb_string(void* mrb, VALUE value) {
143
+
144
+ return rb_string_value_cstr(&value);
145
+
146
+ }
@@ -0,0 +1,38 @@
1
+ #include <ruby.h>
2
+
3
+ extern void* open_interpreter(void) {
4
+
5
+ return (void*) 0;
6
+
7
+ }
8
+
9
+ extern void close_interpreter(void* rb) {
10
+
11
+
12
+ }
13
+
14
+ extern void load_script_from_file(void* rb, const char* filename) {
15
+
16
+
17
+
18
+ }
19
+
20
+ extern VALUE execute_script_line(void* rb, const char* text) {
21
+
22
+ int status;
23
+ VALUE result = rb_eval_string_protect(text, &status);
24
+
25
+ if(status) {
26
+
27
+ VALUE exception = rb_errinfo();
28
+ VALUE exception_str = rb_inspect(exception);
29
+
30
+ //! TODO: Are there any internal methods to print this prettier?
31
+
32
+ printf("%s\n", rb_string_value_cstr(&exception_str));
33
+
34
+ }
35
+
36
+ return result;
37
+
38
+ }
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Polycrystal
4
+ class Compiler
5
+ attr_reader :build_path, :registry
6
+
7
+ def initialize(build_path:, registry: Polycrystal::Registry.instance)
8
+ @build_path = build_path
9
+ @registry = registry
10
+ # load anyolite
11
+ add_anyolite
12
+ end
13
+
14
+ def prepare
15
+ # load anyolite
16
+ add_anyolite
17
+ # write crystal entrypoint
18
+ in_file.write(entrypoint)
19
+ in_file.close
20
+ end
21
+
22
+ def execute
23
+ # run compiler
24
+ puts command
25
+ raise 'Failed to compile crystal module' unless system(command)
26
+
27
+ outfile
28
+ end
29
+
30
+ def add_anyolite
31
+ registry.register(
32
+ path: find_anyolite,
33
+ file: 'anyolite/anyolite',
34
+ unshift: true
35
+ )
36
+ end
37
+
38
+ def crystal_paths
39
+ registry.map(&:path).select { |p| p&.length&.positive? }
40
+ end
41
+
42
+ def crystal_files
43
+ registry.map(&:file).select { |p| p&.length&.positive? }
44
+ end
45
+
46
+ def crystal_modules
47
+ registry.select { |m| m.modules&.size&.positive? }.flat_map(&:modules)
48
+ end
49
+
50
+ def entrypoint
51
+ <<~CRYSTAL
52
+ #{requires.join("\n")}
53
+
54
+ FAKE_ARG = "polycrystal"
55
+
56
+ fun __polycrystal_init
57
+ GC.init
58
+ ptr = FAKE_ARG.to_unsafe
59
+ LibCrystalMain.__crystal_main(1, pointerof(ptr))
60
+ puts "Polycrystal init"
61
+ end
62
+
63
+ fun __polycrystal_module_run
64
+ puts "Polycrystal module run"
65
+ rbi = Anyolite::RbInterpreter.new#{' '}
66
+ Anyolite::HelperClasses.load_all(rbi)
67
+ #{pad_lines(wraps, 4).join("\n")}
68
+ end
69
+ CRYSTAL
70
+ end
71
+
72
+ def wraps
73
+ crystal_modules.map do |mod|
74
+ "Anyolite.wrap rbi, #{mod}"
75
+ end
76
+ end
77
+
78
+ def requires
79
+ crystal_files.map do |f|
80
+ "require \"#{f.sub(/\.cr$/, '')}\""
81
+ end
82
+ end
83
+
84
+ def command
85
+ "#{include_paths} #{anyolite_glue} #{compiler_cmd} build #{in_file.path} --link-flags \"#{crystal_link_flags}\" -o #{outfile} --release " \
86
+ '-Danyolite_implementation_ruby_3 -Duse_general_object_format_chars -Dexternal_ruby'
87
+ end
88
+
89
+ def crystal_link_flags
90
+ case RUBY_PLATFORM
91
+ when /darwin/
92
+ "-dynamic -bundle"
93
+ when /linux/
94
+ "-shared -pie"
95
+ else
96
+ raise "Unknown platform"
97
+ end
98
+ end
99
+
100
+ def pad_line(line, n)
101
+ "#{' ' * n}#{line}"
102
+ end
103
+
104
+ def pad_lines(lines, n)
105
+ lines.map { |l| pad_line(l, n) }
106
+ end
107
+
108
+ def in_file
109
+ @in_file ||= File.new(File.expand_path("#{build_path}/polycrystal_module.cr"), 'w')
110
+ end
111
+
112
+ def outfile
113
+ File.expand_path("#{build_path}/polycrystal_module.#{lib_ext}")
114
+ end
115
+
116
+ def lib_ext
117
+ case RUBY_PLATFORM
118
+ when /darwin/
119
+ "bundle"
120
+ when /linux/
121
+ "so"
122
+ else
123
+ raise "Unknown platform"
124
+ end
125
+ end
126
+
127
+ def include_paths
128
+ existing = `#{compiler_cmd} env`.split("\n").find { |line| line.start_with?('CRYSTAL_PATH') }
129
+ [existing, *crystal_paths].join(':')
130
+ end
131
+
132
+ def anyolite_glue
133
+ # "DLDFLAGS"=>"-L/Users/ahrushetskyi/.rbenv/versions/3.0.4/lib -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress"
134
+ env = "-L#{RbConfig::CONFIG['libdir']} #{RbConfig::CONFIG['LIBRUBYARG_SHARED']} " \
135
+ "#{File.expand_path("#{__dir__}/../../ext/polycrystal/data_helper.o")} " \
136
+ "#{File.expand_path("#{__dir__}/../../ext/polycrystal/error_helper.o")} " \
137
+ "#{File.expand_path("#{__dir__}/../../ext/polycrystal/return_functions.o")} " \
138
+ "#{File.expand_path("#{__dir__}/../../ext/polycrystal/script_helper.o")} "
139
+
140
+ "ANYOLITE_LINK_GLUE=\"#{env}\""
141
+ end
142
+
143
+ def compiler_cmd
144
+ 'crystal'
145
+ end
146
+
147
+ def find_anyolite
148
+ default_path = File.expand_path("#{__dir__}/../../crystal")
149
+ local_path = File.expand_path("#{build_path}/deps")
150
+ if File.exist?("#{local_path}/anyolite/anyolite.cr")
151
+ local_path
152
+ elsif File.exist?("#{default_path}/anyolite/anyolite.cr")
153
+ default_path
154
+ else
155
+ load_anyolite(local_path)
156
+ end
157
+ end
158
+
159
+ def load_anyolite(local_path)
160
+ FileUtils.mkdir_p(local_path)
161
+
162
+ system("git clone -b external-ruby https://github.com/ahrushetskyi/anyolite.git #{local_path}/anyolite")
163
+
164
+ local_path
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Polycrystal
4
+ class Loader
5
+ attr_reader :build_path, :registry
6
+
7
+ def initialize(build_path:, registry: Polycrystal::Registry.instance)
8
+ @build_path = build_path
9
+ @registry = registry
10
+ end
11
+
12
+ def load
13
+ compiler = Polycrystal::Compiler.new(
14
+ build_path: build_path,
15
+ registry: registry
16
+ )
17
+ compiler.prepare
18
+ library = compiler.execute
19
+ load_library(library) # load_library is defined in C file
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'forwardable'
5
+
6
+ module Polycrystal
7
+ class Registry
8
+ extend Forwardable
9
+ include Singleton
10
+ include Enumerable
11
+
12
+ CrystalModule = Struct.new(:path, :file, :modules, keyword_init: true)
13
+
14
+ attr_reader :modules
15
+
16
+ def_delegators :@modules, :each
17
+
18
+ def initialize
19
+ @modules = []
20
+ end
21
+
22
+ def register(path:, file: nil, modules: nil, unshift: false)
23
+ raise ArgumentError, 'include file is requred to wrap modules' if file.nil? && !modules.nil?
24
+
25
+ existing = find_module(file: file, path: path)
26
+ if existing
27
+ existing.modules += modules if modules
28
+ else
29
+ new_mod = CrystalModule.new(
30
+ path: path,
31
+ file: file,
32
+ modules: modules
33
+ )
34
+ unshift ? @modules.unshift(new_mod) : @modules.push(new_mod)
35
+ end
36
+ end
37
+
38
+ def find_module(path:, file:)
39
+ modules.find { |mod| mod.path == path && mod.file == file }
40
+ end
41
+
42
+ class << self
43
+ extend Forwardable
44
+
45
+ def_delegators :instance, :register, :modules
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Polycrystal
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polycrystal/version'
4
+ require 'polycrystal/polycrystal'
5
+ require 'polycrystal/registry'
6
+ require 'polycrystal/compiler'
7
+ require 'polycrystal/loader'
8
+
9
+ module Polycrystal
10
+ class Error < StandardError; end
11
+ # Your code goes here...
12
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'polycrystal/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'polycrystal'
9
+ spec.version = Polycrystal::VERSION
10
+ spec.authors = ['Andrii Hrushetskyi']
11
+ spec.email = ['andrii.hrushetskyi@gmail.com']
12
+
13
+ spec.summary = 'Integrate Crystal into ruby'
14
+ spec.description = 'Integrate Crystal into ruby using Anyolite https://github.com/Anyolite/anyolite'
15
+ spec.homepage = 'https://github.com/ahrushetskyi/polycrystal'
16
+ spec.license = 'MIT'
17
+
18
+ spec.platform = Gem::Platform::CURRENT
19
+ spec.required_ruby_version = '>= 3.0.4'
20
+
21
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
22
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
23
+ if spec.respond_to?(:metadata)
24
+ spec.metadata['homepage_uri'] = spec.homepage
25
+ spec.metadata['source_code_uri'] = 'https://github.com/ahrushetskyi/polycrystal'
26
+ spec.metadata['changelog_uri'] = 'https://github.com/ahrushetskyi/polycrystal/CHANGELOG.md'
27
+ else
28
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
29
+ 'public gem pushes.'
30
+ end
31
+
32
+ # Specify which files should be added to the gem when it is released.
33
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
34
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
35
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
36
+ end
37
+ spec.bindir = 'exe'
38
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
39
+ spec.require_paths = %w[lib ext]
40
+ spec.extensions = ['ext/polycrystal/extconf.rb']
41
+
42
+ spec.add_development_dependency 'bundler', '~> 2'
43
+ spec.add_development_dependency 'rake', '~> 10.0'
44
+ spec.add_development_dependency 'rspec', '~> 3.0'
45
+ spec.add_development_dependency 'rubocop', '~> 1.36.0'
46
+ spec.metadata['rubygems_mfa_required'] = 'true'
47
+ end
@@ -0,0 +1 @@
1
+ /build
@@ -0,0 +1 @@
1
+ ruby-3.0.4
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'polycrystal', path: '../'
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ polycrystal (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+
10
+ PLATFORMS
11
+ x86_64-darwin-21
12
+ x86_64-linux
13
+
14
+ DEPENDENCIES
15
+ polycrystal!
16
+
17
+ BUNDLED WITH
18
+ 2.3.14
@@ -0,0 +1,21 @@
1
+ class CrystalModule
2
+ class SomeThing
3
+ def some_method
4
+ 69.0
5
+ end
6
+ end
7
+
8
+ class CrystalClass
9
+ def crystal_method
10
+ return 42
11
+ end
12
+
13
+ def return_object
14
+ SomeThing.new
15
+ end
16
+
17
+ def recv_arg(arg : Int64) : Void
18
+ puts "Received #{arg}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ require 'polycrystal'
7
+
8
+ Polycrystal::Registry.register(
9
+ path: File.expand_path("#{__dir__}/crystal"),
10
+ file: 'sample.cr',
11
+ modules: ['CrystalModule']
12
+ )
13
+
14
+ build_path = File.expand_path("#{__dir__}/build")
15
+
16
+ FileUtils.mkdir_p(build_path)
17
+
18
+ Polycrystal::Loader.new(build_path: build_path).load
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: polycrystal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: x86_64-darwin-21
6
+ authors:
7
+ - Andrii Hrushetskyi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.36.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.36.0
69
+ description: Integrate Crystal into ruby using Anyolite https://github.com/Anyolite/anyolite
70
+ email:
71
+ - andrii.hrushetskyi@gmail.com
72
+ executables: []
73
+ extensions:
74
+ - ext/polycrystal/extconf.rb
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".rubocop.yml"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - ext/polycrystal/data_helper.c
86
+ - ext/polycrystal/error_helper.c
87
+ - ext/polycrystal/extconf.h
88
+ - ext/polycrystal/extconf.rb
89
+ - ext/polycrystal/polycrystal.c
90
+ - ext/polycrystal/return_functions.c
91
+ - ext/polycrystal/script_helper.c
92
+ - lib/polycrystal.rb
93
+ - lib/polycrystal/compiler.rb
94
+ - lib/polycrystal/loader.rb
95
+ - lib/polycrystal/registry.rb
96
+ - lib/polycrystal/version.rb
97
+ - polycrystal.gemspec
98
+ - sample_project/.gitignore
99
+ - sample_project/.ruby-version
100
+ - sample_project/Gemfile
101
+ - sample_project/Gemfile.lock
102
+ - sample_project/crystal/sample.cr
103
+ - sample_project/sample.rb
104
+ homepage: https://github.com/ahrushetskyi/polycrystal
105
+ licenses:
106
+ - MIT
107
+ metadata:
108
+ homepage_uri: https://github.com/ahrushetskyi/polycrystal
109
+ source_code_uri: https://github.com/ahrushetskyi/polycrystal
110
+ changelog_uri: https://github.com/ahrushetskyi/polycrystal/CHANGELOG.md
111
+ rubygems_mfa_required: 'true'
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ - ext
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 3.0.4
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.2.33
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Integrate Crystal into ruby
132
+ test_files: []