fastruby 0.0.12 → 0.0.13

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,1171 @@
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 "set"
22
+ require "rubygems"
23
+ require "sexp"
24
+ require "fastruby/method_extension"
25
+ require "fastruby/set_tree"
26
+ require "fastruby/exceptions"
27
+ require "fastruby/translator/translator_modules"
28
+
29
+
30
+ module FastRuby
31
+ class Context
32
+ attr_accessor :infer_lvar_map
33
+ attr_accessor :alt_method_name
34
+ attr_accessor :locals
35
+ attr_accessor :options
36
+ attr_accessor :infer_self
37
+ attr_accessor :snippet_hash
38
+ attr_reader :no_cache
39
+ attr_reader :init_extra
40
+ attr_reader :extra_code
41
+ attr_reader :yield_signature
42
+
43
+ TranslatorModules.instance.load_under(FastRuby.fastruby_load_path + "/fastruby/translator/modules/")
44
+ TranslatorModules.instance.modls.each do |modl|
45
+ include modl
46
+ end
47
+
48
+ def initialize(common_func = true)
49
+ @infer_lvar_map = Hash.new
50
+ @no_cache = false
51
+ @extra_code = ""
52
+ @options = {}
53
+ @init_extra = Array.new
54
+ @frame_struct = "struct {
55
+ void* parent_frame;
56
+ void* plocals;
57
+ jmp_buf jmp;
58
+ VALUE return_value;
59
+ int rescue;
60
+ VALUE last_error;
61
+ VALUE next_recv;
62
+ int targetted;
63
+ struct FASTRUBYTHREADDATA* thread_data;
64
+ }"
65
+
66
+ @block_struct = "struct {
67
+ void* block_function_address;
68
+ void* block_function_param;
69
+ }"
70
+
71
+
72
+ extra_code << "
73
+ #include \"node.h\"
74
+
75
+ #define FASTRUBY_TAG_RETURN 0x80
76
+ #define FASTRUBY_TAG_NEXT 0x81
77
+ #define FASTRUBY_TAG_BREAK 0x82
78
+ #define FASTRUBY_TAG_RAISE 0x83
79
+ #define FASTRUBY_TAG_REDO 0x84
80
+ #define FASTRUBY_TAG_RETRY 0x85
81
+ #define TAG_RAISE 0x6
82
+
83
+ #ifndef __INLINE_FASTRUBY_BASE
84
+ #include \"#{FastRuby.fastruby_load_path}/../ext/fastruby_base/fastruby_base.inl\"
85
+ #define __INLINE_FASTRUBY_BASE
86
+ #endif
87
+ "
88
+
89
+ ruby_code = "
90
+ $LOAD_PATH << #{FastRuby.fastruby_load_path.inspect}
91
+ require #{FastRuby.fastruby_script_path.inspect}
92
+ "
93
+
94
+ init_extra << "
95
+ rb_eval_string(#{ruby_code.inspect});
96
+ "
97
+
98
+ @lambda_node_gvar = add_global_name("NODE*", 0);
99
+ @proc_node_gvar = add_global_name("NODE*", 0);
100
+ @procnew_node_gvar = add_global_name("NODE*", 0);
101
+
102
+ init_extra << "
103
+ #{@lambda_node_gvar} = rb_method_node(rb_cObject, #{intern_num :lambda});
104
+ #{@proc_node_gvar} = rb_method_node(rb_cObject, #{intern_num :proc});
105
+ #{@procnew_node_gvar} = rb_method_node(CLASS_OF(rb_cProc), #{intern_num :new});
106
+ "
107
+
108
+ @common_func = common_func
109
+ if common_func
110
+ extra_code << "static VALUE _rb_gvar_set(void* ge,VALUE value) {
111
+ rb_gvar_set((struct global_entry*)ge,value);
112
+ return value;
113
+ }
114
+ "
115
+
116
+ extra_code << "static VALUE re_yield(int argc, VALUE* argv, VALUE param, VALUE _parent_frame) {
117
+ VALUE yield_args = rb_ary_new4(argc,argv);
118
+ VALUE* yield_args_p = &yield_args;
119
+
120
+ #{@frame_struct}* pframe;
121
+ pframe = (typeof(pframe))_parent_frame;
122
+
123
+ return #{protected_block("rb_yield_splat(*(VALUE*)yield_args_p)",true,"yield_args_p",true)};
124
+ }"
125
+
126
+ extra_code << "static VALUE _rb_ivar_set(VALUE recv,ID idvar, VALUE value) {
127
+ rb_ivar_set(recv,idvar,value);
128
+ return value;
129
+ }
130
+ "
131
+
132
+ extra_code << "static VALUE __rb_cvar_set(VALUE recv,ID idvar, VALUE value, int warn) {
133
+ rb_cvar_set(recv,idvar,value,warn);
134
+ return value;
135
+ }
136
+ "
137
+
138
+ extra_code << "static VALUE _lvar_assing(VALUE* destination,VALUE value) {
139
+ *destination = value;
140
+ return value;
141
+ }
142
+
143
+ /*
144
+ #{caller.join("\n")}
145
+ */
146
+
147
+ "
148
+ end
149
+ end
150
+
151
+ def to_c(tree)
152
+ return "Qnil" unless tree
153
+ send("to_c_" + tree[0].to_s, tree);
154
+ end
155
+
156
+ def anonymous_function
157
+
158
+ name = "anonymous" + rand(10000000).to_s
159
+ extra_code << yield(name)
160
+
161
+ name
162
+ end
163
+
164
+ def frame_call(inner_code)
165
+ inline_block "
166
+
167
+
168
+ // create a call_frame
169
+ #{@frame_struct} call_frame;
170
+ typeof(call_frame)* old_pframe = (void*)pframe;
171
+
172
+ pframe = (typeof(pframe))&call_frame;
173
+
174
+ call_frame.parent_frame = (void*)pframe;
175
+ call_frame.plocals = plocals;
176
+ call_frame.return_value = Qnil;
177
+ call_frame.targetted = 0;
178
+ call_frame.thread_data = old_pframe->thread_data;
179
+ if (call_frame.thread_data == 0) call_frame.thread_data = rb_current_thread_data();
180
+
181
+ VALUE old_call_frame = plocals->call_frame;
182
+ plocals->call_frame = LONG2FIX(&call_frame);
183
+
184
+ int aux = setjmp(call_frame.jmp);
185
+ if (aux != 0) {
186
+ if (call_frame.targetted == 0) {
187
+ longjmp(old_pframe->jmp,aux);
188
+ }
189
+
190
+ if (aux == FASTRUBY_TAG_BREAK) {
191
+ plocals->call_frame = old_call_frame;
192
+ return call_frame.return_value;
193
+ } else if (aux == FASTRUBY_TAG_RETRY ) {
194
+ // do nothing and let the call execute again
195
+ } else {
196
+ plocals->call_frame = old_call_frame;
197
+ return call_frame.return_value;
198
+ }
199
+ }
200
+
201
+ VALUE ret = #{inner_code};
202
+ plocals->call_frame = old_call_frame;
203
+ return ret;
204
+ "
205
+ end
206
+
207
+ def initialize_method_structs(args_tree)
208
+ @locals_struct = "struct {
209
+ VALUE return_value;
210
+ VALUE pframe;
211
+ VALUE block_function_address;
212
+ VALUE block_function_param;
213
+ VALUE call_frame;
214
+ VALUE active;
215
+ VALUE targetted;
216
+ #{@locals.map{|l| "VALUE #{l};\n"}.join}
217
+ #{args_tree[1..-1].map{|arg| "VALUE #{arg.to_s.gsub("*","")};\n"}.join};
218
+ }"
219
+
220
+ end
221
+
222
+ def to_c_method_defs(tree)
223
+
224
+ method_name = tree[2]
225
+ args_tree = tree[3]
226
+
227
+ impl_tree = tree[4][1]
228
+
229
+ initialize_method_structs(args_tree)
230
+
231
+ strargs = if args_tree.size > 1
232
+
233
+ "VALUE self, void* block_address, VALUE block_param, void* _parent_frame, #{args_tree[1..-1].map{|arg| "VALUE #{arg.to_s.gsub("*","")}" }.join(",") }"
234
+ else
235
+ "VALUE self, void* block_address, VALUE block_param, void* _parent_frame"
236
+ end
237
+
238
+ extra_code << "static VALUE #{@alt_method_name + "_real"}(#{strargs}) {
239
+ #{func_frame}
240
+
241
+ #{args_tree[1..-1].map { |arg|
242
+ arg = arg.to_s
243
+ arg.gsub!("*","")
244
+ "plocals->#{arg} = #{arg};\n"
245
+ }.join("") }
246
+
247
+ plocals->block_function_address = LONG2FIX(block_address);
248
+ plocals->block_function_param = LONG2FIX(block_param);
249
+
250
+ return #{to_c impl_tree};
251
+ }"
252
+
253
+ strargs2 = if args_tree.size > 1
254
+ "VALUE self, #{args_tree[1..-1].map{|arg| "VALUE #{arg}" }.join(",") }"
255
+ else
256
+ "VALUE self"
257
+ end
258
+
259
+ value_cast = ( ["VALUE"]*(args_tree.size+1) ).join(",")
260
+ strmethodargs = ""
261
+
262
+ if args_tree.size > 1
263
+ strmethodargs = "self,block_address,block_param,&frame,#{args_tree[1..-1].map(&:to_s).join(",") }"
264
+ else
265
+ strmethodargs = "self,block_address,block_param,&frame"
266
+ end
267
+
268
+ "
269
+ VALUE #{@alt_method_name}(#{strargs2}) {
270
+ #{@frame_struct} frame;
271
+ int argc = #{args_tree.size};
272
+ void* block_address = 0;
273
+ VALUE block_param = Qnil;
274
+
275
+ frame.plocals = 0;
276
+ frame.parent_frame = 0;
277
+ frame.return_value = Qnil;
278
+ frame.rescue = 0;
279
+ frame.targetted = 0;
280
+ frame.thread_data = rb_current_thread_data();
281
+
282
+ if (rb_block_given_p()) {
283
+ block_address = #{
284
+ anonymous_function{|name|
285
+ "static VALUE #{name}(int argc, VALUE* argv, VALUE param) {
286
+ return rb_yield_splat(rb_ary_new4(argc,argv));
287
+ }"
288
+ }
289
+ };
290
+
291
+ block_param = 0;
292
+ }
293
+
294
+ int aux = setjmp(frame.jmp);
295
+ if (aux != 0) {
296
+ rb_funcall(self, #{intern_num :raise}, 1, frame.thread_data->exception);
297
+ }
298
+
299
+
300
+ return #{@alt_method_name + "_real"}(#{strmethodargs});
301
+ }
302
+ "
303
+ end
304
+
305
+ def add_main
306
+ if options[:main]
307
+
308
+ extra_code << "
309
+ static VALUE #{@alt_method_name}(VALUE self__);
310
+ static VALUE main_proc_call(VALUE self__, VALUE class_self_) {
311
+ #{@alt_method_name}(class_self_);
312
+ return Qnil;
313
+ }
314
+
315
+ "
316
+
317
+ init_extra << "
318
+ {
319
+ VALUE newproc = rb_funcall(rb_cObject,#{intern_num :new},0);
320
+ rb_define_singleton_method(newproc, \"call\", main_proc_call, 1);
321
+ rb_gv_set(\"$last_obj_proc\", newproc);
322
+
323
+ }
324
+ "
325
+ end
326
+ end
327
+
328
+ def define_method_at_init(klass,method_name, size, signature)
329
+ init_extra << "
330
+ {
331
+ VALUE method_name = rb_funcall(
332
+ #{literal_value FastRuby},
333
+ #{intern_num :make_str_signature},
334
+ 2,
335
+ #{literal_value method_name},
336
+ #{literal_value signature}
337
+ );
338
+
339
+ rb_define_method(#{literal_value klass}, RSTRING(method_name)->ptr, #{alt_method_name}, #{size});
340
+ }
341
+ "
342
+ end
343
+
344
+ def to_c_method(tree)
345
+ method_name = tree[1]
346
+ args_tree = tree[2]
347
+ impl_tree = tree[3][1]
348
+
349
+ if (options[:main])
350
+ initialize_method_structs(args_tree)
351
+
352
+ strargs = if args_tree.size > 1
353
+ "VALUE block, VALUE _parent_frame, #{args_tree[1..-1].map{|arg| "VALUE #{arg.to_s.gsub("*","")}" }.join(",") }"
354
+ else
355
+ "VALUE block, VALUE _parent_frame"
356
+ end
357
+
358
+ ret = "VALUE #{@alt_method_name || method_name}() {
359
+
360
+ #{@locals_struct} *plocals;
361
+ #{@frame_struct} frame;
362
+ #{@frame_struct} *pframe;
363
+
364
+ frame.parent_frame = 0;
365
+ frame.return_value = Qnil;
366
+ frame.rescue = 0;
367
+ frame.targetted = 0;
368
+ frame.thread_data = rb_current_thread_data();
369
+
370
+
371
+ int stack_chunk_instantiated = 0;
372
+ VALUE rb_previous_stack_chunk = Qnil;
373
+ VALUE rb_stack_chunk = frame.thread_data->rb_stack_chunk;
374
+ struct STACKCHUNK* stack_chunk = 0;
375
+
376
+ if (rb_stack_chunk != Qnil) {
377
+ Data_Get_Struct(rb_stack_chunk,struct STACKCHUNK,stack_chunk);
378
+ }
379
+
380
+ if (stack_chunk == 0 || (stack_chunk == 0 ? 0 : stack_chunk_frozen(stack_chunk)) ) {
381
+ rb_previous_stack_chunk = rb_stack_chunk;
382
+ rb_gc_register_address(&rb_stack_chunk);
383
+ stack_chunk_instantiated = 1;
384
+
385
+ rb_stack_chunk = rb_stack_chunk_create(Qnil);
386
+ frame.thread_data->rb_stack_chunk = rb_stack_chunk;
387
+
388
+ rb_ivar_set(rb_stack_chunk, #{intern_num :_parent_stack_chunk}, rb_previous_stack_chunk);
389
+
390
+ Data_Get_Struct(rb_stack_chunk,struct STACKCHUNK,stack_chunk);
391
+ }
392
+
393
+ int previous_stack_position = stack_chunk_get_current_position(stack_chunk);
394
+
395
+ plocals = (typeof(plocals))stack_chunk_alloc(stack_chunk ,sizeof(typeof(*plocals))/sizeof(void*));
396
+ plocals->active = Qtrue;
397
+ plocals->targetted = Qfalse;
398
+ plocals->pframe = LONG2FIX(&frame);
399
+ frame.plocals = plocals;
400
+
401
+ pframe = (void*)&frame;
402
+
403
+ VALUE last_expression = Qnil;
404
+
405
+ int aux = setjmp(pframe->jmp);
406
+ if (aux != 0) {
407
+ stack_chunk_set_current_position(stack_chunk, previous_stack_position);
408
+
409
+ if (stack_chunk_instantiated) {
410
+ rb_gc_unregister_address(&rb_stack_chunk);
411
+ frame.thread_data->rb_stack_chunk = rb_previous_stack_chunk;
412
+ }
413
+
414
+ plocals->active = Qfalse;
415
+ return plocals->return_value;
416
+ }
417
+
418
+ plocals->self = self;
419
+
420
+ #{args_tree[1..-1].map { |arg|
421
+ arg = arg.to_s
422
+ arg.gsub!("*","")
423
+ "plocals->#{arg} = #{arg};\n"
424
+ }.join("") }
425
+
426
+ plocals->block_function_address = LONG2FIX(0);
427
+ plocals->block_function_param = LONG2FIX(Qnil);
428
+ plocals->call_frame = LONG2FIX(0);
429
+
430
+ VALUE ret = #{to_c impl_tree};
431
+ stack_chunk_set_current_position(stack_chunk, previous_stack_position);
432
+
433
+ if (stack_chunk_instantiated) {
434
+ rb_gc_unregister_address(&rb_stack_chunk);
435
+ frame.thread_data->rb_stack_chunk = rb_previous_stack_chunk;
436
+ }
437
+
438
+ plocals->active = Qfalse;
439
+ return ret;
440
+
441
+ }"
442
+
443
+ add_main
444
+ ret
445
+ else
446
+
447
+ initialize_method_structs(args_tree)
448
+
449
+ strargs = if args_tree.size > 1
450
+ "VALUE block, VALUE _parent_frame, #{args_tree[1..-1].map{|arg| "VALUE #{arg.to_s.gsub("*","")}" }.join(",") }"
451
+ else
452
+ "VALUE block, VALUE _parent_frame"
453
+ end
454
+
455
+ ret = "VALUE #{@alt_method_name || method_name}(#{strargs}) {
456
+
457
+ #{@frame_struct} frame;
458
+ #{@frame_struct} *pframe;
459
+
460
+ frame.parent_frame = (void*)_parent_frame;
461
+ frame.return_value = Qnil;
462
+ frame.rescue = 0;
463
+ frame.targetted = 0;
464
+ frame.thread_data = ((typeof(pframe))_parent_frame)->thread_data;
465
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
466
+
467
+ int stack_chunk_instantiated = 0;
468
+ VALUE rb_previous_stack_chunk = Qnil;
469
+ VALUE rb_stack_chunk = frame.thread_data->rb_stack_chunk;
470
+ struct STACKCHUNK* stack_chunk = 0;
471
+
472
+ if (rb_stack_chunk != Qnil) {
473
+ Data_Get_Struct(rb_stack_chunk,struct STACKCHUNK,stack_chunk);
474
+ }
475
+
476
+ if (stack_chunk == 0 || (stack_chunk == 0 ? 0 : stack_chunk_frozen(stack_chunk)) ) {
477
+ rb_previous_stack_chunk = rb_stack_chunk;
478
+ rb_gc_register_address(&rb_stack_chunk);
479
+ stack_chunk_instantiated = 1;
480
+
481
+ rb_stack_chunk = rb_stack_chunk_create(Qnil);
482
+ frame.thread_data->rb_stack_chunk = rb_stack_chunk;
483
+
484
+ rb_ivar_set(rb_stack_chunk, #{intern_num :_parent_stack_chunk}, rb_previous_stack_chunk);
485
+
486
+ Data_Get_Struct(rb_stack_chunk,struct STACKCHUNK,stack_chunk);
487
+ }
488
+
489
+
490
+ #{@locals_struct} *plocals;
491
+
492
+ int previous_stack_position = stack_chunk_get_current_position(stack_chunk);
493
+
494
+ plocals = (typeof(plocals))stack_chunk_alloc(stack_chunk ,sizeof(typeof(*plocals))/sizeof(void*));
495
+ frame.plocals = plocals;
496
+ plocals->active = Qtrue;
497
+ plocals->targetted = Qfalse;
498
+ plocals->pframe = LONG2FIX(&frame);
499
+ plocals->call_frame = LONG2FIX(0);
500
+
501
+ pframe = (void*)&frame;
502
+
503
+ #{@block_struct} *pblock;
504
+ VALUE last_expression = Qnil;
505
+
506
+ int aux = setjmp(pframe->jmp);
507
+ if (aux != 0) {
508
+ plocals->active = Qfalse;
509
+
510
+ stack_chunk_set_current_position(stack_chunk, previous_stack_position);
511
+
512
+ if (stack_chunk_instantiated) {
513
+ rb_gc_unregister_address(&rb_stack_chunk);
514
+ frame.thread_data->rb_stack_chunk = rb_previous_stack_chunk;
515
+ }
516
+
517
+ if (plocals->targetted == Qfalse) {
518
+ longjmp(((typeof(pframe))_parent_frame)->jmp,aux);
519
+ }
520
+
521
+ return plocals->return_value;
522
+ }
523
+
524
+ plocals->self = self;
525
+
526
+ #{args_tree[1..-1].map { |arg|
527
+ arg = arg.to_s
528
+ arg.gsub!("*","")
529
+ "plocals->#{arg} = #{arg};\n"
530
+ }.join("") }
531
+
532
+ pblock = (void*)block;
533
+ if (pblock) {
534
+ plocals->block_function_address = LONG2FIX(pblock->block_function_address);
535
+ plocals->block_function_param = LONG2FIX(pblock->block_function_param);
536
+ } else {
537
+ plocals->block_function_address = LONG2FIX(0);
538
+ plocals->block_function_param = LONG2FIX(Qnil);
539
+ }
540
+
541
+ VALUE __ret = #{to_c impl_tree};
542
+ stack_chunk_set_current_position(stack_chunk, previous_stack_position);
543
+
544
+ if (stack_chunk_instantiated) {
545
+ rb_gc_unregister_address(&rb_stack_chunk);
546
+ frame.thread_data->rb_stack_chunk = rb_previous_stack_chunk;
547
+ }
548
+
549
+ plocals->active = Qfalse;
550
+ return __ret;
551
+ }"
552
+
553
+ add_main
554
+ ret
555
+ end
556
+ end
557
+
558
+ def locals_accessor
559
+ "plocals->"
560
+ end
561
+
562
+ def locals_scope(locals)
563
+ old_locals = @locals
564
+ old_locals_struct = @locals_struct
565
+
566
+ @locals = locals
567
+ @locals_struct = "struct {
568
+ VALUE return_value;
569
+ VALUE pframe;
570
+ VALUE block_function_address;
571
+ VALUE block_function_param;
572
+ VALUE call_frame;
573
+ VALUE active;
574
+ VALUE targetted;
575
+ #{@locals.map{|l| "VALUE #{l};\n"}.join}
576
+ }"
577
+
578
+ begin
579
+ yield
580
+ ensure
581
+ @locals = old_locals
582
+ @locals_struct = old_locals_struct
583
+ end
584
+ end
585
+
586
+ def infer_type(recv)
587
+ if recv[0] == :call
588
+ if recv[2] == :infer
589
+ eval(recv[3].last.last.to_s)
590
+ end
591
+ elsif recv[0] == :lvar
592
+ @infer_lvar_map[recv[1]]
593
+ elsif recv[0] == :self
594
+ @infer_self
595
+ elsif recv[0] == :str or recv[0] == :lit
596
+ recv[1].class
597
+ else
598
+ nil
599
+ end
600
+ end
601
+
602
+ def on_block
603
+ old_on_block = @on_block
604
+ @on_block = true
605
+ return yield
606
+ ensure
607
+ @on_block = old_on_block
608
+ end
609
+
610
+ def with_extra_inference(extra_inference)
611
+ previous_infer_lvar_map = @infer_lvar_map
612
+ begin
613
+ @infer_lvar_map = @infer_lvar_map.merge(extra_inference)
614
+ yield
615
+ ensure
616
+ @infer_lvar_map = previous_infer_lvar_map
617
+ end
618
+ end
619
+
620
+ def directive(tree)
621
+ recv = tree[1]
622
+ mname = tree[2]
623
+ args = tree[3]
624
+
625
+ if mname == :infer
626
+ return to_c(recv)
627
+ elsif mname == :lvar_type
628
+ lvar_name = args[1][1] || args[1][2]
629
+ lvar_type = eval(args[2][1].to_s)
630
+
631
+ @infer_lvar_map[lvar_name] = lvar_type
632
+ return ""
633
+ elsif mname == :block_given?
634
+ return "FIX2LONG(plocals->block_function_address) == 0 ? Qfalse : Qtrue"
635
+ elsif mname == :inline_c
636
+
637
+ code = args[1][1]
638
+
639
+ unless (args[2] == s(:false))
640
+ return anonymous_function{ |name| "
641
+ static VALUE #{name}(VALUE param) {
642
+ #{@frame_struct} *pframe = (void*)param;
643
+ #{@locals_struct} *plocals = (void*)pframe->plocals;
644
+ #{code};
645
+ return Qnil;
646
+ }
647
+ "
648
+ }+"((VALUE)pframe)"
649
+ else
650
+ code
651
+ end
652
+
653
+ else
654
+ nil
655
+ end
656
+ end
657
+
658
+ def inline_block_reference(arg, nolocals = false)
659
+ code = nil
660
+
661
+ if arg.instance_of? FastRuby::FastRubySexp
662
+ code = to_c(arg);
663
+ else
664
+ code = arg
665
+ end
666
+
667
+ anonymous_function{ |name| "
668
+ static VALUE #{name}(VALUE param) {
669
+ #{@frame_struct} *pframe = (void*)param;
670
+
671
+ #{nolocals ? "" : "#{@locals_struct} *plocals = (void*)pframe->plocals;"}
672
+ VALUE last_expression = Qnil;
673
+
674
+ #{code};
675
+ return last_expression;
676
+ }
677
+ "
678
+ }
679
+ end
680
+
681
+ def inline_block(code, repass_var = nil, nolocals = false)
682
+ anonymous_function{ |name| "
683
+ static VALUE #{name}(VALUE param#{repass_var ? ",void* " + repass_var : "" }) {
684
+ #{@frame_struct} *pframe = (void*)param;
685
+
686
+ #{nolocals ? "" : "#{@locals_struct} *plocals = (void*)pframe->plocals;"}
687
+ VALUE last_expression = Qnil;
688
+
689
+ #{code}
690
+ }
691
+ "
692
+ } + "((VALUE)pframe#{repass_var ? ", " + repass_var : "" })"
693
+ end
694
+
695
+ def inline_ruby(proced, parameter)
696
+ "rb_funcall(#{proced.__id__}, #{intern_num :call}, 1, #{parameter})"
697
+ end
698
+
699
+ def protected_block(inner_code, always_rescue = false,repass_var = nil, nolocals = false)
700
+ body = nil
701
+ rescue_args = nil
702
+ if repass_var
703
+ body = anonymous_function{ |name| "
704
+ static VALUE #{name}(VALUE param) {
705
+
706
+ #{@frame_struct} frame;
707
+
708
+ typeof(frame)* pframe;
709
+ typeof(frame)* parent_frame = ((typeof(pframe))((void**)param)[0]);
710
+
711
+ frame.parent_frame = 0;
712
+ frame.return_value = Qnil;
713
+ frame.rescue = 0;
714
+ frame.last_error = Qnil;
715
+ frame.targetted = 0;
716
+ frame.thread_data = parent_frame->thread_data;
717
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
718
+
719
+ pframe = &frame;
720
+
721
+ #{
722
+ nolocals ? "frame.plocals = 0;" : "#{@locals_struct}* plocals = parent_frame->plocals;
723
+ frame.plocals = plocals;
724
+ "
725
+ }
726
+
727
+ int aux = setjmp(frame.jmp);
728
+ if (aux != 0) {
729
+
730
+ if (frame.targetted == 1) {
731
+ return frame.return_value;
732
+ } else {
733
+ rb_jump_tag(aux);
734
+ }
735
+ }
736
+
737
+ VALUE #{repass_var} = (VALUE)((void**)param)[1];
738
+ return #{inner_code};
739
+ }
740
+ "
741
+ }
742
+
743
+ rescue_args = ""
744
+ rescue_args = "(VALUE)(VALUE[]){(VALUE)pframe,(VALUE)#{repass_var}}"
745
+ else
746
+
747
+ body = anonymous_function{ |name| "
748
+ static VALUE #{name}(VALUE param) {
749
+ #{@frame_struct} frame;
750
+
751
+ typeof(frame)* pframe;
752
+ typeof(frame)* parent_frame = (typeof(pframe))param;
753
+
754
+ frame.parent_frame = 0;
755
+ frame.return_value = Qnil;
756
+ frame.rescue = 0;
757
+ frame.last_error = Qnil;
758
+ frame.targetted = 0;
759
+ frame.thread_data = parent_frame->thread_data;
760
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
761
+
762
+ pframe = &frame;
763
+
764
+ #{
765
+ nolocals ? "frame.plocals = 0;" : "#{@locals_struct}* plocals = parent_frame->plocals;
766
+ frame.plocals = plocals;
767
+ "
768
+ }
769
+
770
+ int aux = setjmp(frame.jmp);
771
+ if (aux != 0) {
772
+
773
+ if (frame.targetted == 1) {
774
+ return frame.return_value;
775
+ } else {
776
+ rb_jump_tag(aux);
777
+ }
778
+ }
779
+
780
+ return #{inner_code};
781
+ }
782
+ "
783
+ }
784
+
785
+ rescue_args = "(VALUE)pframe"
786
+ end
787
+
788
+ wrapper_code = " if (state != 0) {
789
+ if (state < 0x80) {
790
+
791
+ if (state == TAG_RAISE) {
792
+ // raise emulation
793
+ pframe->thread_data->exception = rb_eval_string(\"$!\");
794
+ longjmp(pframe->jmp, FASTRUBY_TAG_RAISE);
795
+ return Qnil;
796
+ } else {
797
+ rb_jump_tag(state);
798
+ }
799
+ } else {
800
+ longjmp(pframe->jmp, state);
801
+ }
802
+
803
+ }
804
+ "
805
+
806
+ rescue_code = "rb_protect(#{body},#{rescue_args},&state)"
807
+
808
+ if always_rescue
809
+ inline_block "
810
+ int state;
811
+ pframe->last_error = Qnil;
812
+ VALUE result = #{rescue_code};
813
+
814
+ #{wrapper_code}
815
+
816
+ return result;
817
+ ", repass_var, nolocals
818
+ else
819
+ inline_block "
820
+ VALUE result;
821
+ int state;
822
+ pframe->last_error = Qnil;
823
+
824
+ if (pframe->rescue) {
825
+ result = #{rescue_code};
826
+ #{wrapper_code}
827
+ } else {
828
+ return #{inner_code};
829
+ }
830
+
831
+ return result;
832
+ ", repass_var, nolocals
833
+ end
834
+ end
835
+
836
+ def func_frame
837
+ "
838
+ #{@locals_struct} *plocals = malloc(sizeof(typeof(*plocals)));
839
+ #{@frame_struct} frame;
840
+ #{@frame_struct} *pframe;
841
+
842
+ frame.plocals = plocals;
843
+ frame.parent_frame = (void*)_parent_frame;
844
+ frame.return_value = Qnil;
845
+ frame.rescue = 0;
846
+ frame.targetted = 0;
847
+ frame.thread_data = ((typeof(pframe))_parent_frame)->thread_data;
848
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
849
+
850
+ plocals->pframe = LONG2FIX(&frame);
851
+ plocals->targetted = Qfalse;
852
+
853
+ pframe = (void*)&frame;
854
+
855
+ #{@block_struct} *pblock;
856
+ VALUE last_expression = Qnil;
857
+
858
+ int aux = setjmp(pframe->jmp);
859
+ if (aux != 0) {
860
+
861
+ if (plocals->targetted == Qfalse) {
862
+ longjmp(((typeof(pframe))_parent_frame)->jmp,aux);
863
+ }
864
+
865
+ return plocals->return_value;
866
+ }
867
+
868
+ plocals->self = self;
869
+ "
870
+ end
871
+
872
+ def c_escape(str)
873
+ str.inspect
874
+ end
875
+
876
+ def literal_value(value)
877
+ @literal_value_hash = Hash.new unless @literal_value_hash
878
+ return @literal_value_hash[value] if @literal_value_hash[value]
879
+
880
+ name = self.add_global_name("VALUE", "Qnil");
881
+
882
+ begin
883
+
884
+ str = Marshal.dump(value)
885
+
886
+
887
+ if value.instance_of? Module
888
+
889
+ container_str = value.to_s.split("::")[0..-2].join("::")
890
+
891
+ init_extra << "
892
+ #{name} = rb_define_module_under(
893
+ #{container_str == "" ? "rb_cObject" : literal_value(eval(container_str))}
894
+ ,\"#{value.to_s.split("::").last}\");
895
+
896
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
897
+ "
898
+ elsif value.instance_of? Class
899
+ container_str = value.to_s.split("::")[0..-2].join("::")
900
+
901
+ init_extra << "
902
+ #{name} = rb_define_class_under(
903
+ #{container_str == "" ? "rb_cObject" : literal_value(eval(container_str))}
904
+ ,\"#{value.to_s.split("::").last}\"
905
+ ,#{value.superclass == Object ? "rb_cObject" : literal_value(value.superclass)});
906
+
907
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
908
+ "
909
+ elsif value.instance_of? Array
910
+ init_extra << "
911
+ #{name} = rb_ary_new3(#{value.size}, #{value.map{|x| literal_value x}.join(",")} );
912
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
913
+ "
914
+ else
915
+
916
+ init_extra << "
917
+ #{name} = rb_marshal_load(rb_str_new(#{c_escape str}, #{str.size}));
918
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
919
+
920
+ "
921
+ end
922
+ rescue TypeError => e
923
+ @no_cache = true
924
+ FastRuby.logger.info "#{value} disabling cache for extension"
925
+ init_extra << "
926
+ #{name} = rb_funcall(rb_const_get(rb_cObject, #{intern_num :ObjectSpace}), #{intern_num :_id2ref}, 1, INT2FIX(#{value.__id__}));
927
+ "
928
+
929
+ end
930
+ @literal_value_hash[value] = name
931
+
932
+ name
933
+ end
934
+
935
+ def encode_address(recvtype,signature,mname,call_tree,inference_complete,convention_global_name = nil)
936
+ name = self.add_global_name("void*", 0);
937
+ cruby_name = self.add_global_name("void*", 0);
938
+ cruby_len = self.add_global_name("int", 0);
939
+ args_tree = call_tree[3]
940
+ method_tree = nil
941
+
942
+ begin
943
+ method_tree = recvtype.instance_method(@method_name.to_sym).fastruby.tree
944
+ rescue NoMethodError
945
+ end
946
+
947
+
948
+ strargs_signature = (0..args_tree.size-2).map{|x| "VALUE arg#{x}"}.join(",")
949
+ strargs = (0..args_tree.size-2).map{|x| "arg#{x}"}.join(",")
950
+ inprocstrargs = (1..args_tree.size-1).map{|x| "((VALUE*)method_arguments)[#{x}]"}.join(",")
951
+
952
+ if args_tree.size > 1
953
+ strargs_signature = "," + strargs_signature
954
+ toprocstrargs = "self,"+strargs
955
+ strargs = "," + strargs
956
+ inprocstrargs = ","+inprocstrargs
957
+ else
958
+ toprocstrargs = "self"
959
+ end
960
+
961
+ ruby_wrapper = anonymous_function{ |funcname| "
962
+ static VALUE #{funcname}(VALUE self,void* block,void* frame#{strargs_signature}){
963
+ #{@frame_struct}* pframe = frame;
964
+
965
+ VALUE method_arguments[#{args_tree.size}] = {#{toprocstrargs}};
966
+
967
+ return #{
968
+ protected_block "rb_funcall(((VALUE*)method_arguments)[0], #{intern_num mname.to_sym}, #{args_tree.size-1}#{inprocstrargs});", false, "method_arguments"
969
+ };
970
+ }
971
+ "
972
+ }
973
+
974
+ value_cast = ( ["VALUE"]*(args_tree.size) ).join(",")
975
+
976
+ cruby_wrapper = anonymous_function{ |funcname| "
977
+ static VALUE #{funcname}(VALUE self,void* block,void* frame#{strargs_signature}){
978
+ #{@frame_struct}* pframe = frame;
979
+
980
+ VALUE method_arguments[#{args_tree.size}] = {#{toprocstrargs}};
981
+
982
+ // call to #{recvtype}::#{mname}
983
+
984
+ if (#{cruby_len} == -1) {
985
+ return #{
986
+ protected_block "((VALUE(*)(int,VALUE*,VALUE))#{cruby_name})(#{args_tree.size-1}, ((VALUE*)method_arguments)+1,*((VALUE*)method_arguments));", false, "method_arguments"
987
+ };
988
+
989
+ } else if (#{cruby_len} == -2) {
990
+ return #{
991
+ protected_block "((VALUE(*)(VALUE,VALUE))#{cruby_name})(*((VALUE*)method_arguments), rb_ary_new4(#{args_tree.size-1},((VALUE*)method_arguments)+1) );", false, "method_arguments"
992
+ };
993
+
994
+ } else {
995
+ return #{
996
+ protected_block "((VALUE(*)(#{value_cast}))#{cruby_name})(((VALUE*)method_arguments)[0] #{inprocstrargs});", false, "method_arguments"
997
+ };
998
+ }
999
+ }
1000
+ "
1001
+ }
1002
+
1003
+ recvdump = nil
1004
+
1005
+ begin
1006
+ recvdump = literal_value recvtype
1007
+ rescue
1008
+ end
1009
+
1010
+ if recvdump and recvtype
1011
+ init_extra << "
1012
+ {
1013
+ VALUE recvtype = #{recvdump};
1014
+ rb_funcall(#{literal_value FastRuby}, #{intern_num :set_builder_module}, 1, recvtype);
1015
+ VALUE signature = #{literal_value signature};
1016
+ VALUE mname = #{literal_value mname};
1017
+ VALUE tree = #{literal_value method_tree};
1018
+ VALUE convention = rb_funcall(recvtype, #{intern_num :convention}, 3,signature,mname,#{inference_complete ? "Qtrue" : "Qfalse"});
1019
+ VALUE mobject = rb_funcall(recvtype, #{intern_num :method_from_signature},3,signature,mname,#{inference_complete ? "Qtrue" : "Qfalse"});
1020
+
1021
+ struct METHOD {
1022
+ VALUE klass, rklass;
1023
+ VALUE recv;
1024
+ ID id, oid;
1025
+ int safe_level;
1026
+ NODE *body;
1027
+ };
1028
+
1029
+ int len = 0;
1030
+ void* address = 0;
1031
+
1032
+ if (mobject != Qnil) {
1033
+
1034
+ struct METHOD *data;
1035
+ Data_Get_Struct(mobject, struct METHOD, data);
1036
+
1037
+ if (nd_type(data->body) == NODE_CFUNC) {
1038
+ address = data->body->nd_cfnc;
1039
+ len = data->body->nd_argc;
1040
+ }
1041
+ }
1042
+
1043
+ if (address==0) convention = #{literal_value :ruby};
1044
+
1045
+ #{convention_global_name ? convention_global_name + " = 0;" : ""}
1046
+ if (recvtype != Qnil) {
1047
+
1048
+ if (convention == #{literal_value :fastruby}) {
1049
+ #{convention_global_name ? convention_global_name + " = 1;" : ""}
1050
+ #{name} = address;
1051
+ } else if (convention == #{literal_value :fastruby_array}) {
1052
+ // ruby, wrap rb_funcall
1053
+ #{name} = (void*)#{ruby_wrapper};
1054
+ } else if (convention == #{literal_value :cruby}) {
1055
+ // cruby, wrap direct call
1056
+ #{cruby_name} = address;
1057
+
1058
+ if (#{cruby_name} == 0) {
1059
+ #{name} = (void*)#{ruby_wrapper};
1060
+ } else {
1061
+ #{cruby_len} = len;
1062
+ #{name} = (void*)#{cruby_wrapper};
1063
+ }
1064
+ } else {
1065
+ // ruby, wrap rb_funcall
1066
+ #{name} = (void*)#{ruby_wrapper};
1067
+ }
1068
+ } else {
1069
+ // ruby, wrap rb_funcall
1070
+ #{name} = (void*)#{ruby_wrapper};
1071
+ }
1072
+
1073
+ }
1074
+ "
1075
+ else
1076
+ init_extra << "
1077
+ // ruby, wrap rb_funcall
1078
+ #{name} = (void*)#{ruby_wrapper};
1079
+ "
1080
+ end
1081
+
1082
+ name
1083
+ end
1084
+
1085
+ def intern_num(symbol)
1086
+ @intern_num_hash = Hash.new unless @intern_num_hash
1087
+ return @intern_num_hash[symbol] if @intern_num_hash[symbol]
1088
+
1089
+ name = self.add_global_name("ID", 0);
1090
+
1091
+ init_extra << "
1092
+ #{name} = rb_intern(\"#{symbol.to_s}\");
1093
+ "
1094
+
1095
+ @intern_num_hash[symbol] = name
1096
+
1097
+ name
1098
+ end
1099
+
1100
+ def add_global_name(ctype, default)
1101
+ name = "glb" + rand(1000000000).to_s
1102
+
1103
+ extra_code << "
1104
+ static #{ctype} #{name} = #{default};
1105
+ "
1106
+ name
1107
+ end
1108
+
1109
+ def global_entry(glbname)
1110
+ name = add_global_name("struct global_entry*", 0);
1111
+
1112
+ init_extra << "
1113
+ #{name} = rb_global_entry(SYM2ID(#{literal_value glbname}));
1114
+ "
1115
+
1116
+ name
1117
+ end
1118
+
1119
+
1120
+ def frame(code, jmp_code, not_jmp_code = "", rescued = nil)
1121
+
1122
+ anonymous_function{ |name| "
1123
+ static VALUE #{name}(VALUE param) {
1124
+ VALUE last_expression;
1125
+ #{@frame_struct} frame, *pframe, *parent_frame;
1126
+ #{@locals_struct} *plocals;
1127
+
1128
+ parent_frame = (void*)param;
1129
+
1130
+ frame.parent_frame = (void*)param;
1131
+ frame.plocals = parent_frame->plocals;
1132
+ frame.rescue = #{rescued ? rescued : "parent_frame->rescue"};
1133
+ frame.targetted = 0;
1134
+ frame.thread_data = parent_frame->thread_data;
1135
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
1136
+
1137
+ plocals = frame.plocals;
1138
+ pframe = &frame;
1139
+
1140
+ int aux = setjmp(frame.jmp);
1141
+ if (aux != 0) {
1142
+ last_expression = pframe->return_value;
1143
+
1144
+ // restore previous frame
1145
+ typeof(pframe) original_frame = pframe;
1146
+ pframe = parent_frame;
1147
+
1148
+ #{jmp_code};
1149
+
1150
+ if (original_frame->targetted == 0) {
1151
+ longjmp(pframe->jmp,aux);
1152
+ }
1153
+
1154
+ return last_expression;
1155
+ }
1156
+
1157
+ #{code};
1158
+
1159
+ // restore previous frame
1160
+ typeof(pframe) original_frame = pframe;
1161
+ pframe = parent_frame;
1162
+ #{not_jmp_code};
1163
+
1164
+ return last_expression;
1165
+
1166
+ }
1167
+ "
1168
+ } + "((VALUE)pframe)"
1169
+ end
1170
+ end
1171
+ end