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