crystalizer 0.2.2

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.
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