ruby2cext 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,820 @@
1
+
2
+ require "ruby2cext/error"
3
+ require "ruby2cext/plugin"
4
+
5
+ module Ruby2CExtension::Plugins
6
+
7
+ class BuiltinMethods < Ruby2CExtension::Plugin
8
+ # for public methods of builtin types with a fixed arity, which don't do anything with blocks
9
+
10
+ SUPPORTED_BUILTINS = [:Array, :Bignum, :FalseClass, :Fixnum, :Float, :Hash, :NilClass, :Regexp, :String, :Symbol, :TrueClass]
11
+
12
+ NO_CLASS_CHECK_BUILTINS = [:FalseClass, :Fixnum, :NilClass, :Symbol, :TrueClass]
13
+
14
+ COMMON_METHODS = [ # all supported builtins use these methods from Kernel
15
+ [:__id__, 0, :Kernel],
16
+ [:class, 0, :Kernel],
17
+ [:clone, 0, :Kernel],
18
+ [:dup, 0, :Kernel],
19
+ [:freeze, 0, :Kernel],
20
+ [:instance_variables, 0, :Kernel],
21
+ [:object_id, 0, :Kernel],
22
+ [:taint, 0, :Kernel],
23
+ [:tainted?, 0, :Kernel],
24
+ [:untaint, 0, :Kernel],
25
+ [:equal?, 1, :Kernel],
26
+ [:instance_of?, 1, :Kernel],
27
+ [:instance_variable_get, 1, :Kernel],
28
+ [:is_a?, 1, :Kernel],
29
+ [:kind_of?, 1, :Kernel],
30
+ [:method, 1, :Kernel],
31
+ [:instance_variable_set, 2, :Kernel],
32
+ ]
33
+
34
+ METHODS = {
35
+ :Array => [
36
+ [:[], 1, nil, nil, -1],
37
+ [:[], 2, nil, nil, -1],
38
+ [:first, 0, nil, nil, -1],
39
+ [:first, 1, nil, nil, -1],
40
+ [:insert, 2, nil, nil, -1],
41
+ [:insert, 3, nil, nil, -1],
42
+ [:join, 0, nil, nil, -1],
43
+ [:join, 1, nil, nil, -1],
44
+ [:last, 0, nil, nil, -1],
45
+ [:last, 1, nil, nil, -1],
46
+ [:push, 1, nil, nil, -1],
47
+ [:slice, 1, nil, nil, -1],
48
+ [:slice, 2, nil, nil, -1],
49
+ [:slice!, 1, nil, nil, -1],
50
+ [:slice!, 2, nil, nil, -1],
51
+ [:unshift, 1, nil, nil, -1],
52
+ [:values_at, 1, nil, nil, -1],
53
+ [:values_at, 2, nil, nil, -1],
54
+ [:values_at, 3, nil, nil, -1],
55
+ [:values_at, 4, nil, nil, -1],
56
+ [:clear, 0],
57
+ [:compact, 0],
58
+ [:compact!, 0],
59
+ [:empty?, 0],
60
+ [:flatten, 0],
61
+ [:flatten!, 0],
62
+ [:frozen?, 0],
63
+ [:hash, 0],
64
+ [:inspect, 0],
65
+ [:length, 0],
66
+ [:nitems, 0],
67
+ [:pop, 0],
68
+ [:reverse, 0],
69
+ [:reverse!, 0],
70
+ [:shift, 0],
71
+ [:size, 0],
72
+ [:to_a, 0],
73
+ [:to_ary, 0],
74
+ [:to_s, 0],
75
+ [:transpose, 0],
76
+ [:uniq, 0],
77
+ [:uniq!, 0],
78
+ [:&, 1],
79
+ [:|, 1],
80
+ [:*, 1],
81
+ [:+, 1],
82
+ [:-, 1],
83
+ [:<<, 1],
84
+ [:<=>, 1],
85
+ [:==, 1],
86
+ [:assoc, 1],
87
+ [:at, 1],
88
+ [:concat, 1],
89
+ [:delete_at, 1],
90
+ [:eql?, 1],
91
+ [:include?, 1],
92
+ [:index, 1],
93
+ [:rassoc, 1],
94
+ [:replace, 1],
95
+ [:rindex, 1],
96
+ [:entries, 0, :Enumerable],
97
+ [:member?, 1, :Enumerable],
98
+ [:nil?, 0, :Kernel],
99
+ [:===, 1, :Kernel],
100
+ [:=~, 1, :Kernel],
101
+ ],
102
+ :Bignum => [
103
+ [:to_s, 0, nil, nil, -1],
104
+ [:to_s, 1, nil, nil, -1],
105
+ [:-@, 0],
106
+ [:abs, 0],
107
+ [:hash, 0],
108
+ [:size, 0],
109
+ [:to_f, 0],
110
+ [:~, 0],
111
+ [:%, 1, nil, [:Fixnum, :Bignum]],
112
+ [:&, 1],
113
+ [:*, 1, nil, [:Fixnum, :Bignum, :Float]],
114
+ [:**, 1, nil, [:Fixnum, :Bignum, :Float]],
115
+ [:+, 1, nil, [:Fixnum, :Bignum, :Float]],
116
+ [:-, 1, nil, [:Fixnum, :Bignum, :Float]],
117
+ [:/, 1, nil, [:Fixnum, :Bignum, :Float]],
118
+ [:<<, 1],
119
+ [:<=>, 1, nil, [:Fixnum, :Bignum, :Float]],
120
+ [:==, 1],
121
+ [:>>, 1],
122
+ [:[], 1],
123
+ [:^, 1],
124
+ [:coerce, 1],
125
+ [:div, 1, nil, [:Fixnum, :Bignum, :Float]],
126
+ [:divmod, 1, nil, [:Fixnum, :Bignum]],
127
+ [:eql?, 1],
128
+ [:modulo, 1, nil, [:Fixnum, :Bignum]],
129
+ [:quo, 1, nil, [:Fixnum, :Bignum, :Float]],
130
+ [:remainder, 1, nil, [:Fixnum, :Bignum]],
131
+ [:|, 1],
132
+ [:<, 1, :Comparable],
133
+ [:<=, 1, :Comparable],
134
+ [:>, 1, :Comparable],
135
+ [:>=, 1, :Comparable],
136
+ [:between?, 2, :Comparable],
137
+ [:ceil, 0, :Integer],
138
+ [:chr, 0, :Integer],
139
+ [:floor, 0, :Integer],
140
+ [:integer?, 0, :Integer],
141
+ [:next, 0, :Integer],
142
+ [:round, 0, :Integer],
143
+ [:succ, 0, :Integer],
144
+ [:to_i, 0, :Integer],
145
+ [:to_int, 0, :Integer],
146
+ [:truncate, 0, :Integer],
147
+ [:+@, 0, :Numeric],
148
+ [:nonzero?, 0, :Numeric],
149
+ [:zero?, 0, :Numeric],
150
+ [:frozen?, 0, :Kernel],
151
+ [:inspect, 0, :Kernel],
152
+ [:nil?, 0, :Kernel],
153
+ [:to_a, 0, :Kernel],
154
+ [:===, 1, :Kernel],
155
+ [:=~, 1, :Kernel],
156
+ ],
157
+ :FalseClass => [
158
+ [:to_s, 0],
159
+ [:&, 1],
160
+ [:^, 1],
161
+ [:|, 1],
162
+ [:frozen?, 0, :Kernel],
163
+ [:hash, 0, :Kernel],
164
+ [:inspect, 0, :Kernel],
165
+ [:nil?, 0, :Kernel],
166
+ [:to_a, 0, :Kernel],
167
+ [:==, 1, :Kernel],
168
+ [:===, 1, :Kernel],
169
+ [:=~, 1, :Kernel],
170
+ [:eql?, 1, :Kernel],
171
+ ],
172
+ :Fixnum => [
173
+ [:to_s, 0, nil, nil, -1],
174
+ [:to_s, 1, nil, nil, -1],
175
+ [:-@, 0],
176
+ [:abs, 0],
177
+ [:id2name, 0],
178
+ [:size, 0],
179
+ [:to_sym, 0],
180
+ [:to_f, 0],
181
+ [:zero?, 0],
182
+ [:~, 0],
183
+ [:+, 1, nil, [:Fixnum, :Float]],
184
+ [:-, 1, nil, [:Fixnum, :Float]],
185
+ [:*, 1, nil, [:Fixnum, :Float]],
186
+ [:**, 1, nil, [:Fixnum, :Float]],
187
+ [:/, 1, nil, [:Fixnum]],
188
+ [:div, 1, nil, [:Fixnum]],
189
+ [:%, 1, nil, [:Fixnum]],
190
+ [:modulo, 1, nil, [:Fixnum]],
191
+ [:divmod, 1, nil, [:Fixnum]],
192
+ [:quo, 1, nil, [:Fixnum]],
193
+ [:<=>, 1, nil, [:Fixnum]],
194
+ [:>, 1, nil, [:Fixnum]],
195
+ [:>=, 1, nil, [:Fixnum]],
196
+ [:<, 1, nil, [:Fixnum]],
197
+ [:<=, 1, nil, [:Fixnum]],
198
+ [:==, 1],
199
+ [:&, 1],
200
+ [:|, 1],
201
+ [:^, 1],
202
+ [:[], 1],
203
+ [:<<, 1],
204
+ [:>>, 1],
205
+ [:between?, 2, :Comparable],
206
+ [:+@, 0, :Numeric],
207
+ [:nonzero?, 0, :Numeric],
208
+ [:coerce, 1, :Numeric],
209
+ [:eql?, 1, :Numeric],
210
+ [:remainder, 1, :Numeric],
211
+ [:ceil, 0, :Integer],
212
+ [:chr, 0, :Integer],
213
+ [:floor, 0, :Integer],
214
+ [:integer?, 0, :Integer],
215
+ [:next, 0, :Integer],
216
+ [:round, 0, :Integer],
217
+ [:succ, 0, :Integer],
218
+ [:to_i, 0, :Integer],
219
+ [:to_int, 0, :Integer],
220
+ [:truncate, 0, :Integer],
221
+ [:frozen?, 0, :Kernel],
222
+ [:hash, 0, :Kernel],
223
+ [:inspect, 0, :Kernel],
224
+ [:nil?, 0, :Kernel],
225
+ [:to_a, 0, :Kernel],
226
+ [:===, 1, :Kernel],
227
+ [:=~, 1, :Kernel],
228
+ ],
229
+ :Float => [
230
+ [:-@, 0],
231
+ [:to_s, 0],
232
+ [:hash, 0],
233
+ [:to_f, 0],
234
+ [:abs, 0],
235
+ [:zero?, 0],
236
+ [:to_i, 0],
237
+ [:to_int, 0],
238
+ [:floor, 0],
239
+ [:ceil, 0],
240
+ [:round, 0],
241
+ [:truncate, 0],
242
+ [:nan?, 0],
243
+ [:infinite?, 0],
244
+ [:finite?, 0],
245
+ [:coerce, 1],
246
+ [:eql?, 1],
247
+ [:==, 1],
248
+ [:+, 1, nil, [:Fixnum, :Bignum, :Float]],
249
+ [:-, 1, nil, [:Fixnum, :Bignum, :Float]],
250
+ [:*, 1, nil, [:Fixnum, :Bignum, :Float]],
251
+ [:/, 1, nil, [:Fixnum, :Bignum, :Float]],
252
+ [:%, 1, nil, [:Fixnum, :Bignum, :Float]],
253
+ [:modulo, 1, nil, [:Fixnum, :Bignum, :Float]],
254
+ [:divmod, 1, nil, [:Fixnum, :Bignum, :Float]],
255
+ [:**, 1, nil, [:Fixnum, :Bignum, :Float]],
256
+ [:<=>, 1, nil, [:Fixnum, :Bignum, :Float]],
257
+ [:>, 1, nil, [:Fixnum, :Bignum, :Float]],
258
+ [:>=, 1, nil, [:Fixnum, :Bignum, :Float]],
259
+ [:<, 1, nil, [:Fixnum, :Bignum, :Float]],
260
+ [:<=, 1, nil, [:Fixnum, :Bignum, :Float]],
261
+ [:between?, 2, :Comparable],
262
+ [:+@, 0, :Numeric],
263
+ [:integer?, 0, :Numeric],
264
+ [:nonzero?, 0, :Numeric],
265
+ [:div, 1, :Numeric],
266
+ [:quo, 1, :Numeric],
267
+ [:remainder, 1, :Numeric],
268
+ [:frozen?, 0, :Kernel],
269
+ [:inspect, 0, :Kernel],
270
+ [:nil?, 0, :Kernel],
271
+ [:to_a, 0, :Kernel],
272
+ [:===, 1, :Kernel],
273
+ [:=~, 1, :Kernel],
274
+ ],
275
+ :Hash => [
276
+ [:default, 0, nil, nil, -1],
277
+ [:default, 1, nil, nil, -1],
278
+ [:values_at, 1, nil, nil, -1],
279
+ [:values_at, 2, nil, nil, -1],
280
+ [:values_at, 3, nil, nil, -1],
281
+ [:values_at, 4, nil, nil, -1],
282
+ [:clear, 0],
283
+ [:default_proc, 0],
284
+ [:empty?, 0],
285
+ [:inspect, 0],
286
+ [:invert, 0],
287
+ [:keys, 0],
288
+ [:length, 0],
289
+ [:rehash, 0],
290
+ [:shift, 0],
291
+ [:size, 0],
292
+ [:to_a, 0],
293
+ [:to_hash, 0],
294
+ [:to_s, 0],
295
+ [:values, 0],
296
+ [:==, 1],
297
+ [:[], 1],
298
+ [:default=, 1],
299
+ [:delete, 1],
300
+ [:has_key?, 1],
301
+ [:has_value?, 1],
302
+ [:include?, 1],
303
+ [:index, 1],
304
+ [:key?, 1],
305
+ [:member?, 1],
306
+ [:replace, 1],
307
+ [:value?, 1],
308
+ [:store, 2],
309
+ [:entries, 0, :Enumerable],
310
+ [:frozen?, 0, :Kernel],
311
+ [:hash, 0, :Kernel],
312
+ [:nil?, 0, :Kernel],
313
+ [:===, 1, :Kernel],
314
+ [:=~, 1, :Kernel],
315
+ [:eql?, 1, :Kernel],
316
+ ],
317
+ :NilClass => [
318
+ [:inspect, 0],
319
+ [:nil?, 0],
320
+ [:to_a, 0],
321
+ [:to_f, 0],
322
+ [:to_i, 0],
323
+ [:to_s, 0],
324
+ [:&, 1],
325
+ [:^, 1],
326
+ [:|, 1],
327
+ [:frozen?, 0, :Kernel],
328
+ [:hash, 0, :Kernel],
329
+ [:==, 1, :Kernel],
330
+ [:===, 1, :Kernel],
331
+ [:=~, 1, :Kernel],
332
+ [:eql?, 1, :Kernel],
333
+ ],
334
+ :Regexp => [
335
+ [:casefold?, 0],
336
+ [:hash, 0],
337
+ [:inspect, 0],
338
+ [:kcode, 0],
339
+ [:options, 0],
340
+ [:source, 0],
341
+ [:to_s, 0],
342
+ [:~, 0],
343
+ [:==, 1],
344
+ [:===, 1],
345
+ [:=~, 1],
346
+ [:eql?, 1],
347
+ [:match, 1],
348
+ [:frozen?, 0, :Kernel],
349
+ [:nil?, 0, :Kernel],
350
+ [:to_a, 0, :Kernel],
351
+ ],
352
+ :String => [
353
+ [:[], 1, nil, nil, -1],
354
+ [:[], 2, nil, nil, -1],
355
+ [:center, 1, nil, nil, -1],
356
+ [:center, 2, nil, nil, -1],
357
+ [:chomp, 1, nil, nil, -1],
358
+ [:chomp, 2, nil, nil, -1],
359
+ [:chomp!, 1, nil, nil, -1],
360
+ [:chomp!, 2, nil, nil, -1],
361
+ [:count, 1, nil, nil, -1],
362
+ [:count, 2, nil, nil, -1],
363
+ [:count, 3, nil, nil, -1],
364
+ [:delete, 1, nil, nil, -1],
365
+ [:delete, 2, nil, nil, -1],
366
+ [:delete, 3, nil, nil, -1],
367
+ [:delete!, 1, nil, nil, -1],
368
+ [:delete!, 2, nil, nil, -1],
369
+ [:delete!, 3, nil, nil, -1],
370
+ [:index, 1, nil, nil, -1],
371
+ [:index, 2, nil, nil, -1],
372
+ [:ljust, 1, nil, nil, -1],
373
+ [:ljust, 2, nil, nil, -1],
374
+ [:rindex, 1, nil, nil, -1],
375
+ [:rindex, 2, nil, nil, -1],
376
+ [:rjust, 1, nil, nil, -1],
377
+ [:rjust, 2, nil, nil, -1],
378
+ [:slice, 1, nil, nil, -1],
379
+ [:slice, 2, nil, nil, -1],
380
+ [:slice!, 1, nil, nil, -1],
381
+ [:slice!, 2, nil, nil, -1],
382
+ [:split, 0, nil, nil, -1],
383
+ [:split, 1, nil, nil, -1],
384
+ [:split, 2, nil, nil, -1],
385
+ [:squeeze, 1, nil, nil, -1],
386
+ [:squeeze, 2, nil, nil, -1],
387
+ [:squeeze, 3, nil, nil, -1],
388
+ [:squeeze!, 1, nil, nil, -1],
389
+ [:squeeze!, 2, nil, nil, -1],
390
+ [:squeeze!, 3, nil, nil, -1],
391
+ [:to_i, 0, nil, nil, -1],
392
+ [:to_i, 1, nil, nil, -1],
393
+ [:capitalize, 0],
394
+ [:capitalize!, 0],
395
+ [:chop, 0],
396
+ [:chop!, 0],
397
+ [:downcase, 0],
398
+ [:downcase!, 0],
399
+ [:dump, 0],
400
+ [:empty?, 0],
401
+ [:hash, 0],
402
+ [:hex, 0],
403
+ [:inspect, 0],
404
+ [:intern, 0],
405
+ [:length, 0],
406
+ [:lstrip, 0],
407
+ [:lstrip!, 0],
408
+ [:next, 0],
409
+ [:next!, 0],
410
+ [:oct, 0],
411
+ [:reverse, 0],
412
+ [:reverse!, 0],
413
+ [:rstrip, 0],
414
+ [:rstrip!, 0],
415
+ [:size, 0],
416
+ [:strip, 0],
417
+ [:strip!, 0],
418
+ [:succ, 0],
419
+ [:succ!, 0],
420
+ [:swapcase, 0],
421
+ [:swapcase!, 0],
422
+ [:to_f, 0],
423
+ [:to_s, 0],
424
+ [:to_str, 0],
425
+ [:to_sym, 0],
426
+ [:upcase, 0],
427
+ [:upcase!, 0],
428
+ [:%, 1],
429
+ [:*, 1],
430
+ [:+, 1],
431
+ [:<<, 1],
432
+ [:<=>, 1],
433
+ [:==, 1],
434
+ [:=~, 1],
435
+ [:casecmp, 1],
436
+ [:concat, 1],
437
+ [:crypt, 1],
438
+ [:eql?, 1],
439
+ [:include?, 1],
440
+ [:match, 1],
441
+ [:replace, 1],
442
+ [:insert, 2],
443
+ [:tr, 2],
444
+ [:tr!, 2],
445
+ [:tr_s, 2],
446
+ [:tr_s!, 2],
447
+ [:<, 1, :Comparable],
448
+ [:<=, 1, :Comparable],
449
+ [:>, 1, :Comparable],
450
+ [:>=, 1, :Comparable],
451
+ [:between?, 2, :Comparable],
452
+ [:entries, 0, :Enumerable],
453
+ [:to_a, 0, :Enumerable],
454
+ [:member?, 1, :Enumerable],
455
+ [:frozen?, 0, :Kernel],
456
+ [:nil?, 0, :Kernel],
457
+ [:===, 1, :Kernel],
458
+ ],
459
+ :Symbol => [
460
+ [:id2name, 0],
461
+ [:inspect, 0],
462
+ [:to_i, 0],
463
+ [:to_int, 0],
464
+ [:to_s, 0],
465
+ [:to_sym, 0],
466
+ [:===, 1],
467
+ [:frozen?, 0, :Kernel],
468
+ [:hash, 0, :Kernel],
469
+ [:nil?, 0, :Kernel],
470
+ [:to_a, 0, :Kernel],
471
+ [:==, 1, :Kernel],
472
+ [:=~, 1, :Kernel],
473
+ [:eql?, 1, :Kernel],
474
+ ],
475
+ :TrueClass => [
476
+ [:to_s, 0],
477
+ [:&, 1],
478
+ [:^, 1],
479
+ [:|, 1],
480
+ [:frozen?, 0, :Kernel],
481
+ [:hash, 0, :Kernel],
482
+ [:inspect, 0, :Kernel],
483
+ [:nil?, 0, :Kernel],
484
+ [:to_a, 0, :Kernel],
485
+ [:==, 1, :Kernel],
486
+ [:===, 1, :Kernel],
487
+ [:=~, 1, :Kernel],
488
+ [:eql?, 1, :Kernel],
489
+ ],
490
+ }
491
+
492
+ METHOD_NAME_MAPPINGS = Hash.new { |h, k|
493
+ case k.to_s
494
+ when /\A\w+\z/
495
+ h[k] = "builtinoptmeth_#{k}"
496
+ when /\A\w+\?\z/
497
+ h[k] = "builtinoptmeth_#{k.to_s[0..-2]}__pred"
498
+ when /\A\w+!\z/
499
+ h[k] = "builtinoptmeth_#{k.to_s[0..-2]}__bang"
500
+ when /\A\w+=\z/
501
+ h[k] = "builtinoptmeth_#{k.to_s[0..-2]}__assign"
502
+ else
503
+ raise Ruby2CExtension::Ruby2CExtError::Bug, "unexpected method name: #{k.inspect}"
504
+ end
505
+ }
506
+ METHOD_NAME_MAPPINGS.merge!({
507
+ :+@ => "builtinoptop_uplus",
508
+ :-@ => "builtinoptop_uminus",
509
+ :+ => "builtinoptop_plus",
510
+ :- => "builtinoptop_minus",
511
+ :* => "builtinoptop_mul",
512
+ :/ => "builtinoptop_div",
513
+ :** => "builtinoptop_pow",
514
+ :% => "builtinoptop_mod",
515
+ :~ => "builtinoptop_rev",
516
+ :== => "builtinoptop_equal",
517
+ :=== => "builtinoptop_eqq",
518
+ :=~ => "builtinoptop_match",
519
+ :<=> => "builtinoptop_cmp",
520
+ :> => "builtinoptop_gt",
521
+ :>= => "builtinoptop_ge",
522
+ :< => "builtinoptop_lt",
523
+ :<= => "builtinoptop_le",
524
+ :& => "builtinoptop_and",
525
+ :| => "builtinoptop_or",
526
+ :^ => "builtinoptop_xor",
527
+ :[] => "builtinoptop_aref",
528
+ :<< => "builtinoptop_lshift",
529
+ :>> => "builtinoptop_rshift",
530
+ })
531
+
532
+ BUILTIN_TYPE_MAP = Hash.new { |h, k|
533
+ h[k] = "T_#{k}".upcase
534
+ }
535
+ BUILTIN_TYPE_MAP.merge!({
536
+ :NilClass => "T_NIL",
537
+ :TrueClass => "T_TRUE",
538
+ :FalseClass => "T_FALSE",
539
+ })
540
+
541
+ BUILTIN_C_VAR_MAP = Hash.new { |h, k|
542
+ h[k] = "rb_#{Module.const_get(k).instance_of?(Module) ? "m" : "c"}#{k}"
543
+ }
544
+
545
+ attr_reader :methods
546
+
547
+ def initialize(compiler, builtins)
548
+ super(compiler)
549
+ builtins = SUPPORTED_BUILTINS & builtins # "sort" and unique
550
+ @methods = {} # [meth_sym, arity] => # [[type, impl. class/mod, types of first arg or nil, real arity], ...]
551
+ @function_names = {} # [meth_sym, arity] => name # initialized on first use
552
+ @typed_function_infos = {} # [meth_sym, arity, recv_type] => [fun_ptr, real_arity, first_arg_types] # initialized on first use
553
+ @fallback_functions = {} # [meth_sym, real_arity] => name
554
+ @functions_code = [] # source code of the replacement functions
555
+ @fallback_functions_code = [] # source code of the fallback functions
556
+ @method_tbl_size = 0
557
+ @init_code = []
558
+ builtins.each { |builtin|
559
+ (METHODS[builtin] + COMMON_METHODS).each { |arr|
560
+ (@methods[arr[0, 2]] ||= []) << [builtin, arr[2] || builtin, arr[3], arr[4] || arr[1]]
561
+ }
562
+ }
563
+ compiler.add_preprocessor(:call) { |cfun, node|
564
+ handle_call(cfun, node.last, node)
565
+ }
566
+ end
567
+
568
+ def deduce_type(node)
569
+ if Array === node
570
+ case node.first
571
+ when :lit
572
+ node.last[:lit].class.name.to_sym
573
+ when :nil
574
+ :NilClass
575
+ when :false
576
+ :FalseClass
577
+ when :true
578
+ :TrueClass
579
+ when :str, :dstr
580
+ :String
581
+ when :dsym
582
+ :Symbol
583
+ when :array, :zarray
584
+ :Array
585
+ when :hash
586
+ :Hash
587
+ when :dregx, :dregx_once
588
+ :Regexp
589
+ else
590
+ nil
591
+ end
592
+ else
593
+ nil
594
+ end
595
+ end
596
+
597
+ def do_init_lookup(method, method_info, fallback_fun = nil)
598
+ tbl_idx = @method_tbl_size
599
+ @method_tbl_size += 1
600
+ var = "builtinopt_method_tbl[#{tbl_idx}]"
601
+ lookup_args = [BUILTIN_C_VAR_MAP[method_info[0]], BUILTIN_C_VAR_MAP[method_info[1]], compiler.sym(method), method_info[3]]
602
+ @init_code << "#{var} = builtinopt_method_lookup(#{lookup_args.join(", ")});"
603
+ if fallback_fun
604
+ @init_code << "if (!(#{var})) #{var} = #{fallback_fun};"
605
+ end
606
+ var
607
+ end
608
+
609
+ def get_function(method, arity)
610
+ ma = [method, arity]
611
+ if (name = @function_names[ma])
612
+ name
613
+ elsif (meth_list = methods[ma])
614
+ name = @function_names[ma] = "#{METHOD_NAME_MAPPINGS[method]}__#{arity}"
615
+ code = []
616
+ code << "static VALUE #{name}(VALUE recv#{arity > 0 ? ", VALUE *argv" : ""}) {"
617
+ code << "switch(TYPE(recv)) {"
618
+ meth_list.each { |m|
619
+ fun_ptr = do_init_lookup(method, m)
620
+ code << "case #{BUILTIN_TYPE_MAP[m[0]]}:"
621
+ check = fun_ptr.dup
622
+ unless NO_CLASS_CHECK_BUILTINS.include?(m[0])
623
+ check << " && RBASIC(recv)->klass == #{BUILTIN_C_VAR_MAP[m[0]]}"
624
+ end
625
+ call =
626
+ if m[3] == -1
627
+ "(*(#{fun_ptr}))(#{arity}, #{arity > 0 ? "argv" : "NULL"}, recv)"
628
+ else
629
+ args = (0...arity).map { |j| "argv[#{j}]" }.join(", ")
630
+ args = ", " + args unless args.empty?
631
+ "(*(#{fun_ptr}))(recv#{args})"
632
+ end
633
+ if (first_arg_types = m[2])
634
+ if arity != 1
635
+ raise Ruby2CExtension::Ruby2CExtError::Bug, "arity must be 1 for arg type check"
636
+ end
637
+ code << "switch(TYPE(argv[0])) {"
638
+ first_arg_types.each { |o| code << "case #{BUILTIN_TYPE_MAP[o]}:" }
639
+ code << "if (#{check}) return #{call};"
640
+ code << "}"
641
+ else
642
+ code << "if (#{check}) return #{call};"
643
+ end
644
+ code << "break;"
645
+ }
646
+ code << "}"
647
+ code << "return rb_funcall3(recv, #{compiler.sym(method)}, #{arity}, #{arity > 0 ? "argv" : "NULL"});"
648
+ code << "}"
649
+ @functions_code << code.join("\n")
650
+ name
651
+ else
652
+ nil
653
+ end
654
+ end
655
+
656
+ def generate_fallback_function(method, real_arity)
657
+ ma = [method, real_arity]
658
+ if (name = @fallback_functions[ma])
659
+ name
660
+ else
661
+ name = @fallback_functions[ma] = "#{METHOD_NAME_MAPPINGS[method]}__#{real_arity >= 0 ? real_arity : "V"}__fallback"
662
+ code = []
663
+ if real_arity == -1
664
+ code << "static VALUE #{name}(int argc, VALUE *argv, VALUE recv) {"
665
+ code << "return rb_funcall3(recv, #{compiler.sym(method)}, argc, argv);"
666
+ code << "}"
667
+ else
668
+ args = (["VALUE recv"] + (0...real_arity).map { |j| "VALUE arg_#{j}" }).join(", ")
669
+ code << "static VALUE #{name}(#{args}) {"
670
+ if real_arity > 1
671
+ code << "VALUE argv[#{real_arity}];"
672
+ (0...real_arity).each { |j|
673
+ code << "argv[#{j}] = arg_#{j};"
674
+ }
675
+ end
676
+ argv =
677
+ case real_arity
678
+ when 0: "NULL"
679
+ when 1: "&arg_0"
680
+ else "argv"
681
+ end
682
+ code << "return rb_funcall3(recv, #{compiler.sym(method)}, #{real_arity}, #{argv});"
683
+ code << "}"
684
+ end
685
+ @fallback_functions_code << code.join("\n")
686
+ name
687
+ end
688
+ end
689
+
690
+ def get_typed_function_info(method, arity, recv_type)
691
+ mat = [method, arity, recv_type]
692
+ if (infos = @typed_function_infos[mat])
693
+ infos
694
+ elsif (meth_list = methods[[method, arity]])
695
+ if (m = meth_list.find { |arr| arr.first == recv_type })
696
+ @typed_function_infos[mat] = [
697
+ do_init_lookup(method, m, generate_fallback_function(method, m[3])),
698
+ m[3], m[2]
699
+ ]
700
+ else
701
+ nil # this call is not optimizable
702
+ end
703
+ else
704
+ nil
705
+ end
706
+ end
707
+
708
+ def handle_call(cfun, hash, node)
709
+ args = []
710
+ if hash[:args]
711
+ if hash[:args].first == :array
712
+ args = hash[:args].last
713
+ else
714
+ return node
715
+ end
716
+ end
717
+ if (recv_type = deduce_type(hash[:recv]))
718
+ fun_ptr, real_arity, first_arg_types = *get_typed_function_info(hash[:mid], args.size, recv_type)
719
+ if fun_ptr
720
+ cfun.instance_eval {
721
+ recv = comp(hash[:recv])
722
+ if args.empty?
723
+ "(*(#{fun_ptr}))(#{real_arity == -1 ? "0, NULL, " : ""}#{recv})"
724
+ else
725
+ arity = args.size
726
+ c_scope_res {
727
+ l "VALUE recv = #{recv};"
728
+ build_c_arr(args, "argv")
729
+ call_args =
730
+ if real_arity == -1
731
+ "#{arity}, #{arity > 0 ? "argv" : "NULL"}, recv"
732
+ else
733
+ "recv, " + (0...arity).map { |j| "argv[#{j}]" }.join(", ")
734
+ end
735
+ if first_arg_types
736
+ l "switch(TYPE(argv[0])) {"
737
+ first_arg_types.each { |o|
738
+ l "case #{BUILTIN_TYPE_MAP[o]}:"
739
+ }
740
+ assign_res("(*(#{fun_ptr}))(#{call_args})")
741
+ l "break;"
742
+ l "default:"
743
+ assign_res("rb_funcall3(recv, #{compiler.sym(hash[:mid])}, 1, argv)")
744
+ l "}"
745
+ "res"
746
+ else
747
+ "(*(#{fun_ptr}))(#{call_args})"
748
+ end
749
+ }
750
+ end
751
+ }
752
+ else
753
+ node
754
+ end
755
+ elsif (fun = get_function(hash[:mid], args.size))
756
+ cfun.instance_eval {
757
+ recv = comp(hash[:recv])
758
+ if args.empty?
759
+ "#{fun}(#{recv})"
760
+ else
761
+ c_scope_res {
762
+ l "VALUE recv = #{recv};"
763
+ build_c_arr(args, "argv")
764
+ "#{fun}(recv, argv)"
765
+ }
766
+ end
767
+ }
768
+ else
769
+ node
770
+ end
771
+ end
772
+
773
+ METHOD_LOOKUP_CODE = %{
774
+ static BUILTINOPT_FP builtinopt_method_lookup(VALUE klass, VALUE origin, ID mid, long arity) {
775
+ NODE *body;
776
+ while (klass != origin) {
777
+ if (TYPE(klass) == T_ICLASS && RBASIC(klass)->klass == origin) break;
778
+ if (st_lookup(RCLASS(klass)->m_tbl, mid, (st_data_t *)&body)) return NULL;
779
+ klass = RCLASS(klass)->super;
780
+ if (!klass) return NULL;
781
+ }
782
+ if (st_lookup(RCLASS(klass)->m_tbl, mid, (st_data_t *)&body)) {
783
+ body = body->nd_body;
784
+ if (nd_type(body) == NODE_FBODY) body = body->nd_head;
785
+ if (nd_type(body) == NODE_CFUNC && body->nd_argc == arity) {
786
+ return body->nd_cfnc;
787
+ }
788
+ }
789
+ return NULL;
790
+ }
791
+ }
792
+
793
+ def global_c_code
794
+ unless @init_code.empty?
795
+ res = []
796
+ res << "typedef VALUE (*BUILTINOPT_FP)(ANYARGS);"
797
+ res << "static BUILTINOPT_FP builtinopt_method_tbl[#{@method_tbl_size}];"
798
+ res << METHOD_LOOKUP_CODE
799
+
800
+ res.concat(@fallback_functions_code)
801
+
802
+ res << "static void init_builtinopt() {"
803
+ res.concat(@init_code)
804
+ res << "}"
805
+
806
+ res.concat(@functions_code)
807
+
808
+ res.join("\n")
809
+ end
810
+ end
811
+
812
+ def init_c_code
813
+ unless @init_code.empty?
814
+ "init_builtinopt();"
815
+ end
816
+ end
817
+
818
+ end
819
+
820
+ end