fastruby 0.0.1

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,903 @@
1
+ =begin
2
+
3
+ This file is part of the fastruby project, http://github.com/tario/fastruby
4
+
5
+ Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ fastruby is free software: you can redistribute it and/or modify
8
+ it under the terms of the gnu general public license as published by
9
+ the free software foundation, either version 3 of the license, or
10
+ (at your option) any later version.
11
+
12
+ fastruby is distributed in the hope that it will be useful,
13
+ but without any warranty; without even the implied warranty of
14
+ merchantability or fitness for a particular purpose. see the
15
+ gnu general public license for more details.
16
+
17
+ you should have received a copy of the gnu general public license
18
+ along with fastruby. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ require "rubygems"
22
+ require "inline"
23
+ require "set"
24
+
25
+ module FastRuby
26
+ class Context
27
+
28
+ attr_accessor :infer_lvar_map
29
+ attr_accessor :alt_method_name
30
+ attr_accessor :locals
31
+ attr_accessor :options
32
+ attr_accessor :infer_self
33
+ attr_reader :extra_code
34
+ attr_reader :yield_signature
35
+
36
+ def initialize
37
+ @infer_lvar_map = Hash.new
38
+ @extra_code = ""
39
+ @options = {}
40
+
41
+ extra_code << '#include "node.h"
42
+ '
43
+
44
+ extra_code << "static VALUE _rb_gvar_set(void* ge,VALUE value) {
45
+ rb_gvar_set((struct global_entry*)ge,value);
46
+ return value;
47
+ }
48
+ "
49
+
50
+ extra_code << "static VALUE _rb_ivar_set(VALUE recv,ID idvar, VALUE value) {
51
+ rb_ivar_set(recv,idvar,value);
52
+ return value;
53
+ }
54
+ "
55
+
56
+ extra_code << "static VALUE _lvar_assing(VALUE* destination,VALUE value) {
57
+ *destination = value;
58
+ return value;
59
+ }
60
+ "
61
+
62
+ end
63
+
64
+ def on_block
65
+ yield
66
+ end
67
+
68
+ def to_c(tree)
69
+ return "Qnil" unless tree
70
+ send("to_c_" + tree[0].to_s, tree);
71
+ end
72
+
73
+ def anonymous_function(method)
74
+
75
+ name = "anonymous" + rand(10000000).to_s
76
+ extra_code << method.call(name)
77
+
78
+ name
79
+ end
80
+
81
+ def to_c_dot2(tree)
82
+ "rb_range_new(#{to_c tree[1]}, #{to_c tree[2]},0)"
83
+ end
84
+
85
+ def to_c_attrasgn(tree)
86
+ to_c_call(tree)
87
+ end
88
+
89
+ def to_c_iter(tree)
90
+
91
+ call_tree = tree[1]
92
+ args_tree = tree[2]
93
+ recv_tree = call_tree[1]
94
+
95
+ directive_code = directive(call_tree)
96
+ if directive_code
97
+ return directive_code
98
+ end
99
+
100
+ other_call_tree = call_tree.dup
101
+ other_call_tree[1] = s(:lvar, :arg)
102
+
103
+ call_args_tree = call_tree[3]
104
+
105
+ caller_code = nil
106
+
107
+ str_lvar_initialization = @locals_struct + " *plocals;
108
+ plocals = (void*)param;"
109
+
110
+ recvtype = infer_type(recv_tree || s(:self))
111
+
112
+ address = nil
113
+ mobject = nil
114
+ len = nil
115
+
116
+ convention = :ruby
117
+
118
+ extra_inference = {}
119
+
120
+ if recvtype
121
+
122
+ inference_complete = true
123
+ signature = [recvtype]
124
+
125
+ call_args_tree[1..-1].each do |arg|
126
+ argtype = infer_type(arg)
127
+ if argtype
128
+ signature << argtype
129
+ else
130
+ inference_complete = false
131
+ end
132
+ end
133
+
134
+ convention = nil
135
+
136
+ if recvtype.respond_to? :method_tree and inference_complete
137
+
138
+ if recvtype.method_tree[call_tree[2]]
139
+ mobject = recvtype.build(signature, call_tree[2])
140
+ yield_signature = mobject.yield_signature
141
+
142
+ if not args_tree
143
+ elsif args_tree.first == :lasgn
144
+ if yield_signature[0]
145
+ extra_inference[args_tree.last] = yield_signature[0]
146
+ end
147
+ elsif args_tree.first == :masgn
148
+ yield_args = args_tree[1][1..-1].map(&:last)
149
+ (0...yield_signature.size-1).each do |i|
150
+ extra_inference[yield_args[i]] = yield_signature[i]
151
+ end
152
+ end
153
+
154
+ convention = :fastruby
155
+ else
156
+ mobject = recvtype.instance_method(call_tree[2])
157
+ convention = :cruby
158
+ end
159
+ else
160
+ mobject = recvtype.instance_method(call_tree[2])
161
+ convention = :cruby
162
+ end
163
+
164
+ address = getaddress(mobject)
165
+ len = getlen(mobject)
166
+
167
+ unless address
168
+ convention = :ruby
169
+ end
170
+
171
+ end
172
+
173
+ anonymous_impl = tree[3]
174
+
175
+ str_lvar_initialization = @locals_struct + " *plocals;
176
+ plocals = (void*)param;"
177
+
178
+ str_arg_initialization = ""
179
+
180
+ str_impl = ""
181
+
182
+ with_extra_inference(extra_inference) do
183
+ # if impl_tree is a block, implement the last node with a return
184
+ if anonymous_impl
185
+ if anonymous_impl[0] == :block
186
+ str_impl = anonymous_impl[1..-2].map{ |subtree|
187
+ to_c(subtree)
188
+ }.join(";")
189
+
190
+ if anonymous_impl[-1][0] != :return
191
+ str_impl = str_impl + ";last_expression = (#{to_c(anonymous_impl[-1])});"
192
+ else
193
+ str_impl = str_impl + ";#{to_c(anonymous_impl[-1])};"
194
+ end
195
+ else
196
+ if anonymous_impl[0] != :return
197
+ str_impl = str_impl + ";last_expression = (#{to_c(anonymous_impl)});"
198
+ else
199
+ str_impl = str_impl + ";#{to_c(anonymous_impl)};"
200
+ end
201
+ end
202
+ else
203
+ str_impl = "last_expression = Qnil;"
204
+ end
205
+
206
+ end
207
+
208
+ if convention == :ruby or convention == :cruby
209
+
210
+ if call_args_tree.size > 1
211
+
212
+ str_called_code_args = call_args_tree[1..-1].map{ |subtree| to_c subtree }.join(",")
213
+ str_recv = to_c recv_tree
214
+
215
+ str_recv = "plocals->self" unless recv_tree
216
+
217
+ caller_code = proc { |name| "
218
+ static VALUE #{name}(VALUE param) {
219
+ // call to #{call_tree[2]}
220
+
221
+ #{str_lvar_initialization}
222
+ return rb_funcall(#{str_recv}, #{call_tree[2].to_i}, #{call_args_tree.size-1}, #{str_called_code_args});
223
+ }
224
+ "
225
+ }
226
+
227
+ else
228
+ str_recv = to_c recv_tree
229
+ str_recv = "plocals->self" unless recv_tree
230
+
231
+ caller_code = proc { |name| "
232
+ static VALUE #{name}(VALUE param) {
233
+ // call to #{call_tree[2]}
234
+ #{str_lvar_initialization}
235
+ return rb_funcall(#{str_recv}, #{call_tree[2].to_i}, 0);
236
+ }
237
+ "
238
+ }
239
+ end
240
+
241
+ if not args_tree
242
+ str_arg_initialization = ""
243
+ elsif args_tree.first == :lasgn
244
+ str_arg_initialization = "plocals->#{args_tree[1]} = arg;"
245
+ elsif args_tree.first == :masgn
246
+ arguments = args_tree[1][1..-1].map(&:last)
247
+
248
+ (0..arguments.size-1).each do |i|
249
+ str_arg_initialization << "plocals->#{arguments[i]} = rb_ary_entry(arg,#{i});\n"
250
+ end
251
+ end
252
+
253
+ str_arg_initialization
254
+
255
+ block_code = proc { |name| "
256
+ static VALUE #{name}(VALUE arg, VALUE param) {
257
+ // block for call to #{call_tree[2]}
258
+ VALUE last_expression = Qnil;
259
+
260
+ #{str_lvar_initialization};
261
+ #{str_arg_initialization}
262
+ #{str_impl}
263
+
264
+ return last_expression;
265
+ }
266
+ "
267
+ }
268
+
269
+ "rb_iterate(#{anonymous_function(caller_code)}, (VALUE)#{locals_pointer}, #{anonymous_function(block_code)}, (VALUE)#{locals_pointer})"
270
+ elsif convention == :fastruby
271
+
272
+ str_arg_initialization = ""
273
+
274
+ if not args_tree
275
+ str_arg_initialization = ""
276
+ elsif args_tree.first == :lasgn
277
+ str_arg_initialization = "plocals->#{args_tree[1]} = argv[0];"
278
+ elsif args_tree.first == :masgn
279
+ arguments = args_tree[1][1..-1].map(&:last)
280
+
281
+ (0..arguments.size-1).each do |i|
282
+ str_arg_initialization << "plocals->#{arguments[i]} = #{i} < argc ? argv[#{i}] : Qnil;\n"
283
+ end
284
+ end
285
+
286
+ block_code = proc { |name| "
287
+ static VALUE #{name}(int argc, VALUE* argv, VALUE param) {
288
+ // block for call to #{call_tree[2]}
289
+ VALUE last_expression = Qnil;
290
+
291
+ #{str_lvar_initialization};
292
+ #{str_arg_initialization}
293
+ #{str_impl}
294
+
295
+ return last_expression;
296
+ }
297
+ "
298
+ }
299
+
300
+
301
+ str_recv = "plocals->self"
302
+
303
+ if recv_tree
304
+ str_recv = to_c recv_tree
305
+ end
306
+
307
+ if call_args_tree.size > 1
308
+ value_cast = ( ["VALUE"]*(call_tree[3].size) ).join(",")
309
+ value_cast = value_cast + ", VALUE" if convention == :fastruby
310
+
311
+ str_called_code_args = call_tree[3][1..-1].map{|subtree| to_c subtree}.join(",")
312
+
313
+ caller_code = proc { |name| "
314
+ static VALUE #{name}(VALUE param) {
315
+ #{@block_struct} block;
316
+
317
+ block.block_function_address = (void*)#{anonymous_function(block_code)};
318
+ block.block_function_param = (void*)param;
319
+
320
+ // call to #{call_tree[2]}
321
+
322
+ #{str_lvar_initialization}
323
+
324
+ return ((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{str_recv}, (VALUE)&block, #{str_called_code_args});
325
+ }
326
+ "
327
+ }
328
+
329
+ else
330
+ caller_code = proc { |name| "
331
+ static VALUE #{name}(VALUE param) {
332
+ #{@block_struct} block;
333
+
334
+ block.block_function_address = (void*)#{anonymous_function(block_code)};
335
+ block.block_function_param = (void*)param;
336
+
337
+ // call to #{call_tree[2]}
338
+ #{str_lvar_initialization}
339
+
340
+ return ((VALUE(*)(VALUE,VALUE))0x#{address.to_s(16)})(#{str_recv}, (VALUE)&block);
341
+ }
342
+ "
343
+ }
344
+ end
345
+
346
+ "#{anonymous_function(caller_code)}((VALUE)#{locals_pointer})"
347
+ end
348
+ end
349
+
350
+ def to_c_yield(tree)
351
+
352
+ block_code = proc { |name| "
353
+ static VALUE #{name}(VALUE locals_param, VALUE* block_args) {
354
+
355
+ #{@locals_struct} *plocals;
356
+ plocals = (void*)locals_param;
357
+
358
+ if (plocals->block_function_address == 0) {
359
+ rb_raise(rb_eLocalJumpError, \"no block given\");
360
+ } else {
361
+ return ((VALUE(*)(int,VALUE*,VALUE))plocals->block_function_address)(#{tree.size-1}, block_args, plocals->block_function_param);
362
+ }
363
+ }
364
+ "
365
+ }
366
+
367
+ new_yield_signature = tree[1..-1].map{|subtree| infer_type subtree}
368
+ # merge the new_yield_signature with the new
369
+ if @yield_signature
370
+ if new_yield_signature.size == @yield_signature.size
371
+ (0..new_yield_signature.size-1).each do |i|
372
+ if @yield_signature[i] != new_yield_signature[i]
373
+ @yield_signature[i] = nil
374
+ end
375
+ end
376
+ else
377
+ @yield_signature = new_yield_signature.map{|x| nil}
378
+ end
379
+ else
380
+ @yield_signature = new_yield_signature
381
+ end
382
+
383
+ if tree.size > 1
384
+ anonymous_function(block_code)+"((VALUE)#{locals_pointer}, (VALUE[]){#{tree[1..-1].map{|subtree| to_c subtree}.join(",")}})"
385
+ else
386
+ anonymous_function(block_code)+"((VALUE)#{locals_pointer}, (VALUE[]){})"
387
+ end
388
+ end
389
+
390
+ def to_c_block(tree)
391
+
392
+ str = ""
393
+ str = tree[1..-2].map{ |subtree|
394
+ to_c(subtree)
395
+ }.join(";")
396
+
397
+ if tree[-1][0] != :return
398
+ str = str + ";last_expression = #{to_c(tree[-1])};"
399
+ else
400
+ str = str + ";#{to_c(tree[-1])};"
401
+ end
402
+
403
+ caller_code = proc { |name| "
404
+ static VALUE #{name}(VALUE param) {
405
+ #{@locals_struct} *plocals = (void*)param;
406
+ VALUE last_expression;
407
+
408
+ #{str}
409
+
410
+ return last_expression;
411
+ }
412
+ "
413
+ }
414
+
415
+ anonymous_function(caller_code) + "((VALUE)#{locals_pointer})"
416
+ end
417
+
418
+ def to_c_return(tree)
419
+ "return #{to_c(tree[1])};\n"
420
+ end
421
+
422
+ def to_c_lit(tree)
423
+ "(VALUE)#{tree[1].internal_value}"
424
+ end
425
+
426
+ def to_c_nil(tree)
427
+ "Qnil"
428
+ end
429
+
430
+ def to_c_str(tree)
431
+ "(VALUE)#{tree[1].internal_value}"
432
+ end
433
+
434
+ def to_c_hash(tree)
435
+
436
+ hash_aset_code = ""
437
+ (0..(tree.size-3)/2).each do |i|
438
+ strkey = to_c tree[1 + i * 2]
439
+ strvalue = to_c tree[2 + i * 2]
440
+ hash_aset_code << "rb_hash_aset(hash, #{strkey}, #{strvalue});"
441
+ end
442
+
443
+ wrapper_func = proc { |name| "
444
+ static VALUE #{name}(VALUE value_params) {
445
+ #{@locals_struct} *plocals = (void*)value_params;
446
+ VALUE hash = rb_hash_new();
447
+ #{hash_aset_code}
448
+ return hash;
449
+ }
450
+ " }
451
+
452
+ anonymous_function(wrapper_func) + "((VALUE)#{locals_pointer})"
453
+ end
454
+
455
+ def to_c_array(tree)
456
+ if tree.size > 1
457
+ strargs = tree[1..-1].map{|subtree| to_c subtree}.join(",")
458
+ "rb_ary_new3(#{tree.size-1}, #{strargs})"
459
+ else
460
+ "rb_ary_new3(0)"
461
+ end
462
+ end
463
+
464
+ def to_c_defn(tree)
465
+ method_name = tree[1]
466
+ args_tree = tree[2]
467
+
468
+ impl_tree = tree[3][1]
469
+
470
+ @locals_struct = "struct {
471
+ #{@locals.map{|l| "VALUE #{l};\n"}.join}
472
+ #{args_tree[1..-1].map{|arg| "VALUE #{arg};\n"}.join};
473
+ void* block_function_address;
474
+ VALUE block_function_param;
475
+ }"
476
+
477
+ @block_struct = "struct {
478
+ void* block_function_address;
479
+ void* block_function_param;
480
+ }"
481
+
482
+ str_impl = ""
483
+ # if impl_tree is a block, implement the last node with a return
484
+ if impl_tree[0] == :block
485
+ str_impl = to_c impl_tree
486
+ else
487
+ if impl_tree[0] != :return
488
+ str_impl = str_impl + ";last_expression = #{to_c(impl_tree)};"
489
+ else
490
+ str_impl = str_impl + ";#{to_c(impl_tree)};"
491
+ end
492
+ end
493
+
494
+ strargs = if args_tree.size > 1
495
+ "VALUE block, #{args_tree[1..-1].map{|arg| "VALUE #{arg}" }.join(",") }"
496
+ else
497
+ "VALUE block"
498
+ end
499
+
500
+ "VALUE #{@alt_method_name || method_name}(#{strargs}) {
501
+ #{@locals_struct} locals;
502
+ #{@locals_struct} *plocals = (void*)&locals;
503
+ #{@block_struct} *pblock;
504
+ VALUE last_expression;
505
+
506
+ #{args_tree[1..-1].map { |arg|
507
+ "locals.#{arg} = #{arg};\n"
508
+ }.join("") }
509
+
510
+ locals.self = self;
511
+
512
+ pblock = (void*)block;
513
+ if (pblock) {
514
+ locals.block_function_address = pblock->block_function_address;
515
+ locals.block_function_param = (VALUE)pblock->block_function_param;
516
+ } else {
517
+ locals.block_function_address = 0;
518
+ locals.block_function_param = Qnil;
519
+ }
520
+
521
+ return #{str_impl};
522
+ }"
523
+ end
524
+
525
+ def locals_accessor
526
+ "plocals->"
527
+ end
528
+
529
+ def locals_pointer
530
+ "plocals"
531
+ end
532
+
533
+ def to_c_gvar(tree)
534
+ "rb_gvar_get((struct global_entry*)0x#{global_entry(tree[1]).to_s(16)})"
535
+ end
536
+
537
+ def to_c_gasgn(tree)
538
+ "_rb_gvar_set((void*)0x#{global_entry(tree[1]).to_s(16)}, #{to_c tree[2]})"
539
+ end
540
+
541
+ def to_c_ivar(tree)
542
+ "rb_ivar_get(#{locals_accessor}self,#{tree[1].to_i})"
543
+ end
544
+
545
+ def to_c_iasgn(tree)
546
+ "_rb_ivar_set(#{locals_accessor}self,#{tree[1].to_i},#{to_c tree[2]})"
547
+ end
548
+
549
+ def to_c_lasgn(tree)
550
+ if options[:validate_lvar_types]
551
+ klass = @infer_lvar_map[tree[1]]
552
+ if klass
553
+
554
+ verify_type_function = proc { |name| "
555
+ static VALUE #{name}(VALUE arg) {
556
+ if (CLASS_OF(arg)!=#{klass.internal_value}) rb_raise(#{TypeMismatchAssignmentException.internal_value}, \"Illegal assignment at runtime (type mismatch)\");
557
+ return arg;
558
+ }
559
+ "
560
+ }
561
+
562
+
563
+ "_lvar_assing(&#{locals_accessor}#{tree[1]}, #{anonymous_function(verify_type_function)}(#{to_c tree[2]}))"
564
+ else
565
+ "_lvar_assing(&#{locals_accessor}#{tree[1]},#{to_c tree[2]})"
566
+ end
567
+ else
568
+ "_lvar_assing(&#{locals_accessor}#{tree[1]},#{to_c tree[2]})"
569
+ end
570
+ end
571
+
572
+ def to_c_lvar(tree)
573
+ locals_accessor + tree[1].to_s
574
+ end
575
+
576
+ def to_c_self(tree)
577
+ locals_accessor + "self"
578
+ end
579
+
580
+ def to_c_false(tree)
581
+ "Qfalse"
582
+ end
583
+
584
+ def to_c_true(tree)
585
+ "Qtrue"
586
+ end
587
+
588
+ def to_c_and(tree)
589
+ "(RTEST(#{to_c tree[1]}) && RTEST(#{to_c tree[2]})) ? Qtrue : Qfalse"
590
+ end
591
+
592
+ def to_c_or(tree)
593
+ "(RTEST(#{to_c tree[1]}) || RTEST(#{to_c tree[2]})) ? Qtrue : Qfalse"
594
+ end
595
+
596
+ def to_c_not(tree)
597
+ "RTEST(#{to_c tree[1]}) ? Qfalse : Qtrue"
598
+ end
599
+
600
+ def to_c_if(tree)
601
+ condition_tree = tree[1]
602
+ impl_tree = tree[2]
603
+ else_tree = tree[3]
604
+ code = "if (RTEST(#{to_c condition_tree})) {
605
+ last_expression = #{to_c impl_tree};
606
+ }
607
+ "
608
+
609
+ if (else_tree)
610
+ code = code + " else {
611
+ last_expression = #{to_c else_tree};
612
+ }
613
+ "
614
+ end
615
+
616
+ caller_code = proc { |name| "
617
+ static VALUE #{name}(VALUE param) {
618
+ #{@locals_struct} *plocals = (void*)param;
619
+ VALUE last_expression = Qnil;
620
+
621
+ #{code};
622
+
623
+ return last_expression;
624
+ }
625
+ "
626
+ }
627
+
628
+ anonymous_function(caller_code) + "((VALUE)#{locals_pointer})"
629
+ end
630
+
631
+ def to_c_call(tree)
632
+ recv = tree[1]
633
+ mname = tree[2]
634
+ args = tree[3]
635
+
636
+ directive_code = directive(tree)
637
+ if directive_code
638
+ return directive_code
639
+ end
640
+
641
+ strargs = args[1..-1].map{|arg| to_c arg}.join(",")
642
+
643
+ argnum = args.size - 1
644
+
645
+ recv = recv || s(:self)
646
+
647
+ recvtype = infer_type(recv)
648
+
649
+ if recvtype
650
+
651
+ address = nil
652
+ mobject = nil
653
+
654
+ inference_complete = true
655
+ signature = [recvtype]
656
+
657
+ args[1..-1].each do |arg|
658
+ argtype = infer_type(arg)
659
+ if argtype
660
+ signature << argtype
661
+ else
662
+ inference_complete = false
663
+ end
664
+ end
665
+
666
+ convention = nil
667
+
668
+ if recvtype.respond_to? :method_tree and inference_complete
669
+
670
+ if recvtype.method_tree[tree[2]]
671
+ mobject = recvtype.build(signature, tree[2])
672
+ convention = :fastruby
673
+ else
674
+ mobject = recvtype.instance_method(tree[2])
675
+ convention = :cruby
676
+ end
677
+ else
678
+ mobject = recvtype.instance_method(tree[2])
679
+ convention = :cruby
680
+ end
681
+
682
+ address = getaddress(mobject)
683
+ len = getlen(mobject)
684
+
685
+ extraargs = ""
686
+ extraargs = ", Qfalse" if convention == :fastruby
687
+
688
+ if address then
689
+ if argnum == 0
690
+ value_cast = "VALUE"
691
+ value_cast = value_cast + ", VALUE" if convention == :fastruby
692
+
693
+ if convention == :fastruby
694
+ "((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{to_c recv}, Qfalse)"
695
+ else
696
+
697
+ str_incall_args = nil
698
+ if len == -1
699
+ str_incall_args = "0, (VALUE[]){}, recv"
700
+ value_cast = "int,VALUE*,VALUE"
701
+ elsif len == -2
702
+ str_incall_args = "recv, rb_ary_new4(#{})"
703
+ value_cast = "VALUE,VALUE"
704
+ else
705
+ str_incall_args = "recv"
706
+ end
707
+
708
+ wrapper_func = proc { |name| "
709
+ static VALUE #{name}(VALUE recv) {
710
+ // call to #{recvtype}##{mname}
711
+ if (rb_block_given_p()) {
712
+ // no passing block, recall
713
+ return rb_funcall(recv, #{tree[2].to_i}, 0);
714
+ } else {
715
+ return ((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{str_incall_args});
716
+ }
717
+ }
718
+ " }
719
+
720
+ anonymous_function(wrapper_func) + "(#{to_c(recv)})"
721
+
722
+ end
723
+ else
724
+ value_cast = ( ["VALUE"]*(args.size) ).join(",")
725
+ value_cast = value_cast + ", VALUE" if convention == :fastruby
726
+
727
+ wrapper_func = nil
728
+ if convention == :fastruby
729
+ "((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{to_c recv}, Qfalse, #{strargs})"
730
+ else
731
+
732
+ str_incall_args = nil
733
+ if len == -1
734
+ str_incall_args = "#{argnum}, (VALUE[]){#{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")}}, recv"
735
+ value_cast = "int,VALUE*,VALUE"
736
+ elsif len == -2
737
+ str_incall_args = "recv, rb_ary_new4(#{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")})"
738
+ value_cast = "VALUE,VALUE"
739
+ else
740
+ str_incall_args = "recv, #{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")}"
741
+ end
742
+
743
+ wrapper_func = proc { |name| "
744
+ static VALUE #{name}(VALUE recv, #{ (1..argnum).map{|x| "VALUE _arg"+x.to_s }.join(",")} ) {
745
+ // call to #{recvtype}##{mname}
746
+ if (rb_block_given_p()) {
747
+ // no passing block, recall
748
+ return rb_funcall(recv, #{tree[2].to_i}, #{argnum}, #{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")});
749
+ } else {
750
+ return ((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{str_incall_args});
751
+ }
752
+ }
753
+ " }
754
+
755
+ anonymous_function(wrapper_func) + "(#{to_c(recv)}, #{strargs})"
756
+ end
757
+ end
758
+ else
759
+
760
+ if argnum == 0
761
+ "rb_funcall(#{to_c recv}, #{tree[2].to_i}, 0)"
762
+ else
763
+ "rb_funcall(#{to_c recv}, #{tree[2].to_i}, #{argnum}, #{strargs} )"
764
+ end
765
+ end
766
+
767
+ else
768
+ if argnum == 0
769
+ "rb_funcall(#{to_c recv}, #{tree[2].to_i}, 0)"
770
+ else
771
+ "rb_funcall(#{to_c recv}, #{tree[2].to_i}, #{argnum}, #{strargs} )"
772
+ end
773
+ end
774
+ end
775
+
776
+ def to_c_while(tree)
777
+ caller_code = proc { |name| "
778
+ static VALUE #{name}(VALUE param) {
779
+ #{@locals_struct} *plocals = (void*)param;
780
+ VALUE last_expression;
781
+
782
+ while (#{to_c tree[1]}) {
783
+ #{to_c tree[2]};
784
+ }
785
+
786
+ return Qnil;
787
+ }
788
+ "
789
+ }
790
+
791
+ anonymous_function(caller_code) + "((VALUE)#{locals_pointer})"
792
+ end
793
+
794
+ def infer_type(recv)
795
+ if recv[0] == :call
796
+ if recv[2] == :infer
797
+ eval(recv[3].last.last.to_s)
798
+ end
799
+ elsif recv[0] == :lvar
800
+ @infer_lvar_map[recv[1]]
801
+ elsif recv[0] == :self
802
+ @infer_self
803
+ elsif recv[0] == :str or recv[0] == :lit
804
+ recv[1].class
805
+ else
806
+ nil
807
+ end
808
+ end
809
+
810
+ def with_extra_inference(extra_inference)
811
+ previous_infer_lvar_map = @infer_lvar_map
812
+ begin
813
+ @infer_lvar_map = @infer_lvar_map.merge(extra_inference)
814
+ yield
815
+ ensure
816
+ @infer_lvar_map = previous_infer_lvar_map
817
+ end
818
+ end
819
+
820
+ def directive(tree)
821
+ recv = tree[1]
822
+ mname = tree[2]
823
+ args = tree[3]
824
+
825
+ if mname == :infer
826
+ return to_c(recv)
827
+ elsif mname == :lvar_type
828
+ lvar_name = args[1][1] || args[1][2]
829
+ lvar_type = eval(args[2][1].to_s)
830
+
831
+ @infer_lvar_map[lvar_name] = lvar_type
832
+ return ""
833
+ elsif mname == :block_given?
834
+ return "#{locals_accessor}block_function_address == 0 ? Qfalse : Qtrue"
835
+ elsif mname == :inline_c
836
+ code = args[1][1]
837
+
838
+ caller_code = proc { |name| "
839
+ static VALUE #{name}(VALUE param) {
840
+ #{@locals_struct} *plocals = (void*)param;
841
+ #{code};
842
+ return Qnil;
843
+ }
844
+ "
845
+ }
846
+
847
+ return anonymous_function(caller_code)+"((VALUE)plocals)"
848
+ else
849
+ nil
850
+ end
851
+ end
852
+
853
+ inline :C do |builder|
854
+ builder.include "<node.h>"
855
+ builder.c "VALUE getaddress(VALUE method) {
856
+ struct METHOD {
857
+ VALUE klass, rklass;
858
+ VALUE recv;
859
+ ID id, oid;
860
+ int safe_level;
861
+ NODE *body;
862
+ };
863
+
864
+ struct METHOD *data;
865
+ Data_Get_Struct(method, struct METHOD, data);
866
+
867
+ if (nd_type(data->body) == NODE_CFUNC) {
868
+ return INT2FIX(data->body->nd_cfnc);
869
+ }
870
+
871
+ return Qnil;
872
+ }"
873
+
874
+ builder.c "VALUE getlen(VALUE method) {
875
+ struct METHOD {
876
+ VALUE klass, rklass;
877
+ VALUE recv;
878
+ ID id, oid;
879
+ int safe_level;
880
+ NODE *body;
881
+ };
882
+
883
+ struct METHOD *data;
884
+ Data_Get_Struct(method, struct METHOD, data);
885
+
886
+ if (nd_type(data->body) == NODE_CFUNC) {
887
+ return INT2FIX(data->body->nd_argc);
888
+ }
889
+
890
+ return Qnil;
891
+ }"
892
+
893
+ builder.c "VALUE global_entry(VALUE global_id) {
894
+ ID id = SYM2ID(global_id);
895
+ struct global_entry* entry;
896
+
897
+ entry = rb_global_entry(id);
898
+ return INT2FIX(entry);
899
+ }
900
+ "
901
+ end
902
+ end
903
+ end