crystalizer 0.2.2

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