gvars 0.9.5 → 0.9.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a3f32ce689ea5b4077c4736674afc353494fafd71bec8dc0e108cc95c33424a
4
- data.tar.gz: 237e9f7383637a345dde01342598ec24b01c8e4f8e953f33cde03fc164cb1ba8
3
+ metadata.gz: 837ffc167909e2abe16ff54fdcd7bfbe4cb6dc0346f1c3e9037b17933f3b89a7
4
+ data.tar.gz: 9e36c6847244b578e63032468c2d94e7fff4f4e05c68cd3a1c126fc257a02b7c
5
5
  SHA512:
6
- metadata.gz: 54094ee144a422dca37c6ae607d1250829b987c33f92b38f7fb6c2b5ef6aa291a8249bb170db0330c0eac2020178ae6fb4579ee95e82f3f810561f978dc8f1d5
7
- data.tar.gz: 7052348eee6adec41144417564e472575b943f03b4a9edfcf3d2a1e9e715dcde50eb3f56393c8423c42319629ba0a4a97ef0846f4b38b841fb0b063d72f0b280
6
+ metadata.gz: 7e71e7a19371437156d14f1831045a6229b4fcd055cc82680309266c5ed20781ec030b78e591e38e4a7adb62633ceb98d5284ca69b8e40eb475ee4ea829ee269
7
+ data.tar.gz: f5cba137bad5f8d9db885f468df9bc147f89b7c0106653a409fd8aa5d32767a8fceba372b4a4c778bdf4161ae59c215dd2081cff250aeccfcf4c2c161658c1a9
data/ext/gvars/extconf.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'mkmf'
2
2
 
3
- # $CFLAGS << ' -g -fsanitize=undefined'
3
+ $CFLAGS << ' -g -fsanitize=undefined'
4
4
 
5
5
  create_makefile 'gvars/gvars'
data/ext/gvars/gvars.c CHANGED
@@ -1,10 +1,13 @@
1
1
  #include "ruby.h"
2
+ #define dbg(...) (fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__), fprintf(stderr,__VA_ARGS__),fprintf(stderr,"\n"),fflush(stderr))
3
+
4
+ VALUE gvars_module;
2
5
 
3
6
  // Converts `name` to a global variable name, ensures it's valid, and returns it.
4
7
  //
5
8
  // for checking, it makes that `name` starts with `$`. This isn't really required, as ruby supports
6
9
  // globals that don't start with `$` (but doesn't expose any methods to interact with them)
7
- static char *get_global_name(VALUE *name) {
10
+ static char *get_global_name(VALUE *name, bool raise) {
8
11
  // If `name` is a symbol, get its backing string
9
12
  if (RB_SYMBOL_P(*name)) {
10
13
  *name = rb_sym2str(*name);
@@ -13,7 +16,7 @@ static char *get_global_name(VALUE *name) {
13
16
  char *namestr = StringValueCStr(*name);
14
17
 
15
18
  // Ensure it starts with a dollar sign
16
- if (namestr[0] != '$') {
19
+ if (raise && namestr[0] != '$') {
17
20
  rb_raise(rb_eNameError, "'%s' is not allowed as a global variable name", namestr);
18
21
  }
19
22
 
@@ -24,13 +27,26 @@ static char *get_global_name(VALUE *name) {
24
27
  static VALUE
25
28
  gvars_f_global_variable_get(VALUE self, VALUE name)
26
29
  {
27
- return rb_gv_get(get_global_name(&name));
30
+ return rb_gv_get(get_global_name(&name, true));
31
+ }
32
+
33
+ static VALUE
34
+ gvars_f_global_variable_aref(VALUE self, VALUE name)
35
+ {
36
+ return rb_gv_get(get_global_name(&name, false));
28
37
  }
29
38
 
30
39
  static VALUE
31
40
  gvars_f_global_variable_set(VALUE self, VALUE name, VALUE value)
32
41
  {
33
- return rb_gv_set(get_global_name(&name), value);
42
+ return rb_gv_set(get_global_name(&name, true), value);
43
+ }
44
+
45
+
46
+ static VALUE
47
+ gvars_f_global_variable_aset(VALUE self, VALUE name, VALUE value)
48
+ {
49
+ return rb_gv_set(get_global_name(&name, false), value);
34
50
  }
35
51
 
36
52
  static VALUE
@@ -42,52 +58,260 @@ gvars_f_global_variables(VALUE self)
42
58
  static VALUE
43
59
  gvars_f_alias_global_variable(VALUE self, VALUE new, VALUE old)
44
60
  {
45
- ID newid = rb_intern(get_global_name(&new));
46
- rb_alias_variable(newid, rb_intern(get_global_name(&old)));
61
+ ID newid = rb_intern(get_global_name(&new, true));
62
+ rb_alias_variable(newid, rb_intern(get_global_name(&old, true)));
47
63
  return ID2SYM(newid);
48
64
  }
49
65
 
50
66
  static VALUE
51
67
  gvars_enum_length(VALUE self, VALUE args, VALUE eobj)
52
68
  {
53
- return LONG2NUM(RARRAY_LEN(rb_f_global_variables()));
69
+ return LONG2NUM(RARRAY_LEN(rb_f_global_variables()));
54
70
  }
55
71
 
56
72
  static VALUE
57
73
  gvars_f_each(VALUE self)
58
74
  {
59
- RETURN_SIZED_ENUMERATOR(self, 0, 0, gvars_enum_length);
75
+ RETURN_SIZED_ENUMERATOR(self, 0, 0, gvars_enum_length);
60
76
 
61
- VALUE gvars = gvars_f_global_variables(self);
62
- for (long i = 0; i < RARRAY_LEN(gvars); i++) {
63
- VALUE key = RARRAY_AREF(gvars, i);
64
- rb_yield(rb_ary_new3(2, key, gvars_f_global_variable_get(self, key)));
65
- }
77
+ VALUE gvars = gvars_f_global_variables(self);
78
+ for (long i = 0; i < RARRAY_LEN(gvars); i++) {
79
+ VALUE key = RARRAY_AREF(gvars, i);
80
+ rb_yield(rb_ary_new_from_args(2, key, gvars_f_global_variable_get(self, key)));
81
+ }
66
82
 
67
- return self;
83
+ return self;
68
84
  }
69
85
 
70
86
  static VALUE
71
87
  gvars_f_to_h(VALUE self)
72
88
  {
73
- VALUE gvars = gvars_f_global_variables(self);
74
- VALUE hash = rb_hash_new_capa(RARRAY_LEN(gvars));
89
+ VALUE gvars = gvars_f_global_variables(self);
90
+ VALUE hash = rb_hash_new_capa(RARRAY_LEN(gvars));
75
91
 
76
- for (long i = 0; i < RARRAY_LEN(gvars); i++) {
77
- VALUE key = RARRAY_AREF(gvars, i);
78
- rb_hash_aset(hash, key, gvars_f_global_variable_get(self, key));
79
- }
92
+ for (long i = 0; i < RARRAY_LEN(gvars); i++) {
93
+ VALUE key = RARRAY_AREF(gvars, i);
94
+ rb_hash_aset(hash, key, gvars_f_global_variable_get(self, key));
95
+ }
80
96
 
81
- return hash;
97
+ return hash;
82
98
  }
83
99
 
100
+ struct value_ptr { VALUE *value; };
101
+ static void value_ptr_mark(void *ptr) { rb_gc_mark(*((struct value_ptr *) ptr)->value); }
102
+ static void value_ptr_free(void *ptr) { ruby_xfree(((struct value_ptr *) ptr)->value); }
103
+ static size_t value_ptr_size(const void *ptr) { return sizeof(struct value_ptr); }
104
+ static const rb_data_type_t value_ptr_data_type = {
105
+ .wrap_struct_name = "gvars/value_ptr",
106
+ .function = {
107
+ .dmark = value_ptr_mark,
108
+ .dfree = value_ptr_free,
109
+ .dsize = value_ptr_size,
110
+ },
111
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
112
+ };
84
113
 
85
- VALUE gvars_module;
114
+
115
+ struct gvars_virtual_var {
116
+ VALUE backing, getter, setter;
117
+ };
118
+
119
+ static void gvars_virtual_var_mark(void *ptr) {
120
+ struct gvars_virtual_var *gv = ptr;
121
+ rb_gc_mark(gv->backing);
122
+ rb_gc_mark(gv->getter);
123
+ rb_gc_mark(gv->setter);
124
+ }
125
+
126
+ static size_t gvars_virtual_var_size(const void *ptr) {
127
+ return sizeof(struct gvars_virtual_var);
128
+ }
129
+
130
+ static const rb_data_type_t gvars_type = {
131
+ .wrap_struct_name = "gvars/var",
132
+ .function = {
133
+ .dmark = gvars_virtual_var_mark,
134
+ .dfree = RUBY_DEFAULT_FREE,
135
+ .dsize = gvars_virtual_var_size,
136
+ },
137
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
138
+ };
139
+
140
+
141
+ static VALUE gvars_virtual_var_getter(ID id, VALUE *data) {
142
+ struct gvars_virtual_var *gv;
143
+ TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
144
+ return rb_proc_call(gv->getter, rb_ary_new());
145
+ }
146
+
147
+ static void gvars_virtual_var_setter(VALUE val, ID id, VALUE *data) {
148
+ struct gvars_virtual_var *gv;
149
+ TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
150
+ rb_proc_call(gv->setter, rb_ary_new_from_args(1, val));
151
+ }
152
+
153
+ static VALUE gvars_hooked_state_var_getter(ID id, VALUE *data) {
154
+ struct gvars_virtual_var *gv;
155
+ TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
156
+ return rb_proc_call(gv->getter, rb_ary_new_from_args(1, gv->backing));
157
+ }
158
+
159
+ static VALUE gvars_hooked_initial_var_getter(ID id, VALUE *data) {
160
+ struct gvars_virtual_var *gv;
161
+ TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
162
+ return gv->backing = rb_proc_call(gv->getter, rb_ary_new_from_args(1, gv->backing));
163
+ }
164
+
165
+ static void gvars_hooked_var_setter_noproc(VALUE val, ID id, VALUE *data) {
166
+ struct gvars_virtual_var *gv;
167
+ TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
168
+ gv->backing = val;
169
+ }
170
+
171
+ static void gvars_hooked_var_setter_proc(VALUE val, ID id, VALUE *data) {
172
+ struct gvars_virtual_var *gv;
173
+ TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
174
+ gv->backing = rb_proc_call(gv->setter, rb_ary_new_from_args(2, val, gv->backing));
175
+ }
176
+
177
+ static VALUE gvars_virtual_var_alloc(VALUE backing, VALUE getter, VALUE setter) {
178
+ struct gvars_virtual_var *gv;
179
+ VALUE gvar_ty = TypedData_Make_Struct(rb_cObject, struct gvars_virtual_var, &gvars_type, gv);
180
+ gv->backing = backing;
181
+ gv->getter = getter;
182
+ gv->setter = setter;
183
+ return gvar_ty;
184
+ }
185
+
186
+ enum virtual_kind {
187
+ GVARS_VIRTUAL_KIND_VIRTUAL,
188
+ GVARS_VIRTUAL_KIND_INITIAL,
189
+ GVARS_VIRTUAL_KIND_STATE,
190
+ };
191
+
192
+ static void
193
+ gvars_define_virtual_method(VALUE self, VALUE *name, VALUE backing, VALUE getter, VALUE setter, enum virtual_kind kind, int readonly_special)
194
+ {
195
+ char *name_str = get_global_name(name, true);
196
+ struct value_ptr *vp;
197
+ VALUE vp_data = TypedData_Make_Struct(rb_cObject, struct value_ptr, &value_ptr_data_type, vp);
198
+ vp->value = RB_ALLOC(VALUE);
199
+ *vp->value = gvars_virtual_var_alloc(backing, getter, setter);
200
+
201
+ rb_gvar_getter_t *getter_meth;
202
+ rb_gvar_setter_t *setter_meth;
203
+
204
+ switch (kind) {
205
+ case GVARS_VIRTUAL_KIND_VIRTUAL:
206
+ getter_meth = gvars_virtual_var_getter;
207
+ setter_meth = setter == Qnil ? rb_gvar_readonly_setter : gvars_virtual_var_setter;
208
+ break;
209
+
210
+ case GVARS_VIRTUAL_KIND_STATE:
211
+ case GVARS_VIRTUAL_KIND_INITIAL:
212
+ getter_meth = kind == GVARS_VIRTUAL_KIND_INITIAL ? gvars_hooked_initial_var_getter : gvars_hooked_state_var_getter;
213
+ if (readonly_special) setter_meth = rb_gvar_readonly_setter;
214
+ else setter_meth = setter == Qnil ? gvars_hooked_var_setter_noproc : gvars_hooked_var_setter_proc;
215
+ break;
216
+
217
+ default:
218
+ rb_bug("oops");
219
+ }
220
+
221
+
222
+ rb_define_hooked_variable(name_str, vp->value, getter_meth, setter_meth);
223
+
224
+ VALUE hash = rb_iv_get(gvars_module, "vars");
225
+ VALUE namesym = rb_str_new_cstr(name_str);
226
+ rb_hash_aset(hash, namesym, vp_data);
227
+ }
228
+
229
+ enum virtual_method_keywords {
230
+ VIRTUAL_METHOD_KEYWORDS_INITIAL,
231
+ VIRTUAL_METHOD_KEYWORDS_STATE,
232
+ VIRTUAL_METHOD_KEYWORDS_READONLY,
233
+ LENGTH_OF_VIRTUAL_METHOD_KEYWORDS,
234
+ };
235
+
236
+ static ID virtual_method_keyword_ids[LENGTH_OF_VIRTUAL_METHOD_KEYWORDS];
237
+
238
+ static VALUE convert_to_proc(const char *name, VALUE input) {
239
+ VALUE proc = rb_convert_type(input, T_DATA, "Proc", "to_proc");
240
+
241
+ if (NIL_P(proc) || !rb_obj_is_proc(proc)) {
242
+ rb_raise(rb_eTypeError, "wrong %s type %s (expected Proc)", name, rb_obj_classname(proc));
243
+ }
244
+
245
+ return proc;
246
+ }
247
+
248
+ static VALUE
249
+ gvars_f_define_virtual_method(int argc, VALUE *argv, VALUE self)
250
+ {
251
+ VALUE name, backing, getter, setter, opts_hash, opts[LENGTH_OF_VIRTUAL_METHOD_KEYWORDS];
252
+
253
+ switch (rb_scan_args(argc, argv, "12:", &name, &getter, &setter, &opts_hash)) {
254
+ case 1:
255
+ getter = (rb_need_block(), rb_block_proc());
256
+ setter = Qnil;
257
+ break;
258
+
259
+ case 2:
260
+ setter = Qnil;
261
+
262
+ case 3:
263
+ if (rb_block_given_p()) {
264
+ rb_warn("given block not used");
265
+ }
266
+ break;
267
+ default:
268
+ rb_bug("oops");
269
+ }
270
+
271
+ getter = convert_to_proc("getter", getter);
272
+ if (!NIL_P(setter)) setter = convert_to_proc("setter", setter);
273
+
274
+ enum virtual_kind kind = GVARS_VIRTUAL_KIND_VIRTUAL;
275
+ rb_get_kwargs(opts_hash, virtual_method_keyword_ids, 0, LENGTH_OF_VIRTUAL_METHOD_KEYWORDS, opts);
276
+
277
+ if (opts[VIRTUAL_METHOD_KEYWORDS_INITIAL] != Qundef) {
278
+ if (opts[VIRTUAL_METHOD_KEYWORDS_STATE] != Qundef) {
279
+ rb_raise(rb_eArgError, "only one of 'initial' or 'state' may be supplied");
280
+ }
281
+ backing = opts[VIRTUAL_METHOD_KEYWORDS_INITIAL];
282
+ kind = GVARS_VIRTUAL_KIND_INITIAL;
283
+ } else if (opts[VIRTUAL_METHOD_KEYWORDS_STATE] != Qundef) {
284
+ backing = opts[VIRTUAL_METHOD_KEYWORDS_STATE];
285
+ kind = GVARS_VIRTUAL_KIND_STATE;
286
+ } else {
287
+ backing = Qnil;
288
+ }
289
+
290
+ // sadly no bool expected :-/
291
+ bool is_readonly = opts[VIRTUAL_METHOD_KEYWORDS_READONLY] != Qundef && RTEST(opts[VIRTUAL_METHOD_KEYWORDS_READONLY]);
292
+ if (is_readonly) {
293
+ if (setter != Qnil) {
294
+ rb_raise(rb_eArgError, "'readonly: true' may not be supplied when a setter proc is supplied");
295
+ }
296
+
297
+ setter = Qnil;
298
+ is_readonly = true;
299
+ }
300
+
301
+ gvars_define_virtual_method(self, &name, backing, getter, setter, kind, is_readonly);
302
+
303
+ return name; //TODO: ID2SYM(id)
304
+ }
86
305
 
87
306
  void
88
307
  Init_gvars(void)
89
308
  {
90
309
  gvars_module = rb_define_module("GVars");
310
+ rb_ivar_set(gvars_module, rb_intern("vars"), rb_hash_new());
311
+
312
+ virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_INITIAL] = rb_intern("initial");
313
+ virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_STATE] = rb_intern("state");
314
+ virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_READONLY] = rb_intern("readonly");
91
315
 
92
316
  // Define module-level functions that can be used as mixins
93
317
  rb_define_module_function(gvars_module, "global_variable_get", gvars_f_global_variable_get, 1);
@@ -97,17 +321,22 @@ Init_gvars(void)
97
321
  // Don't make it an instance method, as it exists in Kernel
98
322
  rb_define_singleton_method(gvars_module, "global_variables", gvars_f_global_variables, 0);
99
323
 
324
+ // Don't make it an instance method, cause we don't want it included
325
+ rb_define_singleton_method(gvars_module, "define_virtual_method", gvars_f_define_virtual_method, -1);
326
+
100
327
  // Enumerable methods
101
- rb_extend_object(gvars_module, rb_mEnumerable);
328
+ rb_extend_object(gvars_module, rb_mEnumerable);
102
329
  rb_define_singleton_method(gvars_module, "each", gvars_f_each, 0);
103
330
  rb_define_singleton_method(gvars_module, "to_h", gvars_f_to_h, 0);
104
331
 
332
+ rb_define_singleton_method(gvars_module, "[]", gvars_f_global_variable_aref, 1);
333
+ rb_define_singleton_method(gvars_module, "[]=", gvars_f_global_variable_aset, 2);
334
+
105
335
  // Aliases
106
336
  VALUE gvars_singleton = rb_singleton_class(gvars_module);
107
337
  rb_define_alias(gvars_singleton, "get", "global_variable_get");
108
- rb_define_alias(gvars_singleton, "[]", "global_variable_get");
109
338
  rb_define_alias(gvars_singleton, "set", "global_variable_set");
110
- rb_define_alias(gvars_singleton, "[]=", "global_variable_set");
111
339
  rb_define_alias(gvars_singleton, "alias", "alias_global_variable");
112
340
  rb_define_alias(gvars_singleton, "list", "global_variables");
341
+ rb_define_alias(gvars_singleton, "virtual", "define_virtual_method");
113
342
  }
data/gb ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ version = nil
4
+ File.open File.join __dir__, 'lib', 'gvars', 'version.rb' do |f|
5
+ f.read =~ /VERSION = '(.*)'/ and (version = $1; break)
6
+ end
7
+
8
+ fail "cant find version" unless version
9
+ system('gem build gvars') && system("gem install ./gvars-#{version}.gem")
data/lib/gvars/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GVars
4
- VERSION = '0.9.5'
4
+ VERSION = '0.9.8'
5
5
  end
data/tmp.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'gvars'
2
+ p GVars::VERSION
3
+
4
+ require 'optparse'
5
+ OptParse.new do |op|
6
+ op.on '--timeout=X', 'the timeout'
7
+ op.parse %w[--timeout 34.5], into: GVars
8
+ end
9
+
10
+ p $timeout
11
+
12
+ __END__
13
+ h = {}
14
+ GVars.virtual(:$-e, &h.method(:[]).curry(1).<<(:$-e))
15
+ # GVars.virtual(:$-e,) { $-W.to_s }
16
+ # $-v = false
17
+ # p $-e
18
+ # $-v = true
19
+ # p $-e
20
+ # $-v = nil
21
+ p $-e
22
+
23
+ exit
24
+
25
+ 100.times do
26
+ GVars.virtual(:$foo) { p "bar" }
27
+ # GVars.virtual(:$foo) { p "foo" }
28
+ # $foo
29
+ # p($foo)
30
+ # p GVars.instance_variable_get(:@vars)
31
+ GC.start
32
+ end
33
+
34
+ GVars.virtual(:$foo1) { "hello" }; fail unless 'hello' == $foo1
35
+ GVars.virtual(:$foo2, proc{ "hello" }); fail unless 'hello' == $foo2
36
+ GVars.virtual(:$foo3, proc{ "hello" }, proc { it + 1 }); fail unless ['hello', 3, 'hello'] == [$foo3, $foo3 = 3, $foo3]
37
+
38
+ GVars.virtual(:$bar1, initial: 0) { it + 1 }; fail unless [1, 2, 3] == [$bar1, $bar1, $bar1]
39
+ $bar1 = 10
40
+ fail unless 11 == $bar1
41
+
42
+ GVars.virtual(:$bar2, state: 'a') { it.succ!.dup }
43
+ fail unless ['b', 'c', 'd'] == [$bar2, $bar2, $bar2]
44
+
45
+
46
+ GVars.virtual(:$lol) { @x = 3 }
47
+ class Thing
48
+ def doit
49
+ $lol
50
+ p @x
51
+ end
52
+ end
53
+ Thing.new.doit
54
+ p @x
55
+ trace_var :$bar, proc {|v| puts "$_ is now '#{v}'" }
56
+ $bar = 2
57
+ $bar = 3
58
+ trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
59
+ $_ = "hello"
60
+ $_ = ' there'
61
+
62
+ # $bar2 = 10
63
+ # fail unless 11 == $bar2
64
+
65
+ # GVars.virtual(:$bar, proc{ p "bar" })
66
+ # GVars.virtual(:$baz, proc{ p "bar" }, proc { p "baz: #{_1}"})
67
+
68
+ # 100.times do
69
+ # $foo
70
+ # $bar
71
+ # $baz
72
+ # GC.start
73
+ # end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gvars
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Westerman
@@ -24,10 +24,11 @@ files:
24
24
  - Rakefile
25
25
  - ext/gvars/extconf.rb
26
26
  - ext/gvars/gvars.c
27
- - ext/gvars/gvars.c-old
27
+ - gb
28
28
  - lib/gvars.rb
29
29
  - lib/gvars/version.rb
30
30
  - sig/gvars.rbs
31
+ - tmp.rb
31
32
  homepage: https://github.com/sampersand/gvars
32
33
  licenses:
33
34
  - MIT
@@ -1,147 +0,0 @@
1
- #include "ruby.h"
2
-
3
- // Converts `name` to a global variable name, ensures it's valid, and returns it.
4
- //
5
- // for checking, it makes that `name` starts with `$`. This isn't really required, as ruby supports
6
- // globals that don't start with `$` (but doesn't expose any methods to interact with them)
7
- static char *get_global_name(VALUE *name) {
8
- // printf("spot1\n");
9
- if (RB_SYMBOL_P(*name)) *name = rb_sym2str(*name);
10
- // printf("spot2\n");
11
- char *namestr = StringValueCStr(*name);
12
- // printf("spot3\n");
13
-
14
- if (namestr[0] != '$') {
15
- rb_raise(rb_eNameError, "'%s' is not allowed as a global variable name", namestr);
16
- }
17
-
18
- return namestr;
19
- }
20
-
21
- static VALUE
22
- gvars_f_get(VALUE self, VALUE name)
23
- {
24
- // extern VALUE rb_gvars_get(ID);
25
- return rb_gvars_get(rb_intern(get_global_name(&name)));
26
- }
27
-
28
- static VALUE
29
- gvars_f_set(VALUE self, VALUE name, VALUE value)
30
- {
31
- return rb_gv_set(get_global_name(&name), value);
32
- }
33
-
34
- static VALUE
35
- gvars_f_defined_p(VALUE self, VALUE name)
36
- {
37
- extern VALUE rb_gvar_defined(ID);
38
- return rb_gvar_defined(rb_check_id(&name));
39
- }
40
-
41
- static VALUE
42
- gvars_list(VALUE self)
43
- {
44
- return rb_f_global_variables();
45
- }
46
-
47
- static VALUE
48
- gvars_f_alias(VALUE self, VALUE new, VALUE old)
49
- {
50
- ID newid = rb_intern(get_global_name(&new));
51
- rb_alias_variable(newid, rb_intern(get_global_name(&old)));
52
- return ID2SYM(newid);
53
- }
54
-
55
- struct hooked_var {
56
- VALUE getter, setter;
57
- };
58
-
59
- static VALUE hooked_var_getter(ID id, VALUE *data) {
60
- struct hooked_var *bv = (struct hooked_var *)data;
61
- return rb_proc_call_kw(bv->getter, rb_ary_new3(1, rb_id2str(id)), RB_NO_KEYWORDS);
62
- }
63
-
64
- static void hooked_var_setter(VALUE val, ID id, VALUE *data) {
65
- struct hooked_var *bv = (struct hooked_var *)data;
66
- rb_proc_call_kw(bv->setter, rb_ary_new3(2, rb_id2str(id), val), RB_NO_KEYWORDS);
67
- }
68
-
69
- static VALUE
70
- gvars_define_virtual_global(int argc, VALUE *argv, VALUE self)
71
- {
72
- VALUE name, getter, setter;
73
-
74
- switch (rb_scan_args(argc, argv, "12", &name, &getter, &setter)) {
75
- case 1:
76
- getter = (rb_need_block(), rb_block_proc());
77
- setter = Qnil;
78
- break;
79
-
80
- case 2:
81
- setter = Qnil;
82
-
83
- case 3:
84
- if (rb_block_given_p()) {
85
- rb_warn("given block not used");
86
- }
87
- break;
88
- default:
89
- rb_bug("oops");
90
- }
91
-
92
- char *name_str = get_global_name(&name);
93
-
94
- VALUE getter_proc, setter_proc;
95
-
96
- getter_proc = rb_convert_type(getter, T_DATA, "Proc", "to_proc");
97
- if (NIL_P(getter_proc) || !rb_obj_is_proc(getter_proc)) {
98
- rb_raise(rb_eTypeError, "wrong getter type %s (expected Proc)", rb_obj_classname(getter_proc));
99
- }
100
-
101
- if (NIL_P(setter)) {
102
- setter_proc = Qnil;
103
- } else {
104
- setter_proc = rb_convert_type(setter, T_DATA, "Proc", "to_proc");
105
- if (NIL_P(setter_proc) || !rb_obj_is_proc(setter_proc)) {
106
- rb_raise(rb_eTypeError, "wrong setter type %s (expected Proc)", rb_obj_classname(setter_proc));
107
- }
108
- }
109
-
110
- // todo: not just leak memory here lol
111
- struct hooked_var *hv = (struct hooked_var *)malloc(sizeof(struct hooked_var));
112
- hv->getter = getter_proc;
113
- hv->setter = setter_proc;
114
-
115
- rb_define_hooked_variable(name_str, (VALUE *)hv, hooked_var_getter, setter_proc == Qnil ? rb_gvar_readonly_setter : hooked_var_setter);
116
- extern void rb_gvar_ractor_local(const char *);
117
- rb_gvar_ractor_local(name_str);
118
- return name; //TODO: ID2SYM(id)
119
- }
120
-
121
- VALUE gvars_module;
122
-
123
- void
124
- Init_gvars(void)
125
- {
126
- gvars_module = rb_define_module("GVars");
127
-
128
- // Define module-level functions that can be used as mixins
129
- rb_define_module_function(gvars_module, "global_variable_get", gvars_f_get, 1);
130
- rb_define_module_function(gvars_module, "global_variable_set", gvars_f_set, 2);
131
- // rb_define_module_function(gvars_module, "global_variable_defined?", gvars_f_defined_p, 1);
132
- rb_define_module_function(gvars_module, "alias_global_variable", gvars_f_alias, 2);
133
-
134
- // Don't make mixin, as it exists in Kernel
135
- rb_define_singleton_method(gvars_module, "global_variables", gvars_list, 0);
136
-
137
- // aliases at top-level
138
- rb_define_alias(rb_singleton_class(gvars_module), "get", "global_variable_get");
139
- rb_define_alias(rb_singleton_class(gvars_module), "set", "global_variable_set");
140
- // rb_define_alias(rb_singleton_class(gvars_module), "defined?", "global_variable_defined?");
141
- rb_define_alias(rb_singleton_class(gvars_module), "alias", "alias_global_variable");
142
- rb_define_alias(rb_singleton_class(gvars_module), "list", "global_variables");
143
- rb_define_alias(rb_singleton_class(gvars_module), "[]", "global_variable_get");
144
- rb_define_alias(rb_singleton_class(gvars_module), "[]=", "global_variable_set");
145
-
146
- // rb_define_singleton_method(gvars_module, "virtual", gvars_define_virtual_global, -1);
147
- }