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