fastruby 0.0.21 → 0.0.22

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