fastruby 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Rakefile +1 -1
  2. data/benchmarks/benchmark.rb +3 -0
  3. data/benchmarks/benchmark.rb~ +3 -12
  4. data/benchmarks/benchmark8.rb +48 -0
  5. data/benchmarks/benchmark8.rb~ +46 -0
  6. data/lib/fastruby.rb +2 -1
  7. data/lib/fastruby.rb~ +2 -1
  8. data/lib/fastruby/builder.rb +18 -1
  9. data/lib/fastruby/builder.rb~ +18 -5
  10. data/lib/fastruby/modules/lvar_type/lasgn.rb~ +41 -0
  11. data/lib/fastruby/modules/translator/block.rb +1 -1
  12. data/lib/fastruby/modules/translator/block.rb~ +128 -0
  13. data/lib/fastruby/modules/translator/call.rb +62 -139
  14. data/lib/fastruby/modules/translator/call.rb~ +61 -140
  15. data/lib/fastruby/modules/translator/defn.rb +49 -105
  16. data/lib/fastruby/modules/translator/defn.rb~ +211 -0
  17. data/lib/fastruby/modules/translator/exceptions.rb +1 -0
  18. data/lib/fastruby/modules/translator/exceptions.rb~ +120 -0
  19. data/lib/fastruby/modules/translator/iter.rb +13 -20
  20. data/lib/fastruby/modules/translator/iter.rb~ +738 -0
  21. data/lib/fastruby/modules/translator/literal.rb +8 -1
  22. data/lib/fastruby/modules/translator/literal.rb~ +157 -0
  23. data/lib/fastruby/modules/translator/nonlocal.rb +7 -0
  24. data/lib/fastruby/modules/translator/nonlocal.rb~ +304 -0
  25. data/lib/fastruby/modules/translator/static.rb +1 -0
  26. data/lib/fastruby/modules/translator/static.rb~ +290 -0
  27. data/lib/fastruby/modules/translator/variable.rb +24 -6
  28. data/lib/fastruby/modules/translator/variable.rb~ +298 -0
  29. data/lib/fastruby/translator/translator.rb +411 -284
  30. data/lib/fastruby/translator/translator.rb~ +1728 -0
  31. data/spec/fastruby_only/base_spec.rb~ +74 -0
  32. data/spec/ruby/base_spec.rb~ +1 -338
  33. data/spec/ruby/block/break_spec.rb~ +21 -0
  34. data/spec/ruby/block/callcc_spec.rb~ +236 -0
  35. data/spec/ruby/block/lambda_spec.rb~ +1 -178
  36. data/spec/ruby/block/next_spec.rb~ +85 -0
  37. data/spec/ruby/block/proc_spec.rb~ +22 -0
  38. data/spec/ruby/block/redo_spec.rb~ +133 -0
  39. data/spec/ruby/block/retry_spec.rb~ +135 -0
  40. data/spec/ruby/block_spec.rb~ +494 -2
  41. data/spec/ruby/call/base_call_spec.rb~ +60 -2
  42. data/spec/ruby/defn/default_args_spec.rb~ +303 -0
  43. data/spec/ruby/defn/multiple_args_spec.rb~ +317 -0
  44. data/spec/ruby/defn/replacement_spec.rb +29 -1
  45. data/spec/ruby/defn/replacement_spec.rb~ +52 -21
  46. data/spec/ruby/exception/internal_ex_spec.rb~ +2 -2
  47. data/spec/ruby/variable_spec.rb~ +46 -23
  48. data/spec/static/flow_spec.rb~ +48 -0
  49. metadata +34 -12
@@ -0,0 +1,1728 @@
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
+ require "fastruby/translator/scope_mode_helper"
29
+ require "fastruby/modules"
30
+ require "define_method_handler"
31
+ require "base64"
32
+
33
+ module FastRuby
34
+ class Context
35
+ attr_accessor :locals
36
+ attr_accessor :options
37
+ attr_reader :no_cache
38
+ attr_reader :init_extra
39
+ attr_reader :extra_code
40
+
41
+ class Value
42
+ attr_accessor :value
43
+ def initialize(v); @value = v; end
44
+ end
45
+
46
+ def self.define_translator_for(ntype, options = {}, &blk)
47
+ condition_blk = proc do |*x|
48
+ tree = x.first; tree.node_type == ntype
49
+ end
50
+
51
+ if options[:arity]
52
+ if options[:arity] == 1
53
+ condition_blk = proc do |*x|
54
+ tree = x.first; x.size == 1 and tree.node_type == ntype
55
+ end
56
+ end
57
+ end
58
+
59
+ define_method_handler(:to_c, options, &blk).condition &condition_blk
60
+ end
61
+
62
+ define_method_handler(:to_c, :priority => 10000){|*x|
63
+ "Qnil"
64
+ }.condition{|*x| x.size == 1 and (not x.first)}
65
+
66
+ define_method_handler(:to_c, :priority => 1000){|tree, result_var|
67
+ "#{result_var} = #{to_c(tree)};"
68
+ }.condition{|*x| x.size == 2 and (not x.first)}
69
+
70
+ define_method_handler(:to_c, :priority => -9000){ |tree, result_var|
71
+ "#{result_var} = #{to_c(tree)};"
72
+ }.condition{|*x| x.size == 2 }
73
+
74
+ define_method_handler(:to_c, :priority => -10000) do |*x|
75
+ tree, result_var = x
76
+
77
+ raise "undefined translator for node type :#{tree.node_type}"
78
+ end
79
+
80
+ define_method_handler(:initialize_to_c){|*x|}.condition{|*x|false}
81
+
82
+ define_translator_for(:call, :priority => 100){ |*x|
83
+ tree, result_var = x
84
+
85
+ tree[2] = :fastruby_require
86
+ to_c(tree)
87
+ }.condition{|*x|
88
+ tree = x.first; tree.node_type == :call && tree[2] == :require
89
+ }
90
+
91
+ define_method_handler(:infer_value, :priority => -1000) do |tree|
92
+ nil
93
+ end
94
+
95
+ FastRuby::Modules.load_all("translator")
96
+
97
+ def catch_block(*catchs)
98
+ old_catch_blocks = @catch_blocks.dup
99
+ begin
100
+ catchs.each &@catch_blocks.method(:<<)
101
+ return yield
102
+ ensure
103
+ @catch_blocks = old_catch_blocks
104
+ end
105
+ end
106
+
107
+ def initialize(common_func = true, inferencer = nil)
108
+ initialize_to_c
109
+
110
+ @inferencer = inferencer
111
+ @catch_blocks = []
112
+ @no_cache = false
113
+ @extra_code = ""
114
+ @options = {}
115
+ @init_extra = Array.new
116
+ @frame_struct = "struct {
117
+ void* parent_frame;
118
+ void* plocals;
119
+ jmp_buf jmp;
120
+ VALUE return_value;
121
+ int rescue;
122
+ VALUE last_error;
123
+ VALUE next_recv;
124
+ int targetted;
125
+ struct FASTRUBYTHREADDATA* thread_data;
126
+ }"
127
+
128
+ @block_struct = "struct {
129
+ void* block_function_address;
130
+ void* block_function_param;
131
+ VALUE proc;
132
+ }"
133
+
134
+ extra_code << "
135
+ static void frb_jump_tag(int state) {
136
+ VALUE exception = rb_funcall(#{literal_value FastRuby::JumpTagException}, #{intern_num :new},1,INT2FIX(state));
137
+ rb_exc_raise(exception);
138
+ }
139
+ "
140
+
141
+ extra_code << "
142
+ #define FASTRUBY_TAG_RETURN 0x80
143
+ #define FASTRUBY_TAG_NEXT 0x81
144
+ #define FASTRUBY_TAG_BREAK 0x82
145
+ #define FASTRUBY_TAG_RAISE 0x83
146
+ #define FASTRUBY_TAG_REDO 0x84
147
+ #define FASTRUBY_TAG_RETRY 0x85
148
+ #define TAG_RAISE 0x6
149
+
150
+ # define PTR2NUM(x) (ULONG2NUM((unsigned long)(x)))
151
+ # define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
152
+
153
+ #ifndef __INLINE_FASTRUBY_BASE
154
+ #include \"#{FastRuby.fastruby_load_path}/../ext/fastruby_base/fastruby_base.inl\"
155
+ #define __INLINE_FASTRUBY_BASE
156
+ #endif
157
+ "
158
+
159
+ ruby_code = "
160
+ unless $LOAD_PATH.include? #{FastRuby.fastruby_load_path.inspect}
161
+ $LOAD_PATH << #{FastRuby.fastruby_load_path.inspect}
162
+ end
163
+ require #{FastRuby.fastruby_script_path.inspect}
164
+ "
165
+
166
+ init_extra << "
167
+ rb_eval_string(#{ruby_code.inspect});
168
+ "
169
+
170
+
171
+ if RUBY_VERSION =~ /^1\.8/
172
+
173
+ @lambda_node_gvar = add_global_name("NODE*", 0);
174
+ @proc_node_gvar = add_global_name("NODE*", 0);
175
+ @procnew_node_gvar = add_global_name("NODE*", 0);
176
+ @callcc_node_gvar = add_global_name("NODE*", 0);
177
+
178
+ init_extra << "
179
+ #{@lambda_node_gvar} = rb_method_node(rb_cObject, #{intern_num :lambda});
180
+ #{@proc_node_gvar} = rb_method_node(rb_cObject, #{intern_num :proc});
181
+ #{@procnew_node_gvar} = rb_method_node(CLASS_OF(rb_cProc), #{intern_num :new});
182
+ #{@callcc_node_gvar} = rb_method_node(rb_mKernel, #{intern_num :callcc});
183
+ "
184
+ elsif RUBY_VERSION =~ /^1\.9/
185
+
186
+ @lambda_node_gvar = add_global_name("void*", 0);
187
+ @proc_node_gvar = add_global_name("void*", 0);
188
+ @procnew_node_gvar = add_global_name("void*", 0);
189
+ @callcc_node_gvar = add_global_name("void*", 0);
190
+
191
+ init_extra << "
192
+ #{@lambda_node_gvar} = rb_method_entry(rb_cObject, #{intern_num :lambda});
193
+ #{@proc_node_gvar} = rb_method_entry(rb_cObject, #{intern_num :proc});
194
+ #{@procnew_node_gvar} = rb_method_entry(CLASS_OF(rb_const_get(rb_cObject, #{intern_num :Proc})), #{intern_num :new});
195
+ #{@callcc_node_gvar} = rb_method_entry(rb_mKernel, #{intern_num :callcc});
196
+ "
197
+ end
198
+
199
+ @common_func = common_func
200
+ if common_func
201
+ extra_code << "static VALUE _rb_gvar_set(void* ge,VALUE value) {
202
+ rb_gvar_set((struct global_entry*)ge,value);
203
+ return value;
204
+ }
205
+ "
206
+
207
+ extra_code << "static VALUE re_yield(int argc, VALUE* argv, VALUE param, VALUE _parent_frame) {
208
+ VALUE yield_args = rb_ary_new4(argc,argv);
209
+ VALUE* yield_args_p = &yield_args;
210
+
211
+ #{@frame_struct}* pframe;
212
+ pframe = (typeof(pframe))_parent_frame;
213
+
214
+ return #{protected_block("last_expression = rb_yield_splat(*(VALUE*)yield_args_p)",true,"yield_args_p",true)};
215
+ }"
216
+
217
+ extra_code << "static VALUE _rb_ivar_set(VALUE recv,ID idvar, VALUE value) {
218
+ rb_ivar_set(recv,idvar,value);
219
+ return value;
220
+ }
221
+ "
222
+
223
+ extra_code << "static VALUE __rb_cvar_set(VALUE recv,ID idvar, VALUE value, int warn) {
224
+ #{if RUBY_VERSION =~ /^1\.9/
225
+ "rb_cvar_set(recv,idvar,value);"
226
+ elsif RUBY_VERSION =~ /^1\.8/
227
+ "rb_cvar_set(recv,idvar,value,warn);"
228
+ else
229
+ raise RuntimeError, "unsupported ruby version #{RUBY_VERSION}"
230
+ end
231
+ }
232
+
233
+ return value;
234
+ }
235
+ "
236
+
237
+ extra_code << "static VALUE _lvar_assing(VALUE* destination,VALUE value) {
238
+ *destination = value;
239
+ return value;
240
+ }
241
+
242
+ /*
243
+ #{caller.join("\n")}
244
+ */
245
+
246
+ "
247
+
248
+ end
249
+ end
250
+
251
+
252
+ def _raise(class_tree, message_tree = nil)
253
+ @has_raise = true
254
+ @has_inline_block = true
255
+ class_tree = to_c class_tree unless class_tree.instance_of? String
256
+
257
+ if message_tree.instance_of? String
258
+ message_tree = "rb_str_new2(#{message_tree.inspect})"
259
+ else
260
+ message_tree = to_c message_tree
261
+ end
262
+
263
+ if message_tree
264
+ return inline_block("
265
+ pframe->thread_data->exception = rb_funcall(#{class_tree}, #{intern_num :exception},1,#{message_tree});
266
+ longjmp(pframe->jmp, FASTRUBY_TAG_RAISE);
267
+ return Qnil;
268
+ ")
269
+ else
270
+ return inline_block("
271
+ pframe->thread_data->exception = rb_funcall(#{class_tree}, #{intern_num :exception},0);
272
+ longjmp(pframe->jmp, FASTRUBY_TAG_RAISE);
273
+ return Qnil;
274
+ ")
275
+ end
276
+
277
+ end
278
+
279
+ def anonymous_function(*x)
280
+
281
+ name = "anonymous" + rand(10000000).to_s
282
+ extra_code << yield(name,*x)
283
+
284
+ name
285
+ end
286
+
287
+ def frame_call(inner_code, precode = "", postcode = "")
288
+ inline_block "
289
+
290
+ volatile VALUE ret = Qnil;
291
+ // create a call_frame
292
+ #{@frame_struct} call_frame;
293
+ typeof(call_frame)* volatile old_pframe = (void*)pframe;
294
+
295
+ pframe = (typeof(pframe))&call_frame;
296
+
297
+ call_frame.parent_frame = (void*)pframe;
298
+ call_frame.plocals = plocals;
299
+ call_frame.return_value = Qnil;
300
+ call_frame.targetted = 0;
301
+ call_frame.thread_data = old_pframe->thread_data;
302
+ if (call_frame.thread_data == 0) call_frame.thread_data = rb_current_thread_data();
303
+
304
+ void* volatile old_call_frame = plocals->call_frame;
305
+ plocals->call_frame = &call_frame;
306
+
307
+ #{precode}
308
+
309
+ int aux = setjmp(call_frame.jmp);
310
+ if (aux != 0) {
311
+ if (call_frame.targetted == 0) {
312
+ #{postcode}
313
+ longjmp(old_pframe->jmp,aux);
314
+ }
315
+
316
+ if (aux == FASTRUBY_TAG_BREAK) {
317
+ plocals->call_frame = old_call_frame;
318
+ #{postcode}
319
+ return call_frame.return_value;
320
+ } else if (aux == FASTRUBY_TAG_RETRY ) {
321
+ // do nothing and let the call execute again
322
+ } else {
323
+ plocals->call_frame = old_call_frame;
324
+ #{postcode}
325
+ return call_frame.return_value;
326
+ }
327
+ }
328
+
329
+
330
+ #{inner_code};
331
+
332
+ #{postcode}
333
+
334
+ plocals->call_frame = old_call_frame;
335
+ return ret;
336
+ "
337
+ end
338
+
339
+ def initialize_method_structs(args_tree)
340
+ @locals_struct = "struct {
341
+ int size;
342
+ void* call_frame;
343
+ void* parent_locals;
344
+ void* pframe;
345
+ void* block_function_address;
346
+ void* block_function_param;
347
+ VALUE active;
348
+ VALUE targetted;
349
+ VALUE return_value;
350
+ #{@locals.map{|l| "VALUE #{l};\n"}.join}
351
+ }"
352
+
353
+ end
354
+
355
+ def add_main
356
+ if options[:main]
357
+
358
+ extra_code << "
359
+ static VALUE #{@alt_method_name}(VALUE self__);
360
+ static VALUE main_proc_call(VALUE self__, VALUE signature, VALUE class_self_) {
361
+ #{@alt_method_name}(class_self_);
362
+ return Qnil;
363
+ }
364
+
365
+ "
366
+
367
+ init_extra << "
368
+ {
369
+ VALUE newproc = rb_funcall(rb_cObject,#{intern_num :new},0);
370
+ rb_define_singleton_method(newproc, \"call\", main_proc_call, 2);
371
+ rb_gv_set(\"$last_obj_proc\", newproc);
372
+ }
373
+ "
374
+ end
375
+ end
376
+
377
+ def define_method_at_init(method_name, size, signature)
378
+ extra_code << "
379
+ static VALUE main_proc_call(VALUE self__, VALUE signature, VALUE class_self_) {
380
+ VALUE method_name = rb_funcall(
381
+ #{literal_value FastRuby},
382
+ #{intern_num :make_str_signature},
383
+ 2,
384
+ #{literal_value method_name},
385
+ signature
386
+ );
387
+
388
+ ID id = rb_intern(RSTRING_PTR(method_name));
389
+
390
+ rb_funcall(
391
+ #{literal_value FastRuby},
392
+ #{intern_num :set_builder_module},
393
+ 1,
394
+ class_self_
395
+ );
396
+
397
+ VALUE rb_method_hash;
398
+ void** address = 0;
399
+ rb_method_hash = rb_funcall(class_self_, #{intern_num :method_hash},1,#{literal_value method_name});
400
+
401
+ if (rb_method_hash != Qnil) {
402
+ VALUE tmp = rb_hash_aref(rb_method_hash, PTR2NUM(id));
403
+ if (tmp != Qnil) {
404
+ address = (void*)NUM2PTR(tmp);
405
+ }
406
+ }
407
+
408
+ if (address == 0) {
409
+ address = malloc(sizeof(void*));
410
+ }
411
+ *address = #{@alt_method_name};
412
+
413
+ rb_funcall(
414
+ class_self_,
415
+ #{intern_num :register_method_value},
416
+ 3,
417
+ #{literal_value method_name},
418
+ PTR2NUM(id),
419
+ PTR2NUM(address)
420
+ );
421
+
422
+ return Qnil;
423
+ }
424
+ "
425
+
426
+ init_extra << "
427
+ {
428
+ VALUE newproc = rb_funcall(rb_cObject,#{intern_num :new},0);
429
+ rb_define_singleton_method(newproc, \"call\", main_proc_call, 2);
430
+ rb_gv_set(\"$last_obj_proc\", newproc);
431
+ }
432
+ "
433
+ end
434
+
435
+ def to_c_method(tree, signature = nil)
436
+
437
+ if tree[0] == :defn
438
+ method_name = tree[1]
439
+ original_args_tree = tree[2]
440
+ block_argument = tree[2].find{|x| x.to_s[0] == ?&}
441
+ impl_tree = tree[3][1]
442
+ elsif tree[0] == :defs
443
+ method_name = tree[2]
444
+ original_args_tree = tree[3]
445
+ block_argument = tree[3].find{|x| x.to_s[0] == ?&}
446
+ impl_tree = tree[4][1]
447
+ end
448
+
449
+ @method_arguments = original_args_tree[1..-1]
450
+
451
+ if "0".respond_to?(:ord)
452
+ @alt_method_name = "_" + method_name.to_s.gsub("_x_", "_x__x_").gsub(/\W/){|x| "_x_#{x.ord}" } + "_" + rand(10000000000).to_s
453
+ else
454
+ @alt_method_name = "_" + method_name.to_s.gsub("_x_", "_x__x_").gsub(/\W/){|x| "_x_#{x[0]}" } + "_" + rand(10000000000).to_s
455
+ end
456
+
457
+ @has_yield = false
458
+ @has_dynamic_call = false
459
+ @has_nonlocal_goto = false
460
+ @has_inline_block = (options[:main] or tree.find_tree(:lasgn)
461
+ @has_plocals_ref = false
462
+ @has_raise = false
463
+ @has_inline_c = false
464
+
465
+ args_tree = original_args_tree.select{|x| x.to_s[0] != ?&}
466
+
467
+ initialize_method_structs(original_args_tree)
468
+
469
+ if options[:main] then
470
+ strargs = if args_tree.size > 1
471
+ "VALUE self, VALUE block, VALUE _parent_frame, #{(0..signature.size-1).map{|x| "VALUE arg#{x}"}.join(",")}"
472
+ else
473
+ "VALUE self, VALUE block, VALUE _parent_frame"
474
+ end
475
+
476
+ else
477
+
478
+ strargs = "VALUE self, VALUE block, VALUE _parent_frame, int argc, VALUE* argv"
479
+
480
+ splat_arg = args_tree[1..-1].find{|x| x.to_s.match(/\*/) }
481
+
482
+ maxargnum = args_tree[1..-1].count{ |x|
483
+ if x.instance_of? Symbol
484
+ not x.to_s.match(/\*/) and not x.to_s.match(/\&/)
485
+ else
486
+ false
487
+ end
488
+ }
489
+
490
+ minargnum = maxargnum
491
+
492
+ args_tree[1..-1].each do |subtree|
493
+ unless subtree.instance_of? Symbol
494
+ if subtree[0] == :block
495
+ minargnum = minargnum - (subtree.size-1)
496
+ end
497
+ end
498
+ end
499
+
500
+ if args_tree[1..-1].find{|x| x.to_s.match(/\*/)}
501
+ maxargnum = 2147483647
502
+ end
503
+
504
+ read_arguments_code = ""
505
+
506
+
507
+ validate_arguments_code = if signature.size-1 < minargnum
508
+ "
509
+ rb_raise(rb_eArgError, \"wrong number of arguments (#{signature.size-1} for #{minargnum})\");
510
+ "
511
+ elsif signature.size-1 > maxargnum
512
+ "
513
+ rb_raise(rb_eArgError, \"wrong number of arguments (#{signature.size-1} for #{maxargnum})\");
514
+ "
515
+ else
516
+
517
+ default_block_tree = args_tree[1..-1].find{|subtree|
518
+ unless subtree.instance_of? Symbol
519
+ if subtree[0] == :block
520
+ next true
521
+ end
522
+ end
523
+
524
+ false
525
+ }
526
+
527
+ i = -1
528
+
529
+ normalargsnum = args_tree[1..-1].count{|subtree|
530
+ if subtree.instance_of? Symbol
531
+ unless subtree.to_s.match(/\*/) or subtree.to_s.match(/\&/)
532
+ next true
533
+ end
534
+ end
535
+
536
+ false
537
+ }
538
+
539
+ read_arguments_code = args_tree[1..-1].map { |arg_|
540
+ arg = arg_.to_s
541
+ i = i + 1
542
+
543
+ if i < normalargsnum
544
+ if i < signature.size-1
545
+ "plocals->#{arg} = argv[#{i}];\n"
546
+ else
547
+
548
+ if default_block_tree
549
+ @has_inline_block = true
550
+ initialize_tree = default_block_tree[1..-1].find{|subtree| subtree[1] == arg_}
551
+ if initialize_tree
552
+ to_c(initialize_tree) + ";\n"
553
+ else
554
+ ""
555
+ end
556
+ else
557
+ ";\n"
558
+ end
559
+ end
560
+ else
561
+ ""
562
+ end
563
+ }.join("")
564
+
565
+ if splat_arg
566
+ @has_splat_args = true
567
+ if signature.size-1 < normalargsnum then
568
+ read_arguments_code << "
569
+ plocals->#{splat_arg.to_s.gsub("*","")} = rb_ary_new3(0);
570
+ "
571
+ else
572
+ read_arguments_code << "
573
+ plocals->#{splat_arg.to_s.gsub("*","")} = rb_ary_new4(
574
+ #{(signature.size-1) - (normalargsnum)}, argv+#{normalargsnum}
575
+ );
576
+ "
577
+ end
578
+
579
+ end
580
+
581
+
582
+ ""
583
+ end
584
+
585
+ if block_argument
586
+
587
+ @has_yield = true
588
+
589
+ proc_reyield_block_tree = s(:iter, s(:call, nil, :proc, s(:arglist)), s(:masgn, s(:array, s(:splat, s(:lasgn, :__xproc_arguments)))), s(:yield, s(:splat, s(:lvar, :__xproc_arguments))))
590
+
591
+ require "fastruby/sexp_extension"
592
+
593
+ read_arguments_code << "
594
+ if (pblock ? pblock->proc != Qnil : 0) {
595
+ plocals->#{block_argument.to_s.gsub("&","")} = pblock->proc;
596
+ } else {
597
+ plocals->#{block_argument.to_s.gsub("&","")} = #{to_c FastRuby::FastRubySexp.from_sexp(proc_reyield_block_tree)};
598
+ }
599
+ "
600
+
601
+ read_arguments_code << "
602
+ if (pblock) {
603
+ rb_ivar_set(plocals->#{block_argument.to_s.gsub("&","")},
604
+ #{intern_num "__block_address"}, PTR2NUM(pblock->block_function_address));
605
+ rb_ivar_set(plocals->#{block_argument.to_s.gsub("&","")},
606
+ #{intern_num "__block_param"}, PTR2NUM(pblock->block_function_param));
607
+ }
608
+
609
+ "
610
+ end
611
+
612
+ end
613
+
614
+ require "fastruby/sexp_extension"
615
+
616
+ trs = lambda{|tree|
617
+ if not tree.respond_to? :node_type
618
+ next tree
619
+ elsif tree.node_type == :call
620
+ mmname = tree[2]
621
+ next trs.call(tree[1]) || tree[1] if mmname == :infer
622
+ next fs(:nil) if mmname == :_throw or mmname == :_loop or mmname == :_raise
623
+ next fs(:nil) if mmname == :== and infer_value(tree)
624
+ elsif tree.node_type == :iter
625
+ mmname = tree[1][2]
626
+ next fs(:nil) if mmname == :_static
627
+ next fs(:block, trs.call(tree[3]) || tree[3]) if mmname == :_catch
628
+ end
629
+
630
+ tree.map &trs
631
+ }
632
+
633
+ evaluate_tree = tree.transform &trs
634
+
635
+ impl_code = to_c(impl_tree, "last_expression")
636
+
637
+ put_setjmp = (@has_dynamic_call or @has_nonlocal_goto or @has_yield or @has_raise or @has_inline_c)
638
+ put_block_init = @has_yield
639
+ if options[:main]
640
+ put_block_init = false
641
+ end
642
+
643
+ scope_mode = FastRuby::ScopeModeHelper.get_scope_mode(evaluate_tree)
644
+ if scope_mode == :dag or put_setjmp or put_block_init or @has_splat_args
645
+ put_frame = true
646
+ put_locals = true
647
+ else
648
+ put_frame = @has_inline_block
649
+ put_locals = @has_plocals_ref
650
+ end
651
+
652
+ ret = "VALUE #{@alt_method_name || method_name}(#{options[:main] ? "VALUE self" : strargs}) {
653
+ #{validate_arguments_code}
654
+
655
+ #{if put_frame
656
+ "
657
+ #{@frame_struct} frame;
658
+ #{@frame_struct} * volatile pframe;
659
+
660
+ frame.parent_frame = #{options[:main] ? "0" : "(void*)_parent_frame"};
661
+ frame.return_value = Qnil;
662
+ frame.rescue = 0;
663
+ frame.targetted = 0;
664
+ frame.thread_data = #{options[:main] ? "0" : "((typeof(pframe))_parent_frame)->thread_data"};
665
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
666
+ "
667
+ end
668
+ }
669
+
670
+ #{
671
+ if scope_mode == :dag
672
+ "
673
+ int stack_chunk_instantiated = 0;
674
+
675
+ volatile VALUE rb_previous_stack_chunk = Qnil;
676
+ VALUE rb_stack_chunk = frame.thread_data->rb_stack_chunk;
677
+ struct STACKCHUNK* volatile stack_chunk = 0;
678
+
679
+ if (rb_stack_chunk != Qnil) {
680
+ Data_Get_Struct(rb_stack_chunk,struct STACKCHUNK,stack_chunk);
681
+ }
682
+
683
+ if (stack_chunk == 0 || (stack_chunk == 0 ? 0 : stack_chunk_frozen(stack_chunk)) ) {
684
+ rb_previous_stack_chunk = rb_stack_chunk;
685
+ rb_gc_register_address(&rb_stack_chunk);
686
+ stack_chunk_instantiated = 1;
687
+
688
+ rb_stack_chunk = rb_stack_chunk_create(Qnil);
689
+ frame.thread_data->rb_stack_chunk = rb_stack_chunk;
690
+
691
+ rb_ivar_set(rb_stack_chunk, #{intern_num :_parent_stack_chunk}, rb_previous_stack_chunk);
692
+
693
+ Data_Get_Struct(rb_stack_chunk,struct STACKCHUNK,stack_chunk);
694
+ }
695
+
696
+ #{@locals_struct}* volatile plocals;
697
+
698
+ volatile int previous_stack_position = stack_chunk_get_current_position(stack_chunk);
699
+ plocals = (typeof(plocals))stack_chunk_alloc(stack_chunk ,sizeof(typeof(*plocals))/sizeof(void*));
700
+
701
+ "
702
+ else
703
+ "
704
+ #{@locals_struct} locals;
705
+ typeof(locals) * volatile plocals = &locals;
706
+ "
707
+ end
708
+ }
709
+
710
+ #{if put_locals and put_frame
711
+ "
712
+ plocals->parent_locals = (frame.thread_data->last_plocals);
713
+ void* volatile old_parent_locals = frame.thread_data->last_plocals;
714
+
715
+ #{
716
+ if scope_mode == :dag
717
+ "frame.thread_data->last_plocals = plocals;\n"
718
+ end
719
+ }
720
+
721
+ frame.plocals = plocals;
722
+ plocals->pframe = (&frame);
723
+ pframe = (void*)&frame;
724
+ "
725
+ end
726
+ }
727
+
728
+ #{if put_locals
729
+ "
730
+ plocals->active = Qtrue;
731
+ plocals->targetted = Qfalse;
732
+ plocals->call_frame = (0);
733
+ "
734
+ end
735
+ }
736
+
737
+ volatile VALUE last_expression = Qnil;
738
+
739
+ #{if put_setjmp
740
+ "
741
+
742
+ int aux = setjmp(pframe->jmp);
743
+ if (aux != 0) {
744
+ plocals->active = Qfalse;
745
+
746
+ #{
747
+ if scope_mode == :dag
748
+ "
749
+ stack_chunk_set_current_position(stack_chunk, previous_stack_position);
750
+
751
+ if (stack_chunk_instantiated) {
752
+ rb_gc_unregister_address(&rb_stack_chunk);
753
+ frame.thread_data->rb_stack_chunk = rb_previous_stack_chunk;
754
+ }
755
+ "
756
+ end
757
+ }
758
+ #{
759
+ unless options[:main]
760
+ "
761
+ if (plocals->targetted == Qfalse || aux != FASTRUBY_TAG_RETURN) {
762
+ frame.thread_data->last_plocals = old_parent_locals;
763
+
764
+ longjmp(((typeof(pframe))_parent_frame)->jmp,aux);
765
+ }
766
+ "
767
+ end
768
+ }
769
+
770
+ frame.thread_data->last_plocals = old_parent_locals;
771
+
772
+ return plocals->return_value;
773
+ }
774
+ "
775
+ end
776
+ }
777
+
778
+ #{if put_locals
779
+ "
780
+ plocals->self = self;
781
+ "
782
+ end
783
+ }
784
+
785
+ #{
786
+ if put_block_init
787
+ "
788
+ #{@block_struct} * volatile pblock;
789
+ pblock = (void*)block;
790
+ if (pblock) {
791
+ plocals->block_function_address = pblock->block_function_address;
792
+ plocals->block_function_param = pblock->block_function_param;
793
+ } else {
794
+ plocals->block_function_address = (0);
795
+ plocals->block_function_param = 0;
796
+ }
797
+ "
798
+ end
799
+ }
800
+
801
+ #{if put_locals
802
+ "
803
+ #{read_arguments_code}
804
+ "
805
+ end
806
+ }
807
+
808
+ #{impl_code};
809
+
810
+ local_return:
811
+ #{
812
+ if scope_mode == :dag
813
+ "
814
+ stack_chunk_set_current_position(stack_chunk, previous_stack_position);
815
+
816
+ if (stack_chunk_instantiated) {
817
+ rb_gc_unregister_address(&rb_stack_chunk);
818
+ frame.thread_data->rb_stack_chunk = rb_previous_stack_chunk;
819
+ }
820
+ "
821
+ end
822
+ }
823
+
824
+ #{if put_locals
825
+ "
826
+ plocals->active = Qfalse;
827
+ "
828
+ end
829
+ }
830
+
831
+ #{if put_locals and put_frame
832
+ "
833
+ frame.thread_data->last_plocals = old_parent_locals;
834
+ "
835
+ end
836
+ }
837
+
838
+ return last_expression;
839
+ }"
840
+
841
+ add_main
842
+ extra_code << ret
843
+
844
+ "
845
+ static VALUE dummy_#{@alt_method_name}_#{rand(1000000000000000000000000000000000)}(VALUE a) {
846
+ return Qnil;
847
+ }
848
+ "
849
+ end
850
+
851
+ def locals_accessor
852
+ @has_plocals_ref = true
853
+ "plocals->"
854
+ end
855
+
856
+ def locals_scope(locals)
857
+ old_locals = @locals
858
+ old_locals_struct = @locals_struct
859
+
860
+ @locals = locals
861
+ @locals_struct = "struct {
862
+ int size;
863
+ void* call_frame;
864
+ void* parent_locals;
865
+ void* pframe;
866
+ void* block_function_address;
867
+ void* block_function_param;
868
+ VALUE active;
869
+ VALUE targetted;
870
+ VALUE return_value;
871
+ #{@locals.map{|l| "VALUE #{l};\n"}.join}
872
+ }"
873
+
874
+ begin
875
+ yield
876
+ ensure
877
+ @locals = old_locals
878
+ @locals_struct = old_locals_struct
879
+ end
880
+ end
881
+
882
+ def infer_type(recv)
883
+ array = @inferencer.infer(recv).to_a
884
+
885
+ if array.size == 1
886
+ array[0]
887
+ else
888
+ nil
889
+ end
890
+ end
891
+
892
+ def on_block
893
+ old_on_block = @on_block
894
+ @on_block = true
895
+ return yield
896
+ ensure
897
+ @on_block = old_on_block
898
+ end
899
+
900
+ def directive(tree)
901
+ recv = tree[1]
902
+ mname = tree[2]
903
+ args = tree[3]
904
+
905
+ if mname == :infer
906
+ return to_c(recv)
907
+ elsif mname == :block_given?
908
+ @has_yield = true
909
+ return "plocals->block_function_address == 0 ? Qfalse : Qtrue"
910
+ elsif mname == :inline_c
911
+ @has_inline_c = true
912
+ code = args[1][1]
913
+
914
+ unless (args[2] == s(:false))
915
+ return anonymous_function{ |name| "
916
+ static VALUE #{name}(VALUE param) {
917
+ #{@frame_struct} *pframe = (void*)param;
918
+ #{@locals_struct} *plocals = (void*)pframe->plocals;
919
+ #{code};
920
+
921
+ return Qnil;
922
+ }
923
+ "
924
+ }+"((VALUE)pframe)"
925
+ else
926
+ code
927
+ end
928
+
929
+ else
930
+ nil
931
+ end
932
+ end
933
+
934
+ def inline_block_reference(arg, nolocals = false)
935
+ @has_inline_block = true
936
+
937
+ code = nil
938
+
939
+ if arg.instance_of? FastRuby::FastRubySexp
940
+ code = to_c(arg);
941
+ else
942
+ code = arg
943
+ end
944
+
945
+ anonymous_function{ |name| "
946
+ static VALUE #{name}(VALUE param) {
947
+ #{@frame_struct} *pframe = (void*)param;
948
+
949
+ #{nolocals ? "" : "#{@locals_struct} *plocals = (void*)pframe->plocals;"}
950
+ VALUE last_expression = Qnil;
951
+
952
+ #{code};
953
+ return last_expression;
954
+ }
955
+ "
956
+ }
957
+ end
958
+
959
+ def catch_on_throw
960
+ old_catch_jmp_on_throw = @catch_jmp_on_throw || false
961
+ @catch_jmp_on_throw = true
962
+ begin
963
+ ret = yield
964
+ ensure
965
+ @catch_jmp_on_throw = old_catch_jmp_on_throw
966
+ end
967
+
968
+ ret
969
+ end
970
+
971
+ def inline_block(*args)
972
+ @has_inline_block = true
973
+
974
+ unless block_given?
975
+ code = args.first
976
+ return inline_block(*args[1..-1]) {
977
+ code
978
+ }
979
+ end
980
+
981
+ repass_var = args[0]
982
+ nolocals = args[1] || false
983
+
984
+ code = catch_on_throw{ yield }
985
+
986
+ anonymous_function{ |name| "
987
+ static VALUE #{name}(VALUE param#{repass_var ? ",void* " + repass_var : "" }) {
988
+ #{@frame_struct} * volatile pframe = (void*)param;
989
+
990
+ #{nolocals ? "" : "#{@locals_struct} * volatile plocals = (void*)pframe->plocals;"}
991
+ volatile VALUE last_expression = Qnil;
992
+
993
+ #{code}
994
+ return Qnil;
995
+
996
+
997
+ #{@catch_blocks.map { |cb|
998
+ "#{cb.to_s}_end:
999
+
1000
+ plocals->return_value = last_expression;
1001
+ plocals->targetted = 1;
1002
+ longjmp(pframe->jmp, #{intern_num( cb.to_s + "_end")});
1003
+
1004
+ #{cb.to_s}_start:
1005
+
1006
+ plocals->return_value = last_expression;
1007
+ plocals->targetted = 1;
1008
+ longjmp(pframe->jmp, #{intern_num( cb.to_s + "_start")});
1009
+
1010
+ "
1011
+
1012
+ }.join("\n")
1013
+ }
1014
+ #{unless nolocals
1015
+ "
1016
+ local_return:
1017
+ plocals->return_value = last_expression;
1018
+ plocals->targetted = 1;
1019
+ longjmp(pframe->jmp, FASTRUBY_TAG_RETURN);
1020
+ return last_expression;
1021
+ "
1022
+ end
1023
+ }
1024
+ fastruby_local_redo:
1025
+ longjmp(pframe->jmp,FASTRUBY_TAG_REDO);
1026
+ return Qnil;
1027
+ fastruby_local_next:
1028
+ longjmp(pframe->jmp,FASTRUBY_TAG_NEXT);
1029
+ return Qnil;
1030
+
1031
+
1032
+ }
1033
+ "
1034
+ } + "((VALUE)pframe#{repass_var ? ", " + repass_var : "" })"
1035
+ end
1036
+
1037
+ def inline_ruby(proced, parameter)
1038
+ "rb_funcall(#{proced.__id__}, #{intern_num :call}, 1, #{parameter})"
1039
+ end
1040
+
1041
+ def protected_block(*args)
1042
+ unless block_given?
1043
+ inner_code = args.first
1044
+ return protected_block(*args[1..-1]) {
1045
+ inner_code
1046
+ }
1047
+ end
1048
+
1049
+ repass_var = args[1]
1050
+ nolocals = args[2] || false
1051
+
1052
+ inline_block(repass_var, nolocals) do
1053
+ generate_protected_block(yield, *args)
1054
+ end
1055
+ end
1056
+
1057
+ def generate_protected_block(inner_code, always_rescue = false,repass_var = nil, nolocals = false)
1058
+ body = nil
1059
+ rescue_args = nil
1060
+
1061
+ body = anonymous_function{ |name| "
1062
+ static VALUE #{name}(VALUE param) {
1063
+
1064
+ #{@frame_struct} frame;
1065
+
1066
+ typeof(frame)* volatile pframe;
1067
+
1068
+ #{if repass_var
1069
+ "typeof(frame)* parent_frame = ((typeof(pframe))((void**)param)[0]);"
1070
+ else
1071
+ "typeof(frame)* parent_frame = (typeof(pframe))param;"
1072
+ end
1073
+ }
1074
+
1075
+ frame.parent_frame = 0;
1076
+ frame.return_value = Qnil;
1077
+ frame.rescue = 0;
1078
+ frame.last_error = Qnil;
1079
+ frame.targetted = 0;
1080
+ frame.thread_data = parent_frame->thread_data;
1081
+ frame.next_recv = parent_frame->next_recv;
1082
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
1083
+
1084
+ pframe = &frame;
1085
+
1086
+ #{
1087
+ nolocals ? "frame.plocals = 0;" : "#{@locals_struct}* plocals = parent_frame->plocals;
1088
+ frame.plocals = plocals;
1089
+ "
1090
+ }
1091
+
1092
+ int aux = setjmp(frame.jmp);
1093
+ if (aux != 0) {
1094
+
1095
+ if (frame.targetted == 1) {
1096
+ return frame.return_value;
1097
+ } else {
1098
+ frb_jump_tag(aux);
1099
+ }
1100
+ }
1101
+
1102
+ #{if repass_var
1103
+ "VALUE #{repass_var} = (VALUE)((void**)param)[1];"
1104
+ end
1105
+ }
1106
+ volatile VALUE last_expression = Qnil;
1107
+ #{inner_code};
1108
+ return last_expression;
1109
+ }
1110
+ "
1111
+ }
1112
+
1113
+ if repass_var
1114
+ rescue_args = ""
1115
+ rescue_args = "(VALUE)(VALUE[]){(VALUE)pframe,(VALUE)#{repass_var}}"
1116
+ else
1117
+ rescue_args = "(VALUE)pframe"
1118
+ end
1119
+
1120
+ wrapper_code = "
1121
+ if (str.state >= 0x80) {
1122
+ longjmp(pframe->jmp, str.state);
1123
+ } else {
1124
+ if (str.last_error != Qnil) {
1125
+ // raise emulation
1126
+ pframe->thread_data->exception = str.last_error;
1127
+ longjmp(pframe->jmp, FASTRUBY_TAG_RAISE);
1128
+ return Qnil;
1129
+ }
1130
+ }
1131
+ "
1132
+
1133
+ return_err_struct = "struct {
1134
+ VALUE last_error;
1135
+ int state;
1136
+ }
1137
+ "
1138
+
1139
+ rescue_body = anonymous_function{ |name| "
1140
+ static VALUE #{name}(VALUE param, VALUE err) {
1141
+ #{return_err_struct} *pstr = (void*)param;
1142
+
1143
+ if (rb_obj_is_instance_of(err, #{literal_value FastRuby::JumpTagException})) {
1144
+ pstr->state = FIX2INT(rb_funcall(err, #{intern_num :state}, 0));
1145
+ } else {
1146
+ pstr->last_error = err;
1147
+ }
1148
+
1149
+ return Qnil;
1150
+ }
1151
+ "
1152
+ }
1153
+
1154
+ rescue_code = "rb_rescue2(#{body}, #{rescue_args}, #{rescue_body}, (VALUE)&str, rb_eException, (VALUE)0)"
1155
+
1156
+ if always_rescue
1157
+ "
1158
+ #{return_err_struct} str;
1159
+
1160
+ str.state = 0;
1161
+ str.last_error = Qnil;
1162
+
1163
+ pframe->last_error = Qnil;
1164
+ VALUE result = #{rescue_code};
1165
+
1166
+ #{wrapper_code}
1167
+
1168
+ return result;
1169
+ "
1170
+ else
1171
+ "
1172
+ VALUE result;
1173
+ #{return_err_struct} str;
1174
+
1175
+ str.state = 0;
1176
+ str.last_error = Qnil;
1177
+
1178
+ pframe->last_error = Qnil;
1179
+
1180
+ if (pframe->rescue) {
1181
+ result = #{rescue_code};
1182
+ #{wrapper_code}
1183
+ } else {
1184
+ VALUE last_expression = Qnil;
1185
+ #{inner_code};
1186
+ return last_expression;
1187
+ }
1188
+
1189
+ return result;
1190
+ "
1191
+ end
1192
+ end
1193
+
1194
+ def c_escape(str)
1195
+ str.inspect
1196
+ end
1197
+
1198
+ def literal_value(value)
1199
+ @literal_value_hash = Hash.new unless @literal_value_hash
1200
+ return @literal_value_hash[value] if @literal_value_hash[value]
1201
+
1202
+ name = self.add_global_name("VALUE", "Qnil");
1203
+
1204
+ begin
1205
+
1206
+ str = Marshal.dump(value)
1207
+
1208
+
1209
+ if value.instance_of? Module
1210
+
1211
+ container_str = value.to_s.split("::")[0..-2].join("::")
1212
+
1213
+ init_extra << "
1214
+ #{name} = rb_define_module_under(
1215
+ #{container_str == "" ? "rb_cObject" : literal_value(eval(container_str))}
1216
+ ,\"#{value.to_s.split("::").last}\");
1217
+
1218
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
1219
+ "
1220
+ elsif value.instance_of? Class
1221
+ container_str = value.to_s.split("::")[0..-2].join("::")
1222
+
1223
+ str_class_name = value.to_s.split("::").last
1224
+
1225
+ if (str_class_name == "Object")
1226
+ init_extra << "
1227
+ #{name} = rb_cObject;
1228
+ "
1229
+ else
1230
+ init_extra << "
1231
+ #{name} = rb_define_class_under(
1232
+ #{container_str == "" ? "rb_cObject" : literal_value(eval(container_str))}
1233
+ ,\"#{str_class_name}\"
1234
+ ,#{value.superclass == Object ? "rb_cObject" : literal_value(value.superclass)});
1235
+
1236
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
1237
+ "
1238
+ end
1239
+ elsif value.instance_of? Array
1240
+ init_extra << "
1241
+ #{name} = rb_ary_new3(#{value.size}, #{value.map{|x| literal_value x}.join(",")} );
1242
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
1243
+ "
1244
+ else
1245
+
1246
+ init_extra << "
1247
+
1248
+ {
1249
+ VALUE encoded_str = rb_str_new2(#{Base64.encode64(str).inspect});
1250
+ VALUE str = rb_funcall(rb_cObject, #{intern_num :decode64}, 1, encoded_str);
1251
+ #{name} = rb_marshal_load(str);
1252
+
1253
+ rb_funcall(#{name},#{intern_num :gc_register_object},0);
1254
+ }
1255
+
1256
+ "
1257
+ end
1258
+ rescue TypeError => e
1259
+ @no_cache = true
1260
+ FastRuby.logger.info "#{value} disabling cache for extension"
1261
+ init_extra << "
1262
+ #{name} = rb_funcall(rb_const_get(rb_cObject, #{intern_num :ObjectSpace}), #{intern_num :_id2ref}, 1, INT2FIX(#{value.__id__}));
1263
+ "
1264
+
1265
+ end
1266
+ @literal_value_hash[value] = name
1267
+
1268
+ name
1269
+ end
1270
+
1271
+ def dynamic_block_call(signature, mname)
1272
+ dynamic_call(signature, mname, true)
1273
+ end
1274
+
1275
+ # returns a anonymous function who made a dynamic call
1276
+ def dynamic_call(signature, mname, return_on_block_call = false, funcall_fallback = true, global_klass_variable = nil)
1277
+ # TODO: initialize the table
1278
+ @has_dynamic_call = true
1279
+ max_argument_size = 0
1280
+ recvtype = signature.first
1281
+
1282
+ unless recvtype
1283
+ max_argument_size = max_argument_size + 1
1284
+ end
1285
+
1286
+ compare_hash = {}
1287
+ (1..signature.size-1).each do |j|
1288
+ unless signature[j]
1289
+ compare_hash[max_argument_size] = j-1
1290
+ max_argument_size = max_argument_size + 1
1291
+ end
1292
+ end
1293
+
1294
+ table_size = if compare_hash.size == 0
1295
+ 1
1296
+ elsif compare_hash.size == 1
1297
+ 16
1298
+ else
1299
+ 64
1300
+ end
1301
+
1302
+ table_name = reserve_table(table_size, max_argument_size)
1303
+
1304
+ if recvtype
1305
+
1306
+ init_extra << "{
1307
+ memset(#{table_name},0,sizeof(#{table_name}));
1308
+
1309
+ VALUE mname = #{literal_value mname};
1310
+ VALUE recvtype = #{literal_value recvtype};
1311
+ rb_funcall(#{literal_value FastRuby}, #{intern_num :set_builder_module}, 1, recvtype);
1312
+ VALUE fastruby_method = rb_funcall(recvtype, #{intern_num :fastruby_method}, 1, mname);
1313
+ rb_iterate(#{anonymous_function{|funcname|
1314
+ "static VALUE #{funcname}(VALUE recv) {
1315
+ return rb_funcall(recv, #{intern_num :observe}, 1, #{literal_value(mname.to_s + "#" + table_name.to_s)});
1316
+ }
1317
+ "
1318
+ }},fastruby_method,
1319
+ #{anonymous_function{|funcname|
1320
+ "static VALUE #{funcname}() {
1321
+ // clear table
1322
+ memset(#{table_name},0,sizeof(#{table_name}));
1323
+ return Qnil;
1324
+ }
1325
+ "
1326
+ }
1327
+ }
1328
+ ,Qnil);
1329
+ }
1330
+ "
1331
+ else
1332
+
1333
+ # TODO: implemente this in ruby
1334
+ init_extra << "
1335
+ {
1336
+ memset(#{table_name},0,sizeof(#{table_name}));
1337
+
1338
+ rb_iterate(#{anonymous_function{|funcname|
1339
+ "static VALUE #{funcname}(VALUE recv) {
1340
+ return rb_funcall(recv, #{intern_num :observe_method_name}, 1, #{literal_value(mname.to_sym)});
1341
+ }
1342
+ "
1343
+ }},#{literal_value FastRuby::Method},
1344
+ #{anonymous_function{|funcname|
1345
+ "static VALUE #{funcname}() {
1346
+ // clear table
1347
+ memset(#{table_name},0,sizeof(#{table_name}));
1348
+ return Qnil;
1349
+ }
1350
+ "
1351
+ }
1352
+ }
1353
+ ,Qnil);
1354
+
1355
+ }
1356
+ "
1357
+
1358
+ end
1359
+
1360
+ anonymous_function{|funcname| "
1361
+ static VALUE #{funcname}(VALUE self,void* block,void* frame, int argc, VALUE* argv #{return_on_block_call ? ", int* block_call" : ""}){
1362
+ void* fptr = 0;
1363
+ #{if global_klass_variable
1364
+ "
1365
+ VALUE klass = #{global_klass_variable};
1366
+ "
1367
+ else
1368
+ "
1369
+ VALUE klass = CLASS_OF(self);
1370
+ "
1371
+ end
1372
+ }
1373
+
1374
+ char method_name[argc*40+64];
1375
+
1376
+ unsigned int fptr_hash = 0;
1377
+ int match = 1;
1378
+
1379
+ #{if table_size > 1
1380
+ "
1381
+ #{unless signature.first
1382
+ "fptr_hash = klass;
1383
+ "
1384
+ end
1385
+ }
1386
+
1387
+ #{
1388
+ compare_hash.map { |k,v|
1389
+ "if (#{v} < argc) {
1390
+ fptr_hash += CLASS_OF(argv[#{v}]);
1391
+ }
1392
+ "
1393
+ }.join("\n")
1394
+ };
1395
+
1396
+ fptr_hash = fptr_hash % #{table_size};
1397
+
1398
+ int j = 0;
1399
+
1400
+ if (argc+15 != #{table_name}[fptr_hash].argc) {
1401
+ match = 0;
1402
+ goto does_not_match;
1403
+ }
1404
+
1405
+ #{unless recvtype
1406
+ "
1407
+ if (match == 1 && #{table_name}[fptr_hash].argument_type[0] != klass ) {
1408
+ match = 0;
1409
+ goto does_not_match;
1410
+ }
1411
+ "
1412
+ end
1413
+ }
1414
+
1415
+ #{
1416
+ compare_hash.map { |k,v|
1417
+ "if (match == 1 && #{table_name}[fptr_hash].argument_type[#{k}] != CLASS_OF(argv[#{v}])) {
1418
+ match = 0;
1419
+ goto does_not_match;
1420
+ }
1421
+ "
1422
+ }.join("\n")
1423
+ };
1424
+ "
1425
+ end
1426
+ }
1427
+
1428
+ if (#{table_name}[fptr_hash].address == 0) match = 0;
1429
+ if (match == 1) {
1430
+ fptr = #{table_name}[fptr_hash].address;
1431
+ } else {
1432
+ does_not_match:
1433
+ method_name[0] = '_';
1434
+ method_name[1] = 0;
1435
+
1436
+ strncpy(method_name+1, \"#{mname}\",sizeof(method_name)-4);
1437
+ sprintf(method_name+strlen(method_name), \"%li\", (long)NUM2PTR(rb_obj_id(CLASS_OF(self))));
1438
+
1439
+ int i;
1440
+ for (i=0; i<argc; i++) {
1441
+ sprintf(method_name+strlen(method_name), \"%li\", (long)NUM2PTR(rb_obj_id(CLASS_OF(argv[i]))));
1442
+ }
1443
+
1444
+ void** address = 0;
1445
+ ID id;
1446
+ VALUE rb_method_hash;
1447
+
1448
+ id = rb_intern(method_name);
1449
+
1450
+ if (rb_respond_to(klass, #{intern_num :method_hash})) {
1451
+ rb_method_hash = rb_funcall(klass, #{intern_num :method_hash},1,#{literal_value mname});
1452
+
1453
+ if (rb_method_hash != Qnil) {
1454
+ VALUE tmp = rb_hash_aref(rb_method_hash, PTR2NUM(id));
1455
+ if (tmp != Qnil) {
1456
+ address = (void**)NUM2PTR(tmp);
1457
+ fptr = *address;
1458
+ }
1459
+ }
1460
+
1461
+ if (fptr == 0) {
1462
+ VALUE fastruby_method = rb_funcall(klass, #{intern_num :fastruby_method}, 1, #{literal_value mname});
1463
+ VALUE tree = rb_funcall(fastruby_method, #{intern_num :tree}, 0,0);
1464
+
1465
+ if (RTEST(tree)) {
1466
+ VALUE argv_class[argc+1];
1467
+
1468
+ argv_class[0] = CLASS_OF(self);
1469
+ for (i=0; i<argc; i++) {
1470
+ argv_class[i+1] = CLASS_OF(argv[i]);
1471
+ }
1472
+
1473
+ VALUE signature = rb_ary_new4(argc+1,argv_class);
1474
+
1475
+ rb_funcall(klass, #{intern_num :build}, 2, signature,rb_str_new2(#{mname.to_s.inspect}));
1476
+
1477
+ id = rb_intern(method_name);
1478
+ rb_method_hash = rb_funcall(klass, #{intern_num :method_hash},1,#{literal_value mname});
1479
+
1480
+ if (rb_method_hash != Qnil) {
1481
+ VALUE tmp = rb_hash_aref(rb_method_hash, PTR2NUM(id));
1482
+ if (tmp != Qnil) {
1483
+ address = (void**)NUM2PTR(tmp);
1484
+ fptr = *address;
1485
+ }
1486
+ }
1487
+
1488
+ if (fptr == 0) {
1489
+ rb_raise(rb_eRuntimeError, \"Error: method not found after build\");
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+
1495
+ // insert the value on table
1496
+ #{table_name}[fptr_hash].argc = argc+15;
1497
+
1498
+ #{unless recvtype
1499
+ "
1500
+ #{table_name}[fptr_hash].argument_type[0] = klass;
1501
+ "
1502
+ end
1503
+ }
1504
+
1505
+ #{
1506
+ compare_hash.map { |k,v|
1507
+ "if (#{v} < argc) {
1508
+ #{table_name}[fptr_hash].argument_type[#{k}] = CLASS_OF(argv[#{v}]);
1509
+ }
1510
+ "
1511
+ }.join("\n")
1512
+ };
1513
+
1514
+ #{table_name}[fptr_hash].address = fptr;
1515
+ }
1516
+
1517
+ if (fptr != 0) {
1518
+ return ((VALUE(*)(VALUE,VALUE,VALUE,int,VALUE*))fptr)(self,(VALUE)block,(VALUE)frame, argc, argv);
1519
+ }
1520
+
1521
+ #{if funcall_fallback
1522
+ "
1523
+
1524
+ #{@frame_struct}* pframe = frame;
1525
+ VALUE method_arguments[4];
1526
+
1527
+ method_arguments[0] = (VALUE)argc;
1528
+ method_arguments[1] = (VALUE)argv;
1529
+ method_arguments[2] = (VALUE)self;
1530
+ method_arguments[3] = (VALUE)block;
1531
+
1532
+ if (block == 0) {
1533
+ return #{
1534
+ protected_block "
1535
+ last_expression = rb_funcall2(((VALUE*)method_arguments)[2], #{intern_num mname.to_sym}, ((int*)method_arguments)[0], ((VALUE**)method_arguments)[1]);", true, "method_arguments"
1536
+ };
1537
+
1538
+ } else {
1539
+ #{
1540
+ if return_on_block_call
1541
+ "*block_call = 1;
1542
+ return Qnil;
1543
+ "
1544
+ else
1545
+ "
1546
+ return #{
1547
+ protected_block "
1548
+ #{@block_struct} *pblock;
1549
+ pblock = (typeof(pblock))( ((VALUE*)method_arguments)[3] );
1550
+ last_expression = rb_iterate(
1551
+ #{anonymous_function{|name_|
1552
+ "
1553
+ static VALUE #{name_} (VALUE data) {
1554
+ VALUE* method_arguments = (VALUE*)data;
1555
+ return rb_funcall2(((VALUE*)method_arguments)[2], #{intern_num mname.to_sym}, ((int*)method_arguments)[0], ((VALUE**)method_arguments)[1]);
1556
+ }
1557
+ "
1558
+ }},
1559
+ (VALUE)method_arguments,
1560
+
1561
+ #{anonymous_function{|name_|
1562
+ "
1563
+ static VALUE #{name_} (VALUE arg_, VALUE param, int argc, VALUE* argv) {
1564
+ #{@block_struct}* pblock = (void*)param;
1565
+
1566
+ if (pblock->proc != Qnil) {
1567
+ VALUE arg;
1568
+ #{
1569
+ # TODO: access directly to argc and argv for optimal execution
1570
+ if RUBY_VERSION =~ /^1\.9/
1571
+ "
1572
+ if (TYPE(arg_) == T_ARRAY) {
1573
+ if (_RARRAY_LEN(arg_) <= 1) {
1574
+ arg = rb_ary_new4(argc,argv);
1575
+ } else {
1576
+ arg = arg_;
1577
+ }
1578
+ } else {
1579
+ arg = rb_ary_new4(argc,argv);
1580
+ }
1581
+ "
1582
+ else
1583
+ "arg = arg_;"
1584
+ end
1585
+ }
1586
+
1587
+ return rb_proc_call(pblock->proc, arg);
1588
+
1589
+ } else {
1590
+ #{
1591
+ # TODO: access directly to argc and argv for optimal execution
1592
+ if RUBY_VERSION =~ /^1\.9/
1593
+ "return ((VALUE(*)(int,VALUE*,VALUE,VALUE))pblock->block_function_address)(argc,argv,(VALUE)pblock->block_function_param,(VALUE)0);"
1594
+ else
1595
+ "return Qnil;"
1596
+ end
1597
+ }
1598
+ }
1599
+
1600
+ }
1601
+ "
1602
+ }},
1603
+ (VALUE)pblock
1604
+ );
1605
+ ", true, "method_arguments"
1606
+ };
1607
+ "
1608
+ end
1609
+ }
1610
+ }
1611
+ "
1612
+ else
1613
+ "
1614
+ rb_raise(rb_eRuntimeError, \"Error: invalid dynamic call for defn\");
1615
+ return Qnil;
1616
+ "
1617
+ end
1618
+ }
1619
+ }
1620
+ "
1621
+ }
1622
+ end
1623
+
1624
+ def intern_num(symbol)
1625
+ symbol = symbol.to_sym
1626
+ @intern_num_hash = Hash.new unless @intern_num_hash
1627
+ return @intern_num_hash[symbol] if @intern_num_hash[symbol]
1628
+
1629
+ name = self.add_global_name("ID", 0);
1630
+
1631
+ init_extra << "
1632
+ #{name} = rb_intern(\"#{symbol.to_s}\");
1633
+ "
1634
+
1635
+ @intern_num_hash[symbol] = name
1636
+
1637
+ name
1638
+ end
1639
+
1640
+ def reserve_table(size, argument_count)
1641
+ name = "glb_table" + rand(1000000000).to_s
1642
+
1643
+ extra_code << "
1644
+ static struct {
1645
+ VALUE argument_type[#{argument_count}];
1646
+ void* address;
1647
+ int argc;
1648
+ } #{name}[#{size}];
1649
+ "
1650
+
1651
+ name
1652
+ end
1653
+
1654
+ def add_global_name(ctype, default)
1655
+ name = "glb" + rand(1000000000).to_s
1656
+
1657
+ extra_code << "
1658
+ static #{ctype} #{name} = #{default};
1659
+ "
1660
+ name
1661
+ end
1662
+
1663
+ def global_entry(glbname)
1664
+ name = add_global_name("struct global_entry*", 0);
1665
+
1666
+ init_extra << "
1667
+ #{name} = rb_global_entry(SYM2ID(#{literal_value glbname}));
1668
+ "
1669
+
1670
+ name
1671
+ end
1672
+
1673
+
1674
+ def frame(code, jmp_code, not_jmp_code = "", rescued = nil)
1675
+
1676
+ anonymous_function{ |name| "
1677
+ static VALUE #{name}(VALUE param) {
1678
+ volatile VALUE last_expression = Qnil;
1679
+ #{@frame_struct} frame;
1680
+
1681
+ typeof(frame)* volatile pframe;
1682
+ typeof(frame)* volatile parent_frame;
1683
+ #{@locals_struct}* volatile plocals;
1684
+
1685
+ parent_frame = (void*)param;
1686
+
1687
+ frame.parent_frame = (void*)param;
1688
+ frame.plocals = parent_frame->plocals;
1689
+ frame.rescue = #{rescued ? rescued : "parent_frame->rescue"};
1690
+ frame.targetted = 0;
1691
+ frame.thread_data = parent_frame->thread_data;
1692
+ if (frame.thread_data == 0) frame.thread_data = rb_current_thread_data();
1693
+
1694
+ plocals = frame.plocals;
1695
+ pframe = &frame;
1696
+
1697
+ int aux = setjmp(frame.jmp);
1698
+ if (aux != 0) {
1699
+ last_expression = pframe->return_value;
1700
+
1701
+ // restore previous frame
1702
+ typeof(pframe) original_frame = pframe;
1703
+ pframe = parent_frame;
1704
+
1705
+ #{jmp_code};
1706
+
1707
+ if (original_frame->targetted == 0) {
1708
+ longjmp(pframe->jmp,aux);
1709
+ }
1710
+
1711
+ return last_expression;
1712
+ }
1713
+
1714
+ #{code};
1715
+
1716
+ // restore previous frame
1717
+ volatile typeof(pframe) original_frame = pframe;
1718
+ pframe = parent_frame;
1719
+ #{not_jmp_code};
1720
+
1721
+ return last_expression;
1722
+
1723
+ }
1724
+ "
1725
+ } + "((VALUE)pframe)"
1726
+ end
1727
+ end
1728
+ end