fastruby 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -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