ruby2cext 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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