ruby2cext 0.2.0

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.
@@ -0,0 +1,1409 @@
1
+
2
+ require "ruby2cext/str_to_c_strlit"
3
+ require "ruby2cext/error"
4
+ require "ruby2cext/tools"
5
+ require "ruby2cext/scopes"
6
+ require "ruby2cext/version"
7
+
8
+ module Ruby2CExtension
9
+
10
+ module CommonNodeComp
11
+
12
+ # the comp methods need lots of methods from CFunction::Base or subclasses to work, e.g.:
13
+ # l, assign_res, get_self, get_class, get_cbase, get_cvar_cbase,
14
+ # scope (with get_lvar, get_lvar_idx, get_dvar, get_dvar_curr, vmode_method?, vmode_def_fun, ...)
15
+ # un, sym, global, add_helper
16
+ # ...
17
+
18
+ is_ruby_185 = (RUBY_VERSION >= "1.8.5") # see below
19
+
20
+ include Tools::EnsureNodeTypeMixin
21
+
22
+ def c_scope
23
+ l "{"
24
+ yield
25
+ l "}"
26
+ end
27
+ def c_scope_res
28
+ l "{"
29
+ assign_res(yield)
30
+ l "}"
31
+ "res"
32
+ end
33
+
34
+ def c_if(cond)
35
+ l "if (#{cond}) {"
36
+ yield
37
+ l "}"
38
+ end
39
+ def c_else
40
+ l "else {"
41
+ yield
42
+ l "}"
43
+ end
44
+ def c_for(exprs)
45
+ l "for (#{exprs}) {"
46
+ yield
47
+ l "}"
48
+ end
49
+
50
+ def c_static_once
51
+ c_scope_res {
52
+ l "static VALUE static_once_value = Qundef;"
53
+ c_if("static_once_value == Qundef") {
54
+ assign_res(yield)
55
+ # other thread might have been faster
56
+ c_if("static_once_value == Qundef") {
57
+ l "static_once_value = res;"
58
+ l "rb_global_variable(&static_once_value);"
59
+ }
60
+ }
61
+ "static_once_value"
62
+ }
63
+ end
64
+
65
+ def get_global_entry(vid)
66
+ g_entry = "(VALUE)rb_global_entry(#{sym(vid)})"
67
+ "(struct global_entry*)(#{global_const(g_entry, false)})"
68
+ end
69
+
70
+ def make_block(block)
71
+ if block
72
+ block.first == :block ? block : [:block, [block]]
73
+ else
74
+ [:block, []]
75
+ end
76
+ end
77
+
78
+ def handle_method_args(arg, block_arg)
79
+ ensure_node_type(arg, :args)
80
+ # handle arg
81
+ arg = arg.last
82
+ cnt = arg[:cnt]
83
+ opt = make_block(arg[:opt]).last
84
+ rest = arg[:rest] || -1
85
+ if Array === rest # 1.8.5 change
86
+ ensure_node_type(rest, :lasgn)
87
+ if rest.last[:vid] == 0 # e.g. def foo(*); end
88
+ rest = -2
89
+ else
90
+ rest = rest.last[:cnt]
91
+ end
92
+ end
93
+ rest = rest - 2
94
+ need_wrong_arg_num_helper = false
95
+ if opt.empty? && rest == -3 # then it was rest == -1, which means no rest_arg
96
+ l "if (meth_argc != #{cnt}) wrong_arg_num(meth_argc, #{cnt});"
97
+ need_wrong_arg_num_helper = true
98
+ else
99
+ if cnt > 0
100
+ l "if (meth_argc < #{cnt}) wrong_arg_num(meth_argc, #{cnt});"
101
+ need_wrong_arg_num_helper = true
102
+ end
103
+ if rest == -3 # then it was rest == -1, which means no rest_arg
104
+ l "if (meth_argc > #{cnt + opt.size}) wrong_arg_num(meth_argc, #{cnt + opt.size});"
105
+ need_wrong_arg_num_helper = true
106
+ end
107
+ end
108
+ if need_wrong_arg_num_helper
109
+ add_helper <<-EOC
110
+ static void wrong_arg_num(int argc, int exp) {
111
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, exp);
112
+ }
113
+ EOC
114
+ end
115
+ (0...cnt).each { |i|
116
+ l "#{scope.get_lvar_idx(i)} = meth_argv[#{i}];"
117
+ }
118
+ opt.each_with_index { |asgn, i|
119
+ c_if("meth_argc > #{cnt + i}") {
120
+ l "#{scope.get_lvar_idx(cnt + i)} = meth_argv[#{cnt + i}];"
121
+ }
122
+ c_else {
123
+ l "#{comp(asgn)};"
124
+ }
125
+ }
126
+ if rest >= 0
127
+ sum = cnt + opt.size
128
+ c_if("meth_argc > #{sum}") {
129
+ l "#{scope.get_lvar_idx(rest)} = rb_ary_new4(meth_argc-#{sum}, meth_argv+#{sum});"
130
+ }
131
+ c_else {
132
+ l "#{scope.get_lvar_idx(rest)} = rb_ary_new2(0);"
133
+ }
134
+ end
135
+
136
+ # handle block arg if available
137
+ if block_arg
138
+ ensure_node_type(block_arg, :block_arg)
139
+ c_if("rb_block_given_p()") {
140
+ l "#{scope.get_lvar(block_arg.last[:vid])} = rb_block_proc();"
141
+ }
142
+ # no else, lvars are initialized to nil
143
+ end
144
+ end
145
+
146
+ def comp(node)
147
+ case node
148
+ when false
149
+ "Qnil"
150
+ when String
151
+ node
152
+ else
153
+ while (ntype = node.first) == :newline
154
+ node = node.last[:next]
155
+ end
156
+ orig_node = node
157
+ l "/* #{ntype} */"
158
+ begin
159
+ if (pps = compiler.preprocessors_for(ntype))
160
+ # apply each preprocessor until one returns a string or
161
+ # a different node type
162
+ pps.each { |pp_proc|
163
+ node = pp_proc[self, node]
164
+ break unless Array === node && node.first == ntype
165
+ }
166
+ end
167
+ if Array === node && node.first == ntype
168
+ __send__("comp_#{ntype}", node.last)
169
+ else
170
+ # retry with the result of preprocessing
171
+ comp(node)
172
+ end
173
+ rescue Ruby2CExtError => e
174
+ if Hash === orig_node.last && (n = orig_node.last[:node])
175
+ # add file and line to message
176
+ raise "#{n.file}:#{n.line}: #{e}"
177
+ else
178
+ raise # reraise
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ def comp_block(exprs)
185
+ return "Qnil" if exprs.empty?
186
+ last = exprs.last
187
+ exprs[0..-2].each { |ex|
188
+ l "#{comp(ex)};"
189
+ }
190
+ comp(last)
191
+ end
192
+
193
+ def comp_vcall(hash)
194
+ if scope.vmode_method?(hash[:mid])
195
+ "Qnil"
196
+ else
197
+ "rb_funcall2(#{get_self}, #{sym(hash[:mid])}, 0, 0)"
198
+ end
199
+ end
200
+
201
+ def build_c_arr(arr, var_name, extra = 0)
202
+ l "VALUE #{var_name}[#{arr.size + extra}];"
203
+ arr.each_with_index { |n, i|
204
+ l "#{var_name}[#{i}] = #{comp(n)};"
205
+ }
206
+ end
207
+ def build_args(args, one_extra = false)
208
+ if args.first == :array
209
+ l "const int argc = #{args.last.size};"
210
+ build_c_arr(args.last, "argv", one_extra ? 1 : 0)
211
+ else
212
+ l "int argc; VALUE *argv;"
213
+ l "volatile VALUE argv_ary;" if in_while?
214
+ assign_res(comp(args))
215
+ l "if (TYPE(res) != T_ARRAY) res = rb_ary_to_ary(res);"
216
+ l "argc = RARRAY(res)->len;"
217
+ if in_while?
218
+ # don't use ALLOCA_N in a while loop to avoid a stack overflow
219
+ l "argv_ary = rb_ary_dup(res);"
220
+ l "rb_ary_push(argv_ary, Qnil);" if one_extra
221
+ l "argv = RARRAY(argv_ary)->ptr;"
222
+ else
223
+ l "argv = ALLOCA_N(VALUE, argc#{one_extra ? " + 1" : ""});"
224
+ l "MEMCPY(argv, RARRAY(res)->ptr, VALUE, argc);"
225
+ end
226
+ end
227
+ end
228
+
229
+ NON_ITER_PROC = proc { |str, args, arg_types| str % args }
230
+
231
+ def do_funcall(recv, mid, args, allow_private, iter_proc)
232
+ iter_proc ||= NON_ITER_PROC
233
+ fun = "rb_funcall#{allow_private ? 2 : 3}"
234
+ if args
235
+ c_scope_res {
236
+ l "VALUE recv = #{recv};"
237
+ build_args(args)
238
+ iter_proc["#{fun}(%s, #{sym(mid)}, %s, %s)", %w[recv argc argv], %w[VALUE int VALUE*]]
239
+ }
240
+ else
241
+ if allow_private && iter_proc == NON_ITER_PROC
242
+ if scope.vmode_method?(mid)
243
+ return "Qnil"
244
+ end
245
+ end
246
+ iter_proc["#{fun}(%s, #{sym(mid)}, 0, 0)", [recv], %w[VALUE]]
247
+ end
248
+ end
249
+
250
+ def comp_fcall(hash, &iter_proc)
251
+ do_funcall(get_self, hash[:mid], hash[:args], true, iter_proc)
252
+ end
253
+
254
+ def comp_call(hash, &iter_proc)
255
+ do_funcall(comp(hash[:recv]), hash[:mid], hash[:args], false, iter_proc)
256
+ end
257
+
258
+ def comp_attrasgn(hash)
259
+ fun = "rb_funcall#{hash[:recv] == 0 ? 2 : 3}"
260
+ recv = (hash[:recv] == 0 ? get_self : comp(hash[:recv]))
261
+ c_scope_res {
262
+ l "VALUE recv = #{recv};"
263
+ build_args(hash[:args])
264
+ l "#{fun}(recv, #{sym(hash[:mid])}, argc, argv);"
265
+ "argv[argc - 1]"
266
+ }
267
+ end
268
+
269
+ def helper_super_allowed_check
270
+ # last_func is set to 0 by rb_require_safe
271
+ add_helper <<-EOC
272
+ static void super_allowed_check() {
273
+ if (!ruby_frame->last_func) {
274
+ rb_raise(rb_eNoMethodError, "super called outside of method");
275
+ }
276
+ }
277
+ EOC
278
+ end
279
+
280
+ if is_ruby_185
281
+ def comp_zsuper(hash, &iter_proc)
282
+ if CFunction::Method === self
283
+ # super zsuper only in a method in 1.8.5
284
+ iter_proc ||= NON_ITER_PROC
285
+ helper_super_allowed_check
286
+ l "super_allowed_check();"
287
+ # this is not 100% equivalent to 1.8.5 behavior ...
288
+ iter_proc["rb_call_super(%s, %s)", %w[meth_argc meth_argv], %w[int VALUE*]]
289
+ else
290
+ raise Ruby2CExtError::NotSupported, "super without explicit arguments is not supported here"
291
+ end
292
+ end
293
+ else
294
+ def comp_zsuper(hash, &iter_proc)
295
+ iter_proc ||= NON_ITER_PROC
296
+ helper_super_allowed_check
297
+ l "super_allowed_check();"
298
+ iter_proc["rb_call_super(ruby_frame->argc, ruby_frame->argv)", [], []]
299
+ end
300
+ end
301
+
302
+ def comp_super(hash, &iter_proc)
303
+ iter_proc ||= NON_ITER_PROC
304
+ helper_super_allowed_check
305
+ l "super_allowed_check();"
306
+ args = hash[:args]
307
+ if args
308
+ c_scope_res {
309
+ build_args(args)
310
+ iter_proc["rb_call_super(%s, %s)", %w[argc argv], %w[int VALUE*]]
311
+ }
312
+ else
313
+ iter_proc["rb_call_super(0, 0)", [], []]
314
+ end
315
+ end
316
+
317
+ def comp_lit(hash)
318
+ case (l = hash[:lit])
319
+ when Fixnum
320
+ "LONG2FIX(#{l.inspect})"
321
+ when Symbol
322
+ "ID2SYM(#{sym(l)})"
323
+ when Bignum
324
+ global_const("rb_cstr_to_inum(#{l.to_s.to_c_strlit}, 10, Qfalse)")
325
+ when Float
326
+ global_const("rb_float_new(%.40e)" % l)
327
+ when Range
328
+ global_const("rb_range_new(#{comp_lit(:lit=>l.first)}, #{comp_lit(:lit=>l.last)}, Q#{l.exclude_end?})")
329
+ when Regexp
330
+ s = l.source
331
+ global_const("rb_reg_new(#{s.to_c_strlit}, #{s.size}, #{l.options})")
332
+ else
333
+ raise Ruby2CExtError::Bug, "unsupported literal type: #{l.inspect}"
334
+ end
335
+ end
336
+
337
+ def comp_nil(hash); "Qnil"; end
338
+ def comp_false(hash); "Qfalse"; end
339
+ def comp_true(hash); "Qtrue"; end
340
+ def comp_self(hash); get_self; end
341
+
342
+ def comp_argscat(hash)
343
+ c_scope_res {
344
+ l "VALUE head, body;"
345
+ l "head = #{comp(hash[:head])};"
346
+ l "body = #{comp(hash[:body])};"
347
+ l "body = (NIL_P(body) ? rb_ary_new3(1, Qnil) : rb_Array(body));"
348
+ "rb_ary_concat(head, body)"
349
+ }
350
+ end
351
+ def comp_argspush(hash)
352
+ # argspush is used in a[2,*a]=4 for example
353
+ c_scope_res {
354
+ l "VALUE head;"
355
+ l "head = rb_ary_dup(#{comp(hash[:head])});"
356
+ "rb_ary_push(head, #{comp(hash[:body])})"
357
+ }
358
+ end
359
+
360
+ def comp_begin(hash)
361
+ comp(make_block(hash[:body]))
362
+ end
363
+
364
+ def comp_if(hash)
365
+ cond = comp(hash[:cond])
366
+ if !hash[:body] && !hash[:else]
367
+ l cond + ";"
368
+ "Qnil"
369
+ else
370
+ c_if("RTEST(#{cond})") {
371
+ assign_res(comp(hash[:body]))
372
+ }
373
+ c_else {
374
+ assign_res(comp(hash[:else]))
375
+ }
376
+ "res"
377
+ end
378
+ end
379
+
380
+ def comp_postexe(hash, &iter_proc)
381
+ raise Ruby2CExtError, "postexe only allowed with iter" unless iter_proc
382
+ c_scope {
383
+ l "static int done = 0;"
384
+ c_if("!done") {
385
+ l "done = 1;"
386
+ # compile as at_exit (rb_f_END() is static)
387
+ l do_funcall(get_self, :at_exit, false, true, iter_proc)
388
+ }
389
+ }
390
+ "Qnil"
391
+ end
392
+
393
+ def comp_match(hash)
394
+ "rb_reg_match2(#{comp_lit(hash)})"
395
+ end
396
+ def comp_match2(hash)
397
+ c_scope_res {
398
+ l "VALUE recv;"
399
+ l "recv = #{comp(hash[:recv])};"
400
+ "rb_reg_match(recv, #{comp(hash[:value])})"
401
+ }
402
+ end
403
+ def comp_match3(hash)
404
+ c_scope_res {
405
+ l "VALUE recv, val;"
406
+ l "recv = #{comp(hash[:recv])};"
407
+ l "val = #{comp(hash[:value])};"
408
+ "(TYPE(val) == T_STRING ? rb_reg_match(recv, val) : rb_funcall(val, #{sym(:=~)}, 1, recv))"
409
+ }
410
+ end
411
+
412
+ def handle_when(hash, test_proc)
413
+ ensure_node_type(head = hash[:head], :array)
414
+ c_scope_res {
415
+ l "int when_handle = 1;"
416
+ head.last.each { |check|
417
+ if check.first == :when
418
+ l "VALUE arr; long i;"
419
+ l "arr = #{comp(check.last[:head])};"
420
+ l "if (TYPE(arr) != T_ARRAY) arr = rb_ary_to_ary(arr);"
421
+ c_for("i = 0; i < RARRAY(arr)->len; ++i") {
422
+ c_if("RTEST(#{test_proc["RARRAY(arr)->ptr[i]"]})") {
423
+ l "arr = Qfalse;"
424
+ l "break;"
425
+ }
426
+ }
427
+ l "if (RTEST(arr)) {"
428
+ else
429
+ l "if (!RTEST(#{test_proc[check]})) {"
430
+ end
431
+ }
432
+ l "when_handle = 0;" # here all checks failed
433
+ head.last.size.times { l "}" }
434
+ c_if("when_handle") {
435
+ assign_res(comp(hash[:body]))
436
+ }
437
+ c_else {
438
+ if Array === hash[:next] && hash[:next].first == :when
439
+ assign_res(handle_when(hash[:next].last, test_proc))
440
+ else
441
+ assign_res(comp(hash[:next]))
442
+ end
443
+ }
444
+ "res"
445
+ }
446
+ end
447
+ def comp_when(hash)
448
+ handle_when(hash, proc { |val| comp(val) })
449
+ end
450
+ def comp_case(hash)
451
+ ensure_node_type(hash[:body], :when)
452
+ c_scope_res {
453
+ l "VALUE case_val;"
454
+ l "case_val = #{comp(hash[:head])};"
455
+ handle_when(hash[:body].last, proc { |val|
456
+ # Ruby 1.8.4 actually uses rb_funcall2, but a :call node
457
+ # (which uses rb_funcall3) is more correct
458
+ comp([:call, {:mid => :===, :recv => val, :args => [:array, ["case_val"]]}])
459
+ })
460
+ }
461
+ end
462
+
463
+ def comp_while(hash, is_until = false)
464
+ redo_lbl = un("while_redo")
465
+ next_lbl = un("while_next")
466
+ break_lbl = un("while_break")
467
+ c_scope_res {
468
+ l "VALUE while_res = Qnil;"
469
+ c_for(";;") {
470
+ l "if (#{is_until ? "" : "!"}RTEST(#{comp(hash[:cond])})) break;" if hash[:state] != 0
471
+ l "#{redo_lbl}:"
472
+ push_while(redo_lbl, next_lbl, break_lbl)
473
+ l "#{comp(hash[:body])};"
474
+ pop_while
475
+ l "#{next_lbl}: ;"
476
+ l "if (#{is_until ? "" : "!"}RTEST(#{comp(hash[:cond])})) break;" if hash[:state] == 0
477
+ }
478
+ l "#{break_lbl}:"
479
+ "while_res"
480
+ }
481
+ end
482
+ def comp_until(hash)
483
+ comp_while(hash, true)
484
+ end
485
+
486
+ def handle_iter(iter, bl_fun, closure)
487
+ ensure_node_type(iter, [:call, :fcall, :super, :zsuper, :postexe]) # attrasgn ???
488
+ l "do {"
489
+ block_calls = 0
490
+ assign_res(__send__("comp_#{iter.first}", iter.last) { |str, args, arg_types|
491
+ block_calls += 1
492
+ c_scope_res {
493
+ l "VALUE iter_data[#{args.size + 1}];"
494
+ l "iter_data[0] = Qfalse;"
495
+ args.each_with_index { |arg, i|
496
+ l "iter_data[#{i + 1}] = (VALUE)(#{arg});"
497
+ }
498
+ iter_data_cast = []
499
+ arg_types.each_with_index { |arg_type, i|
500
+ iter_data_cast << "(#{arg_type})(iter_data[#{i + 1}])"
501
+ }
502
+ it_fun = compiler.add_fun(<<-EOC.chomp % iter_data_cast, "iterate")
503
+ static VALUE FUNNAME(VALUE data) {
504
+ VALUE *iter_data = (VALUE*)data;
505
+ ruby_top_self = org_ruby_top_self;
506
+ if (iter_data[0]) return Qundef;
507
+ iter_data[0] = Qtrue;
508
+ return #{str};
509
+ }
510
+ EOC
511
+ # hack to make instance_eval etc. work (rb_iterate() uses ruby_top_self as block.self)
512
+ # this _should_ be save, because rb_trap_immediate should always be 0 here ...
513
+ l "ruby_top_self = Qundef;"
514
+ "rb_iterate(#{it_fun}, (VALUE)iter_data, #{bl_fun}, #{closure})"
515
+ }
516
+ })
517
+ raise Ruby2CExtError::Bug, "internal error while compiling iter" unless block_calls == 1
518
+ l "}"
519
+ l "while (res == Qundef);"
520
+ "res"
521
+ end
522
+
523
+ def comp_block_pass(hash)
524
+ # TODO: is just a workaround/hack, does not work with instance_eval etc.
525
+ c_scope_res {
526
+ l "VALUE proc;"
527
+ l "proc = #{comp(hash[:body])};"
528
+ iter = hash[:iter]
529
+ c_if("NIL_P(proc)") {
530
+ assign_res(__send__("comp_#{iter.first}", iter.last))
531
+ }
532
+ c_else {
533
+ # rb_obj_is_proc is static in eval.c, so we just convert and hope the best...
534
+ add_helper <<-EOC
535
+ #define PROC_TSHIFT (FL_USHIFT+1)
536
+ #define PROC_TMASK (FL_USER1|FL_USER2|FL_USER3)
537
+ static VALUE obj_to_proc(VALUE proc) {
538
+ VALUE tmp;
539
+ tmp = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
540
+ if (rb_class_real(CLASS_OF(tmp)) != rb_cProc)
541
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc)", rb_obj_classname(proc));
542
+ proc = tmp;
543
+ if (ruby_safe_level >= 1 && OBJ_TAINTED(proc) &&
544
+ ruby_safe_level > ((RBASIC(proc)->flags & PROC_TMASK) >> PROC_TSHIFT))
545
+ rb_raise(rb_eSecurityError, "Insecure: tainted block value");
546
+ return proc;
547
+ }
548
+ EOC
549
+ l "proc = obj_to_proc(proc);"
550
+ add_helper <<-EOC
551
+ static VALUE block_pass_helper_block(VALUE bl_val, VALUE proc, VALUE self) {
552
+ if (ruby_current_node->nd_state != 1) {
553
+ if (bl_val == Qundef) bl_val = rb_ary_new2(0);
554
+ else {
555
+ VALUE tmp = rb_check_array_type(bl_val);
556
+ bl_val = (NIL_P(tmp) ? rb_ary_new3(1, bl_val) : tmp);
557
+ }
558
+ }
559
+ if (RARRAY(bl_val)->len == 0) return rb_funcall3(proc, #{sym(:call)}, 0, 0);
560
+ else {
561
+ int argc = RARRAY(bl_val)->len;
562
+ VALUE *argv = ALLOCA_N(VALUE, argc);
563
+ MEMCPY(argv, RARRAY(bl_val)->ptr, VALUE, argc);
564
+ return rb_funcall3(proc, #{sym(:call)}, argc, argv);
565
+ }
566
+ }
567
+ EOC
568
+ assign_res(handle_iter(iter, "block_pass_helper_block", "proc"))
569
+ }
570
+ "res"
571
+ }
572
+ end
573
+
574
+ def comp_for(hash)
575
+ # transform to equivalent iter node
576
+ hash = hash.dup
577
+ hash[:iter] = [:call, {:args=>false, :mid=>:each, :recv=>hash[:iter]}]
578
+ comp_iter(hash)
579
+ end
580
+
581
+ def comp_iter(hash)
582
+ bl_fun, need_clos = CFunction::Block.compile(self, make_block(hash[:body]), hash[:var])
583
+ handle_iter(hash[:iter], bl_fun, need_clos ? get_closure_ary_var : "Qnil")
584
+ end
585
+
586
+ def comp_break(hash)
587
+ # must be implemented by the "user" of CommonNodeComp
588
+ raise Ruby2CExtError::NotSupported, "break is not supported"
589
+ end
590
+ def comp_next(hash)
591
+ # must be implemented by the "user" of CommonNodeComp
592
+ raise Ruby2CExtError::NotSupported, "next is not supported"
593
+ end
594
+ def comp_redo(hash)
595
+ # must be implemented by the "user" of CommonNodeComp
596
+ raise Ruby2CExtError::NotSupported, "redo is not supported"
597
+ end
598
+ def comp_return(hash)
599
+ # must be implemented by the "user" of CommonNodeComp
600
+ raise Ruby2CExtError::NotSupported, "return is not supported"
601
+ end
602
+ def comp_retry(hash)
603
+ # must be implemented by the "user" of CommonNodeComp
604
+ raise Ruby2CExtError::NotSupported, "retry is not supported"
605
+ end
606
+
607
+ def comp_splat(hash)
608
+ assign_res(comp(hash[:head]))
609
+ "(NIL_P(res) ? rb_ary_new3(1, Qnil) : rb_Array(res))"
610
+ end
611
+ def comp_to_ary(hash)
612
+ "rb_ary_to_ary(#{comp(hash[:head])})"
613
+ end
614
+ def comp_svalue(hash)
615
+ assign_res(comp(hash[:head]))
616
+ "(RARRAY(res)->len == 1 ? RARRAY(res)->ptr[0] : (RARRAY(res)->len == 0 ? Qnil : res))"
617
+ end
618
+
619
+ def comp_yield(hash)
620
+ val = (hash[:head] ? comp(hash[:head]) : "Qundef")
621
+ "rb_yield#{hash[:state] == 0 ? "" : "_splat"}(#{val})"
622
+ end
623
+
624
+ def comp_rescue(hash)
625
+ ensure_node_type(resb = hash[:resq], :resbody)
626
+ # compile the real body
627
+ cflow_hash = {}
628
+ body = CFunction::Wrap.compile(self, "rescue_body", cflow_hash) { |cf|
629
+ cf.instance_eval {
630
+ l "#{get_wrap_ptr}->state |= 1;"
631
+ comp(hash[:head])
632
+ }
633
+ }
634
+ # now all the resbodies in one c function
635
+ res_bodies = CFunction::Wrap.compile(self, "rescue_resbodies", cflow_hash) { |cf|
636
+ cf.instance_eval {
637
+ cnt = 0
638
+ while resb
639
+ cnt += 1
640
+ args = resb.last[:args]
641
+ unless args
642
+ l "if (rb_obj_is_kind_of(ruby_errinfo, rb_eStandardError)) {"
643
+ else
644
+ add_helper <<-EOC
645
+ static void rescue_mod_check(VALUE mod) {
646
+ if (!rb_obj_is_kind_of(mod, rb_cModule)) {
647
+ rb_raise(rb_eTypeError, "class or module required for rescue clause");
648
+ }
649
+ }
650
+ EOC
651
+ if args.first == :array # TODO
652
+ # ruby doesn't handle this case specially, but it might be faster this way
653
+ # (on the other side: we might miss exceptions (for not evaluated entries of the array))
654
+ args.last.each { |ex|
655
+ assign_res(comp(ex))
656
+ l "rescue_mod_check(res);"
657
+ l "if (!RTEST(rb_funcall(res, #{sym(:===)}, 1, ruby_errinfo))) {"
658
+ }
659
+ # non did match
660
+ assign_res("Qfalse")
661
+ # close all ifs
662
+ args.last.size.times { l "}" }
663
+ else
664
+ c_scope {
665
+ l "int i = 0;"
666
+ # TODO: ruby has BEGIN_CALLARGS protection here, is this really necessary???
667
+ build_args(args)
668
+ assign_res("Qfalse")
669
+ c_for("; i < argc; ++i") {
670
+ l "rescue_mod_check(argv[i]);"
671
+ c_if("RTEST(rb_funcall(argv[i], #{sym(:===)}, 1, ruby_errinfo))") {
672
+ assign_res("Qtrue");
673
+ l "break;"
674
+ }
675
+ }
676
+ }
677
+ end
678
+ l "if (res) {"
679
+ end
680
+ l "#{get_wrap_ptr}->state &= ~1;" # set first bit = 0
681
+ assign_res(comp(resb.last[:body]))
682
+ l "}"
683
+ l "else {"
684
+ resb = resb.last[:head]
685
+ end
686
+ # we are in the last else, if the exception wasn't handled, then reraise
687
+ l "rb_jump_tag(0x6 /* TAG_RAISE */);"
688
+ # close all elses
689
+ cnt.times { l "}" }
690
+ }
691
+ "res"
692
+ }
693
+ # now call rb_rescue2 with the two bodies (and handle else if necessary)
694
+ c_scope_res {
695
+ else_node = hash[:else]
696
+ l "long save_state = #{get_wrap_ptr}->state;"
697
+ l "long do_else;" if else_node
698
+ wp = "(VALUE)#{get_wrap_ptr}"
699
+ assign_res("rb_rescue2(#{body}, #{wp}, #{res_bodies}, #{wp}, rb_eException, (VALUE)0)")
700
+ l "do_else = #{get_wrap_ptr}->state & 1;" if else_node
701
+ l "#{get_wrap_ptr}->state = (#{get_wrap_ptr}->state & ~1) | (save_state & 1);" # restore the 1st bit of save_state
702
+ CFunction::Wrap::handle_wrap_cflow(self, cflow_hash)
703
+ if else_node
704
+ c_if("do_else") {
705
+ assign_res(comp(else_node))
706
+ }
707
+ end
708
+ "res"
709
+ }
710
+ end
711
+
712
+ def comp_ensure(hash)
713
+ cflow_hash = {}
714
+ b = CFunction::Wrap.compile(self, "ensure_body", cflow_hash) { |cf| cf.comp(hash[:head]) }
715
+ e = CFunction::Wrap.compile(self, "ensure_ensure") { |cf| cf.comp(hash[:ensr]) } # ensr without cflow
716
+ ensr_code = "rb_ensure(#{b}, (VALUE)#{get_wrap_ptr}, #{e}, (VALUE)#{get_wrap_ptr})"
717
+ if cflow_hash.empty?
718
+ ensr_code
719
+ else
720
+ assign_res(ensr_code)
721
+ CFunction::Wrap::handle_wrap_cflow(self, cflow_hash)
722
+ "res"
723
+ end
724
+ end
725
+
726
+ def comp_and(hash)
727
+ assign_res(comp(hash[:first]))
728
+ c_if("RTEST(res)") {
729
+ assign_res(comp(hash[:second]))
730
+ }
731
+ "res"
732
+ end
733
+ def comp_or(hash)
734
+ assign_res(comp(hash[:first]))
735
+ c_if("!RTEST(res)") {
736
+ assign_res(comp(hash[:second]))
737
+ }
738
+ "res"
739
+ end
740
+ def comp_not(hash)
741
+ "(RTEST(#{comp(hash[:body])}) ? Qfalse : Qtrue)"
742
+ end
743
+
744
+ def handle_dot(hash, dot3)
745
+ c_scope_res {
746
+ l "VALUE beg;"
747
+ l "beg = #{comp(hash[:beg])};"
748
+ "rb_range_new(beg, #{comp(hash[:end])}, Q#{dot3})"
749
+ }
750
+ end
751
+ def comp_dot2(hash)
752
+ handle_dot(hash, false)
753
+ end
754
+ def comp_dot3(hash)
755
+ handle_dot(hash, true)
756
+ end
757
+
758
+ def handle_flip(hash, flip3)
759
+ flip_var = scope.get_lvar_idx(hash[:cnt] - 2)
760
+ c_if("RTEST(#{flip_var})") {
761
+ l "if (RTEST(#{comp(hash[:end])})) #{flip_var} = Qfalse;"
762
+ assign_res("Qtrue")
763
+ }
764
+ c_else {
765
+ if flip3
766
+ assign_res("(RTEST(#{comp(hash[:beg])}) ? Qtrue : Qfalse)")
767
+ l "#{flip_var} = res;"
768
+ else
769
+ assign_res(c_scope_res {
770
+ l "VALUE beg_res;"
771
+ l "beg_res = (RTEST(#{comp(hash[:beg])}) ? Qtrue : Qfalse);"
772
+ c_if("beg_res") {
773
+ l "#{flip_var} = (RTEST(#{comp(hash[:end])}) ? Qfalse : Qtrue);"
774
+ }
775
+ "beg_res"
776
+ })
777
+ end
778
+ }
779
+ "res"
780
+ end
781
+ def comp_flip2(hash)
782
+ handle_flip(hash, false)
783
+ end
784
+ def comp_flip3(hash)
785
+ handle_flip(hash, true)
786
+ end
787
+
788
+ if is_ruby_185
789
+ # Ruby 1.8.5
790
+ def comp_op_asgn1(hash)
791
+ ensure_node_type(hash[:args], :argscat)
792
+ c_scope_res {
793
+ l "VALUE oa1_recv, oa1_val;"
794
+ l "oa1_recv = #{comp(hash[:recv])};"
795
+ c_scope_res {
796
+ build_args(hash[:args].last[:body], true)
797
+ l "oa1_val = rb_funcall3(oa1_recv, #{sym(:[])}, argc, argv);"
798
+ mid = hash[:mid]
799
+ rval = hash[:args].last[:head]
800
+ if Symbol === mid
801
+ call = [:call, {:recv=>"oa1_val", :mid=>mid, :args=>[:array, [rval]]}]
802
+ l "oa1_val = #{comp(call)};"
803
+ l "{"
804
+ else
805
+ # mid == 0 is OR, mid == 1 is AND
806
+ l "if (#{mid == 0 ? "!" : ""}RTEST(oa1_val)) {"
807
+ l "oa1_val = #{comp(rval)};"
808
+ end
809
+ l "argv[argc] = oa1_val;"
810
+ l "rb_funcall2(oa1_recv, #{sym(:[]=)}, argc + 1, argv);"
811
+ l "}"
812
+ "oa1_val"
813
+ }
814
+ }
815
+ end
816
+ def comp_op_asgn2(hash)
817
+ ensure_node_type(ids = hash[:next], :op_asgn2)
818
+ ids = ids.last
819
+ c_scope_res {
820
+ l "VALUE oa2_recv, oa2_val;"
821
+ l "oa2_recv = #{comp(hash[:recv])};"
822
+ call = [:call, {:recv=>"oa2_recv", :mid=>ids[:vid], :args=>false}]
823
+ l "oa2_val = #{comp(call)};"
824
+ mid = ids[:mid]
825
+ if Symbol === mid
826
+ call = [:call, {:recv=>"oa2_val", :mid=>mid, :args=>[:array, [hash[:value]]]}]
827
+ l "oa2_val = #{comp(call)};"
828
+ l "{"
829
+ else
830
+ # mid == 0 is OR, mid == 1 is AND
831
+ l "if (#{mid == 0 ? "!" : ""}RTEST(oa2_val)) {"
832
+ l "oa2_val = #{comp(hash[:value])};"
833
+ end
834
+ l "rb_funcall2(oa2_recv, #{sym(ids[:aid])}, 1, &oa2_val);"
835
+ l "}"
836
+ "oa2_val"
837
+ }
838
+ end
839
+ else
840
+ # Ruby 1.8.4
841
+ def comp_op_asgn1(hash)
842
+ ensure_node_type(hash[:args], :array)
843
+ c_scope_res {
844
+ l "VALUE recv, val;"
845
+ l "recv = #{comp(hash[:recv])};"
846
+ c_scope_res {
847
+ build_args([:array, hash[:args].last[1..-1]])
848
+ l "val = rb_funcall2(recv, #{sym(:[])}, argc-1, argv);"
849
+ mid = hash[:mid]
850
+ if Symbol === mid
851
+ l "val = rb_funcall(val, #{sym(mid)}, 1, #{comp(hash[:args].last.first)});"
852
+ l "{"
853
+ else
854
+ # mid == 0 is OR, mid == 1 is AND
855
+ l "if (#{mid == 0 ? "!" : ""}RTEST(val)) {"
856
+ l "val = #{comp(hash[:args].last.first)};"
857
+ end
858
+ l "argv[argc-1] = val;"
859
+ l "rb_funcall2(recv, #{sym(:[]=)}, argc, argv);"
860
+ l "}"
861
+ "val"
862
+ }
863
+ }
864
+ end
865
+ def comp_op_asgn2(hash)
866
+ ensure_node_type(ids = hash[:next], :op_asgn2)
867
+ ids = ids.last
868
+ c_scope_res {
869
+ l "VALUE recv, val;"
870
+ l "recv = #{comp(hash[:recv])};"
871
+ l "val = rb_funcall(recv, #{sym(ids[:vid])}, 0);"
872
+ mid = ids[:mid]
873
+ if Symbol === mid
874
+ l "val = rb_funcall(val, #{sym(mid)}, 1, #{comp(hash[:value])});"
875
+ l "{"
876
+ else
877
+ # mid == 0 is OR, mid == 1 is AND
878
+ l "if (#{mid == 0 ? "!" : ""}RTEST(val)) {"
879
+ l "val = #{comp(hash[:value])};"
880
+ end
881
+ l "rb_funcall2(recv, #{sym(ids[:aid])}, 1, &val);"
882
+ l "}"
883
+ "val"
884
+ }
885
+ end
886
+ end
887
+
888
+ def comp_op_asgn_and(hash)
889
+ assign_res(comp(hash[:head]))
890
+ c_if("RTEST(res)") {
891
+ assign_res(comp(hash[:value]))
892
+ }
893
+ "res"
894
+ end
895
+ def comp_op_asgn_or(hash)
896
+ if Symbol === (aid = hash[:aid])
897
+ def_test =
898
+ case aid.to_s
899
+ when /\A@@/
900
+ "rb_cvar_defined(#{get_cvar_cbase}, #{sym(aid)})"
901
+ when /\A@/
902
+ "rb_ivar_defined(#{get_self}, #{sym(aid)})"
903
+ when /\A\$/
904
+ "rb_gvar_defined(#{get_global_entry(aid)})"
905
+ else
906
+ raise Ruby2CExtError::Bug, "unexpected aid for op_asgn_or: #{aid.inspect}"
907
+ end
908
+ c_if(def_test) {
909
+ assign_res(comp(hash[:head]))
910
+ }
911
+ c_else {
912
+ assign_res("Qnil")
913
+ }
914
+ else
915
+ assign_res(comp(hash[:head]))
916
+ end
917
+ c_if("!RTEST(res)") {
918
+ assign_res(comp(hash[:value]))
919
+ }
920
+ "res"
921
+ end
922
+
923
+ def comp_masgn(hash)
924
+ c_scope_res {
925
+ l "VALUE ma_val;"
926
+ l "long ma_len;"
927
+ l "ma_val = #{comp(hash[:value])};"
928
+ l "ma_len = RARRAY(ma_val)->len;"
929
+ head_len = 0
930
+ if (head = hash[:head])
931
+ ensure_node_type(head, :array)
932
+ head_len = head.last.size
933
+ head.last.each_with_index { |asgn, i|
934
+ handle_assign(asgn, "(#{i} < ma_len ? RARRAY(ma_val)->ptr[#{i}] : Qnil)", false)
935
+ }
936
+ end
937
+ if ((args = hash[:args]) && (args != -1))
938
+ handle_assign(args, "(#{head_len} < ma_len ? rb_ary_new4(ma_len-#{head_len}, " +
939
+ "RARRAY(ma_val)->ptr+#{head_len}) : rb_ary_new2(0))", false)
940
+ end
941
+ "ma_val"
942
+ }
943
+ end
944
+
945
+ def handle_assign(asgn_node, val, undef_check = true)
946
+ c_scope {
947
+ if undef_check
948
+ l "VALUE as_val;"
949
+ l "as_val = #{comp(val)};"
950
+ l "if (as_val == Qundef) as_val = Qnil;"
951
+ val = "as_val"
952
+ end
953
+ dup_node = [asgn_node.first, asgn_node.last.dup]
954
+ case asgn_node.first
955
+ when :lasgn, :dasgn, :dasgn_curr, :iasgn, :gasgn, :cdecl, :cvdecl, :cvasgn
956
+ if dup_node.last[:value]
957
+ raise Ruby2CExtError, "unexpected value in #{asgn_node.first} node in handle_assign"
958
+ end
959
+ dup_node.last[:value] = val
960
+ l "#{comp(dup_node)};"
961
+ when :masgn
962
+ c_scope {
963
+ l "VALUE as_ma_val;"
964
+ l "VALUE tmp;" if (ma_head = asgn_node.last[:head])
965
+ l "as_ma_val = #{comp(val)};"
966
+ # adapted from svalue_to_mrhs()
967
+ if ma_head
968
+ l "tmp = rb_check_array_type(as_ma_val);"
969
+ l "as_ma_val = (NIL_P(tmp) ? rb_ary_new3(1, as_ma_val) : tmp);"
970
+ else
971
+ l "as_ma_val = rb_ary_new3(1, as_ma_val);"
972
+ end
973
+ dup_node.last[:value] = "as_ma_val"
974
+ l "#{comp(dup_node)};"
975
+ }
976
+ when :attrasgn # TODO: can :call also appear here ???
977
+ c_scope {
978
+ l "VALUE as_aa_val;"
979
+ l "as_aa_val = #{comp(val)};"
980
+ if asgn_node.last[:args]
981
+ dup_node.last[:args] = [:argspush, {:body => "as_aa_val", :head => asgn_node.last[:args]}]
982
+ else
983
+ dup_node.last[:args] = [:array, ["as_aa_val"]]
984
+ end
985
+ l "#{comp(dup_node)};"
986
+ }
987
+ else
988
+ raise Ruby2CExtError::Bug, "unexpected assign node type: #{asgn_node.first}"
989
+ end
990
+ }
991
+ end
992
+
993
+ def comp_lasgn(hash)
994
+ "(#{scope.get_lvar(hash[:vid])} = #{comp(hash[:value])})"
995
+ end
996
+ def comp_dasgn(hash)
997
+ "(#{scope.get_dvar(hash[:vid])} = #{comp(hash[:value])})"
998
+ end
999
+ def comp_dasgn_curr(hash)
1000
+ "(#{scope.get_dvar_curr(hash[:vid])} = #{comp(hash[:value])})"
1001
+ end
1002
+ def comp_gasgn(hash)
1003
+ assign_res(comp(hash[:value]))
1004
+ l "rb_gvar_set(#{get_global_entry(hash[:vid])}, res);"
1005
+ "res"
1006
+ end
1007
+ def comp_iasgn(hash)
1008
+ assign_res(comp(hash[:value]))
1009
+ l "rb_ivar_set(#{get_self}, #{sym(hash[:vid])}, res);"
1010
+ "res"
1011
+ end
1012
+
1013
+ def helper_class_module_check
1014
+ add_helper <<-EOC
1015
+ static void class_module_check(VALUE klass) {
1016
+ switch (TYPE(klass)) {
1017
+ case T_CLASS:
1018
+ case T_MODULE:
1019
+ break;
1020
+ default:
1021
+ rb_raise(rb_eTypeError, "%s is not a class/module", RSTRING(rb_obj_as_string(klass))->ptr);
1022
+ }
1023
+ }
1024
+ EOC
1025
+ end
1026
+
1027
+ def make_class_prefix(node)
1028
+ ensure_node_type(node, [:colon2, :colon3])
1029
+ if node.first == :colon2
1030
+ hash = node.last
1031
+ if hash[:head]
1032
+ assign_res(comp(hash[:head]))
1033
+ l "class_module_check(res);"
1034
+ "res"
1035
+ else
1036
+ get_cbase
1037
+ end
1038
+ else
1039
+ "rb_cObject"
1040
+ end
1041
+ end
1042
+
1043
+ def comp_cdecl(hash)
1044
+ c_scope_res {
1045
+ l "VALUE val;"
1046
+ l "val = #{comp(hash[:value])};"
1047
+ if Symbol === hash[:vid]
1048
+ l "rb_const_set(#{get_cbase}, #{sym(hash[:vid])}, val);"
1049
+ else
1050
+ l "rb_const_set(#{make_class_prefix(hash[:else])}, #{sym(hash[:else].last[:mid])}, val);"
1051
+ end
1052
+ "val"
1053
+ }
1054
+ end
1055
+ def comp_cvasgn(hash, decl = false)
1056
+ assign_res(comp(hash[:value]))
1057
+ l "rb_cvar_set(#{get_cvar_cbase}, #{sym(hash[:vid])}, res, Q#{decl});"
1058
+ "res"
1059
+ end
1060
+ def comp_cvdecl(hash)
1061
+ comp_cvasgn(hash, true)
1062
+ end
1063
+
1064
+ def comp_lvar(hash)
1065
+ "#{scope.get_lvar(hash[:vid])}"
1066
+ end
1067
+ def comp_dvar(hash)
1068
+ scope.get_dvar(hash[:vid])
1069
+ end
1070
+ def comp_gvar(hash)
1071
+ "rb_gvar_get(#{get_global_entry(hash[:vid])})"
1072
+ end
1073
+ def comp_ivar(hash)
1074
+ "rb_ivar_get(#{get_self}, #{sym(hash[:vid])})"
1075
+ end
1076
+ def comp_const(hash)
1077
+ add_helper <<-EOC
1078
+ static VALUE const_get(ID id, NODE *cref) {
1079
+ NODE *cbase = cref;
1080
+ VALUE result;
1081
+ while (cbase && cbase->nd_next) {
1082
+ VALUE klass = cbase->nd_clss;
1083
+ while (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) {
1084
+ if (result == Qundef) {
1085
+ if (!RTEST(rb_autoload_load(klass, id))) break;
1086
+ continue;
1087
+ }
1088
+ return result;
1089
+ }
1090
+ cbase = cbase->nd_next;
1091
+ }
1092
+ return rb_const_get(cref->nd_clss, id);
1093
+ }
1094
+ EOC
1095
+ "const_get(#{sym(hash[:vid])}, #{get_cref})"
1096
+ end
1097
+ def comp_cvar(hash)
1098
+ "rb_cvar_get(#{get_cvar_cbase}, #{sym(hash[:vid])})"
1099
+ end
1100
+ def comp_colon2(hash)
1101
+ mid = hash[:mid]
1102
+ if mid.to_s[0,1].downcase != mid.to_s[0,1] # then it is a constant
1103
+ helper_class_module_check
1104
+ assign_res(comp(hash[:head]))
1105
+ l "class_module_check(res);"
1106
+ "rb_const_get_from(res, #{sym(mid)})"
1107
+ else
1108
+ "rb_funcall(#{comp(hash[:head])}, #{sym(mid)}, 0, 0)"
1109
+ end
1110
+ end
1111
+ def comp_colon3(hash)
1112
+ "rb_const_get_from(rb_cObject, #{sym(hash[:mid])})"
1113
+ end
1114
+
1115
+ def comp_nth_ref(hash)
1116
+ "rb_reg_nth_match(#{hash[:nth]}, rb_backref_get())"
1117
+ end
1118
+ def comp_back_ref(hash)
1119
+ case hash[:nth]
1120
+ when ?&
1121
+ "rb_reg_last_match(rb_backref_get())"
1122
+ when ?`
1123
+ "rb_reg_match_pre(rb_backref_get())"
1124
+ when ?'
1125
+ "rb_reg_match_post(rb_backref_get())"
1126
+ when ?+
1127
+ "rb_reg_match_last(rb_backref_get())"
1128
+ else
1129
+ raise Ruby2CExtError, "unexpected back-ref type: '#{hash[:nth].chr}'"
1130
+ end
1131
+ end
1132
+
1133
+ def comp_array(arr)
1134
+ c_scope_res {
1135
+ l "VALUE ary = rb_ary_new2(#{arr.size});"
1136
+ arr.each_with_index { |n, i|
1137
+ l "RARRAY(ary)->ptr[#{i}] = #{comp(n)};"
1138
+ l "RARRAY(ary)->len = #{i+1};"
1139
+ }
1140
+ "ary"
1141
+ }
1142
+ end
1143
+ def comp_zarray(hash)
1144
+ "rb_ary_new()"
1145
+ end
1146
+
1147
+ def comp_hash(hash)
1148
+ if (arr = hash[:head])
1149
+ ensure_node_type(arr, :array)
1150
+ arr = arr.last
1151
+ raise Ruby2CExtError, "odd number list for hash" unless arr.size % 2 == 0
1152
+ c_scope_res {
1153
+ l "VALUE key, hash = rb_hash_new();"
1154
+ arr.each_with_index { |n, i|
1155
+ if i % 2 == 0
1156
+ l "key = #{comp(n)};"
1157
+ else
1158
+ l "rb_hash_aset(hash, key, #{comp(n)});"
1159
+ end
1160
+ }
1161
+ "hash"
1162
+ }
1163
+ else
1164
+ "rb_hash_new()"
1165
+ end
1166
+ end
1167
+
1168
+ def comp_str(hash)
1169
+ lit = global_const("rb_str_new(#{hash[:lit].to_c_strlit}, #{hash[:lit].size})")
1170
+ "rb_str_new3(#{lit})"
1171
+ end
1172
+ def comp_evstr(hash)
1173
+ "rb_obj_as_string(#{comp(hash[:body])})"
1174
+ end
1175
+ def handle_dyn_str(hash)
1176
+ ensure_node_type(hash[:next], :array)
1177
+ c_scope_res {
1178
+ l "VALUE str, str2;"
1179
+ l "str = #{comp_str(hash)};"
1180
+ hash[:next].last.each { |node|
1181
+ l "str2 = #{comp(node)};"
1182
+ l "rb_str_append(str, str2);"
1183
+ l "OBJ_INFECT(str, str2);"
1184
+ }
1185
+ "str"
1186
+ }
1187
+ end
1188
+ def comp_dstr(hash)
1189
+ handle_dyn_str(hash)
1190
+ end
1191
+ def comp_dxstr(hash)
1192
+ handle_dyn_str(hash)
1193
+ "rb_funcall(#{get_self}, '`', 1, res)"
1194
+ end
1195
+ def comp_dsym(hash)
1196
+ handle_dyn_str(hash)
1197
+ "rb_str_intern(res)"
1198
+ end
1199
+ def comp_dregx(hash)
1200
+ handle_dyn_str(hash)
1201
+ "rb_reg_new(RSTRING(res)->ptr, RSTRING(res)->len, #{hash[:cflag]})"
1202
+ end
1203
+ def comp_dregx_once(hash)
1204
+ c_static_once {
1205
+ comp_dregx(hash)
1206
+ }
1207
+ end
1208
+ def comp_xstr(hash)
1209
+ "rb_funcall(#{get_self}, '`', 1, #{comp_str(hash)})"
1210
+ end
1211
+
1212
+ def comp_defn(hash)
1213
+ add_helper <<-EOC
1214
+ static void class_nil_check(VALUE klass) {
1215
+ if (NIL_P(klass)) rb_raise(rb_eTypeError, "no class/module to add method");
1216
+ }
1217
+ EOC
1218
+ l "class_nil_check(#{get_class});" # can happen in instance_eval for Fixnum/Symbol
1219
+ CFunction::Method.compile(self, hash[:defn], scope.vmode_def_fun, get_class, hash[:mid])
1220
+ "Qnil"
1221
+ end
1222
+
1223
+ def comp_defs(hash)
1224
+ add_helper <<-EOC
1225
+ static void defs_allowed(VALUE recv, ID mid) {
1226
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(recv))
1227
+ rb_raise(rb_eSecurityError, "Insecure: can't define singleton method");
1228
+ if (OBJ_FROZEN(recv)) rb_error_frozen("object");
1229
+ if (ruby_safe_level >= 4) {
1230
+ NODE *body = 0;
1231
+ VALUE klass = rb_singleton_class(recv);
1232
+ if (st_lookup(RCLASS(klass)->m_tbl, mid, (st_data_t *)&body))
1233
+ rb_raise(rb_eSecurityError, "redefining method prohibited");
1234
+ }
1235
+ }
1236
+ EOC
1237
+ c_scope_res {
1238
+ l "VALUE recv;"
1239
+ l "recv = #{comp(hash[:recv])};"
1240
+ l "defs_allowed(recv, #{sym(hash[:mid])});"
1241
+ CFunction::Method.compile(self, hash[:defn], "rb_define_singleton_method", "recv", hash[:mid])
1242
+ "Qnil"
1243
+ }
1244
+ end
1245
+
1246
+ def comp_undef(hash)
1247
+ if Array === (mid = hash[:mid]) # 1.8.5
1248
+ l "rb_undef(#{get_class}, rb_to_id(#{comp(mid)}));"
1249
+ else
1250
+ l "rb_undef(#{get_class}, #{sym(mid)});"
1251
+ end
1252
+ "Qnil"
1253
+ end
1254
+
1255
+ def comp_alias(hash)
1256
+ if Array === hash[:new] # 1.8.5
1257
+ c_scope {
1258
+ l "ID new_id;"
1259
+ l "new_id = rb_to_id(#{comp(hash[:new])});"
1260
+ l "rb_alias(#{get_class}, new_id, rb_to_id(#{comp(hash[:old])}));"
1261
+ }
1262
+ else
1263
+ l "rb_alias(#{get_class}, #{sym(hash[:new])}, #{sym(hash[:old])});"
1264
+ end
1265
+ "Qnil"
1266
+ end
1267
+ def comp_valias(hash)
1268
+ l "rb_alias_variable(#{sym(hash[:new])}, #{sym(hash[:old])});"
1269
+ "Qnil"
1270
+ end
1271
+
1272
+ def comp_class(hash)
1273
+ add_helper <<-EOC
1274
+ static VALUE class_prep(VALUE prefix, VALUE super, ID cname) {
1275
+ VALUE klass;
1276
+ if (rb_const_defined_at(prefix, cname)) {
1277
+ klass = rb_const_get_at(prefix, cname);
1278
+ if (TYPE(klass) != T_CLASS) rb_raise(rb_eTypeError, "%s is not a class", rb_id2name(cname));
1279
+ if (super) {
1280
+ VALUE tmp = rb_class_real(RCLASS(klass)->super);
1281
+ if (tmp != super) rb_raise(rb_eTypeError, "superclass mismatch for class %s", rb_id2name(cname));
1282
+ }
1283
+ if (ruby_safe_level >= 4) rb_raise(rb_eSecurityError, "extending class prohibited");
1284
+ }
1285
+ else {
1286
+ if (!super) super = rb_cObject;
1287
+ klass = rb_define_class_id(cname, super);
1288
+ rb_set_class_path(klass, prefix, rb_id2name(cname));
1289
+ rb_const_set(prefix, cname, klass);
1290
+ rb_class_inherited(super, klass);
1291
+ }
1292
+ return klass;
1293
+ }
1294
+ EOC
1295
+ sup = hash[:super]
1296
+ c_scope_res {
1297
+ l "VALUE prefix, tmp_class;"
1298
+ l "VALUE super;" if sup
1299
+ l "prefix = #{make_class_prefix(hash[:cpath])};"
1300
+ l "super = #{comp(sup)};" if sup
1301
+ l "tmp_class = class_prep(prefix, #{sup ? "super" : "0"}, #{sym(hash[:cpath].last[:mid])});"
1302
+ CFunction::ClassModuleScope.compile(self, hash[:body], "tmp_class", true)
1303
+ }
1304
+ end
1305
+
1306
+ def comp_module(hash)
1307
+ add_helper <<-EOC
1308
+ static VALUE module_prep(VALUE prefix, ID cname) {
1309
+ VALUE module;
1310
+ if (rb_const_defined_at(prefix, cname)) {
1311
+ module = rb_const_get_at(prefix, cname);
1312
+ if (TYPE(module) != T_MODULE) rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(cname));
1313
+ if (ruby_safe_level >= 4) rb_raise(rb_eSecurityError, "extending module prohibited");
1314
+ }
1315
+ else {
1316
+ module = rb_define_module_id(cname);
1317
+ rb_set_class_path(module, prefix, rb_id2name(cname));
1318
+ rb_const_set(prefix, cname, module);
1319
+ }
1320
+ return module;
1321
+ }
1322
+ EOC
1323
+ c_scope_res {
1324
+ l "VALUE prefix, tmp_module;"
1325
+ l "prefix = #{make_class_prefix(hash[:cpath])};"
1326
+ l "tmp_module = module_prep(prefix, #{sym(hash[:cpath].last[:mid])});"
1327
+ CFunction::ClassModuleScope.compile(self, hash[:body], "tmp_module", false)
1328
+ }
1329
+ end
1330
+
1331
+ def comp_sclass(hash)
1332
+ add_helper <<-EOC
1333
+ static void sclass_check(VALUE obj) {
1334
+ if (FIXNUM_P(obj) || SYMBOL_P(obj)) rb_raise(rb_eTypeError, "no virtual class for %s", rb_obj_classname(obj));
1335
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(obj)) rb_raise(rb_eSecurityError, "Insecure: can't extend object");
1336
+ }
1337
+ EOC
1338
+ c_scope_res {
1339
+ l "VALUE tmp_sclass;"
1340
+ l "tmp_sclass = #{comp(hash[:recv])};"
1341
+ l "sclass_check(tmp_sclass);"
1342
+ l "tmp_sclass = rb_singleton_class(tmp_sclass);"
1343
+ CFunction::ClassModuleScope.compile(self, hash[:body], "tmp_sclass", true)
1344
+ }
1345
+ end
1346
+
1347
+ def comp_defined(hash)
1348
+ head = hash[:head]
1349
+ hhash = head.last
1350
+ res =
1351
+ case head.first
1352
+ when :match2, :match3
1353
+ '"method"'
1354
+ when :yield
1355
+ '(rb_block_given_p() ? "yield" : 0)'
1356
+ when :self, :nil, :true, :false
1357
+ head.first.to_s.to_c_strlit
1358
+ when :op_asgn1, :op_asgn2, :masgn, :lasgn, :dasgn, :dasgn_curr,
1359
+ :gasgn, :iasgn, :cdecl, :cvdecl, :cvasgn # :attrset can never be parsed
1360
+ '"assignment"'
1361
+ when :lvar
1362
+ '"local-variable"'
1363
+ when :dvar
1364
+ '"local-variable(in-block)"'
1365
+ when :gvar
1366
+ "(rb_gvar_defined(#{get_global_entry(hhash[:vid])}) ? \"global-variable\" : 0)"
1367
+ when :ivar
1368
+ "(rb_ivar_defined(#{get_self}, #{sym(hhash[:vid])}) ? \"instance-variable\" : 0)"
1369
+ when :const
1370
+ add_helper <<-EOC
1371
+ static VALUE const_defined(ID id, NODE *cref) {
1372
+ NODE *cbase = cref;
1373
+ VALUE result;
1374
+ while (cbase && cbase->nd_next) {
1375
+ VALUE klass = cbase->nd_clss;
1376
+ if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) {
1377
+ if (result == Qundef && NIL_P(rb_autoload_p(klass, id))) return Qfalse;
1378
+ return Qtrue;
1379
+ }
1380
+ cbase = cbase->nd_next;
1381
+ }
1382
+ return rb_const_defined(cref->nd_clss, id);
1383
+ }
1384
+ EOC
1385
+ "(const_defined(#{sym(hhash[:vid])}, #{get_cref}) ? \"constant\" : 0)"
1386
+ when :cvar
1387
+ "(rb_cvar_defined(#{get_cvar_cbase}, #{sym(hhash[:vid])}) ? \"class variable\" : 0)"
1388
+ when :colon3
1389
+ "(rb_const_defined_from(rb_cObject, #{sym(hhash[:mid])}) ? \"constant\" : 0)"
1390
+ when :nth_ref
1391
+ "(RTEST(rb_reg_nth_defined(#{hhash[:nth]}, rb_backref_get())) ? \"$#{hhash[:nth]}\" : 0)"
1392
+ when :back_ref
1393
+ "(RTEST(rb_reg_nth_defined(0, rb_backref_get())) ? \"$#{hhash[:nth].chr}\" : 0)"
1394
+ else
1395
+ raise Ruby2CExtError::NotSupported, "defined? with node type #{head.first} is not supported"
1396
+ end
1397
+ if res[0,1] == '"' # just a string
1398
+ "rb_str_new2(#{res})"
1399
+ else
1400
+ c_scope_res {
1401
+ l "char * def_desc;"
1402
+ l "def_desc = #{res};"
1403
+ "(def_desc ? rb_str_new2(def_desc) : Qnil)"
1404
+ }
1405
+ end
1406
+ end
1407
+ end
1408
+
1409
+ end