fastruby 0.0.14 → 0.0.15

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ 0.0.15 Support for continuation objects and callcc
2
+
3
+ Fixed fastruby protocol to support unlimited number of arguments from ruby
4
+
5
+ Support for default arguments (e.g. def foo(a, b=c.to_s, c=99) )
6
+
7
+ Minor fixes to splat arguments on blocks
8
+
9
+ Support for receving block as proc (i.e. def foo(&block); end)
10
+
11
+ Support for passing proc as block (i.e. foo(&block))
12
+
1
13
  0.0.14 Support for method replacement ("monkey patching")
2
14
 
3
15
  Internal: Implemented method hash to store implementation pointers
data/README CHANGED
@@ -58,9 +58,7 @@ I will stabilize the API and document it for next releases. I promise
58
58
  == Known Limitations & Issues
59
59
 
60
60
  * callcc is not supported, it works but using it may result in unexpected behaviour
61
- * recursive methods will be slow since the optimization of method calls only works with already defined methods
62
61
  * calls with blocks to ruby or cruby methods are almost as slow as normal ruby (if the called method is defined by fastruby, the call is pretty fast)
63
- * When calling fastruby methods from normal ruby, the max. number of arguments allowed is 15
64
62
  * replacement of methods using identical code strings will work, but it will generate duplicated objects on the cache each time te snippet run
65
63
 
66
64
  == Usage
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require "rspec/core/rake_task"
7
7
 
8
8
  spec = Gem::Specification.new do |s|
9
9
  s.name = 'fastruby'
10
- s.version = '0.0.14'
10
+ s.version = '0.0.15'
11
11
  s.author = 'Dario Seminara'
12
12
  s.email = 'robertodarioseminara@gmail.com'
13
13
  s.platform = Gem::Platform::RUBY
@@ -53,6 +53,7 @@ y2 = Y2.new
53
53
  x2 = X2.new
54
54
 
55
55
  Y.build([Y,X],:bar)
56
+ X.build([X,Fixnum,Fixnum],:foo)
56
57
 
57
58
  require 'benchmark'
58
59
 
@@ -20,8 +20,8 @@ class Y
20
20
  i = 1000000
21
21
 
22
22
  lvar_type(i,Fixnum)
23
+ lvar_type(x2,X)
23
24
 
24
- x2 = 0
25
25
  ret = 0
26
26
  while i > 0
27
27
  x.foo do |x2|
@@ -66,6 +66,8 @@ y2 = Y2.new
66
66
  x2 = X2.new
67
67
 
68
68
  Y.build([Y,X],:bar)
69
+ X.build([X],:foo)
70
+ X.build([X],:bar)
69
71
 
70
72
  require 'benchmark'
71
73
 
@@ -23,6 +23,7 @@ struct FASTRUBYTHREADDATA {
23
23
  VALUE exception;
24
24
  VALUE accumulator;
25
25
  VALUE rb_stack_chunk;
26
+ void* last_plocals;
26
27
  };
27
28
 
28
29
  struct METHOD {
@@ -196,6 +197,7 @@ static inline VALUE rb_thread_data_create() {
196
197
  thread_data->exception = Qnil;
197
198
  thread_data->accumulator = Qnil;
198
199
  thread_data->rb_stack_chunk = Qnil;
200
+ thread_data->last_plocals = 0;
199
201
 
200
202
  return ret;
201
203
  }
@@ -27,24 +27,6 @@ require "fastruby_load_path"
27
27
  require FastRuby.fastruby_load_path + "/../ext/fastruby_base/fastruby_base"
28
28
 
29
29
  module FastRuby
30
-
31
- def self.build_defs(tree, *options)
32
- method_name = tree[2].to_s
33
-
34
- FastRuby.logger.info "Building singleton method #{self}::#{@method_name}"
35
-
36
- locals = GetLocalsProcessor.get_locals(tree)
37
- locals << :self
38
-
39
- context = FastRuby::Context.new(false)
40
- context.locals = locals
41
- context.options = options
42
-
43
- context.alt_method_name = "singleton_" + method_name + rand(100000000).to_s
44
-
45
- [context.extra_code + context.to_c_method_defs(tree), context.alt_method_name, context.init_extra]
46
- end
47
-
48
30
  class Method
49
31
  attr_accessor :locals
50
32
  attr_accessor :options
@@ -81,19 +63,29 @@ module FastRuby
81
63
  context.locals = locals
82
64
  context.options = options
83
65
 
84
- args_tree = tree[2]
66
+ args_tree = if tree[0] == :defn
67
+ tree[2]
68
+ elsif tree[0] == :defs
69
+ tree[3]
70
+ else
71
+ raise ArgumentError, "unknown type of method definition #{tree[0]}"
72
+ end
85
73
 
86
74
  # create random method name
87
75
  context.snippet_hash = snippet_hash
88
76
  context.alt_method_name = "_" + @method_name.to_s + "_" + rand(10000000000).to_s
89
77
 
90
- (1..signature.size).each do |i|
78
+ (1..signature.size-1).each do |i|
91
79
  arg = args_tree[i]
92
- if arg
93
- if arg.to_s.match(/\*/)
94
- context.infer_lvar_map[arg.to_s.gsub("*","").to_sym] = Array
95
- else
96
- context.infer_lvar_map[arg.to_sym] = signature[i]
80
+
81
+ if arg.instance_of? Symbol
82
+
83
+ if arg
84
+ if arg.to_s.match(/\*/)
85
+ context.infer_lvar_map[arg.to_s.gsub("*","").to_sym] = Array
86
+ else
87
+ context.infer_lvar_map[arg.to_sym] = signature[i]
88
+ end
97
89
  end
98
90
  end
99
91
  end
@@ -122,11 +114,7 @@ module FastRuby
122
114
  def builder.generate_ext
123
115
  ext = []
124
116
 
125
- if @include_ruby_first
126
- @inc.unshift "#include \"ruby.h\""
127
- else
128
- @inc.push "#include \"ruby.h\""
129
- end
117
+ @inc.unshift "#include \"ruby.h\""
130
118
 
131
119
  ext << @inc
132
120
  ext << nil
@@ -173,18 +161,6 @@ module FastRuby
173
161
  unless options[:no_cache]
174
162
  FastRuby.cache.insert(snippet_hash, so_name) unless no_cache
175
163
  end
176
-
177
- ret = Object.new
178
-
179
- ret.extend MethodExtent
180
- ret.yield_signature = context.yield_signature
181
-
182
- ret
183
-
184
- end
185
-
186
- module MethodExtent
187
- attr_accessor :yield_signature
188
164
  end
189
165
  end
190
166
 
@@ -21,4 +21,12 @@ along with fastruby. if not, see <http://www.gnu.org/licenses/>.
21
21
  module FastRuby
22
22
  class TypeMismatchAssignmentException < Exception
23
23
  end
24
+
25
+ class JumpTagException < Exception
26
+ attr_reader :state
27
+
28
+ def initialize(state_)
29
+ @state = state_
30
+ end
31
+ end
24
32
  end
@@ -34,6 +34,23 @@ module FastRuby
34
34
  if tree.node_type == :lasgn
35
35
  @locals << tree[1]
36
36
  end
37
+
38
+ if tree[0] == :args
39
+
40
+ tree[1..-1].each do |subtree|
41
+ if subtree.instance_of? Symbol
42
+ @locals << subtree.to_s.gsub("*","").gsub("&","").to_sym
43
+ end
44
+ end
45
+
46
+ if tree.find{|x| x.to_s[0] == ?&}
47
+ @locals << :__xproc_arguments
48
+ end
49
+ end
50
+
51
+ if tree[0] == :block_pass
52
+ @locals << :__xblock_arguments
53
+ end
37
54
 
38
55
  tree.select{|subtree| subtree.instance_of? FastRuby::FastRubySexp}.each do |subtree|
39
56
  process(subtree)
@@ -103,26 +103,13 @@ class Object
103
103
  end
104
104
  end
105
105
 
106
- def self.execute_tree(argument,*options_hashes)
106
+ def self.execute_tree(tree,*options_hashes)
107
107
  options_hash = {:validate_lvar_types => true}
108
108
  options_hashes.each do |opt|
109
109
  options_hash.merge!(opt)
110
110
  end
111
111
 
112
112
  require "fastruby/fastruby_sexp"
113
- if argument.instance_of? FastRuby::FastRubySexp
114
- tree = argument
115
- elsif argument.instance_of? String
116
- require "rubygems"
117
- require "ruby_parser"
118
- require "fastruby/sexp_extension"
119
- tree = RubyParser.new.parse(argument).to_fastruby_sexp
120
- else
121
- require "pry"
122
- binding.pry
123
- raise ArgumentError
124
- end
125
-
126
113
  method_name = tree[1]
127
114
 
128
115
  self_ = options_hash[:self]
@@ -141,22 +128,5 @@ class Object
141
128
  $refered_from_code_array = Array.new unless $refered_from_code_array
142
129
  $refered_from_code_array << self
143
130
  end
144
-
145
- private
146
- def self.to_class_name(argument)
147
- require "sexp"
148
- if argument.instance_of? Symbol
149
- argument.to_s
150
- elsif argument.instance_of? Sexp
151
- if argument[0] == :colon3
152
- "::" + to_class_name(argument[1])
153
- elsif argument[0] == :colon2
154
- to_class_name(argument[1]) + "::" + to_class_name(argument[2])
155
- elsif argument[0] == :const
156
- to_class_name(argument[1])
157
- end
158
- end
159
- end
160
-
161
131
  end
162
132
 
@@ -26,7 +26,7 @@ module FastRuby
26
26
  def to_c_yield(tree)
27
27
 
28
28
  block_code = proc { |name| "
29
- static VALUE #{name}(VALUE frame_param, VALUE* block_args) {
29
+ static VALUE #{name}(VALUE frame_param, VALUE* block_args, int size) {
30
30
 
31
31
  #{@locals_struct} *plocals;
32
32
  #{@frame_struct} *pframe;
@@ -36,38 +36,60 @@ module FastRuby
36
36
  if (FIX2LONG(plocals->block_function_address) == 0) {
37
37
  #{_raise("rb_eLocalJumpError", "no block given")};
38
38
  } else {
39
- return ((VALUE(*)(int,VALUE*,VALUE,VALUE))FIX2LONG(plocals->block_function_address))(#{tree.size-1}, block_args, FIX2LONG(plocals->block_function_param), (VALUE)pframe);
39
+ return ((VALUE(*)(int,VALUE*,VALUE,VALUE))FIX2LONG(plocals->block_function_address))(size, block_args, FIX2LONG(plocals->block_function_param), (VALUE)pframe);
40
40
  }
41
41
  }
42
42
  "
43
43
  }
44
44
 
45
- new_yield_signature = tree[1..-1].map{|subtree| infer_type subtree}
46
- # merge the new_yield_signature with the new
47
- if @yield_signature
48
- if new_yield_signature.size == @yield_signature.size
49
- (0..new_yield_signature.size-1).each do |i|
50
- if @yield_signature[i] != new_yield_signature[i]
51
- @yield_signature[i] = nil
52
- end
53
- end
54
- else
55
- @yield_signature = new_yield_signature.map{|x| nil}
56
- end
45
+ splat_arg = tree.find{|x| x == :yield ? false : x[0] == :splat}
46
+ ret = nil
47
+ if splat_arg
48
+ ret = inline_block "
49
+ VALUE splat_array = #{to_c(splat_arg[1])};
50
+
51
+ if (CLASS_OF(splat_array) == rb_cArray) {
52
+ VALUE block_args[RARRAY(splat_array)->len + #{tree.size}];
53
+ int i;
54
+ #{
55
+ (0..tree.size-3).map{|i|
56
+ "block_args[#{i}] = #{to_c(tree[i+1])}"
57
+ }.join(";\n")
58
+ };
59
+
60
+ for (i=0; i<RARRAY(splat_array)->len; i++) {
61
+ block_args[i+#{tree.size-2}] = rb_ary_entry(splat_array,i);
62
+ }
63
+
64
+ return #{anonymous_function(&block_code)}((VALUE)pframe, block_args, RARRAY(splat_array)->len + #{tree.size-2});
65
+ } else {
66
+ VALUE block_args[1+#{tree.size}];
67
+ #{
68
+ (0..tree.size-3).map{|i|
69
+ "block_args[#{i}] = #{to_c(tree[i+1])}"
70
+ }.join(";\n")
71
+ };
72
+
73
+ block_args[#{tree.size-2}] = splat_array;
74
+ return #{anonymous_function(&block_code)}((VALUE)pframe, block_args, #{tree.size-1});
75
+ }
76
+
77
+ "
57
78
  else
58
- @yield_signature = new_yield_signature
79
+ ret = if tree.size > 1
80
+ anonymous_function(&block_code)+"((VALUE)pframe, (VALUE[]){#{tree[1..-1].map{|subtree| to_c subtree}.join(",")}},#{tree.size-1})"
81
+ else
82
+ anonymous_function(&block_code)+"((VALUE)pframe, (VALUE[]){}, #{tree.size-1})"
83
+ end
59
84
  end
60
-
61
- ret = if tree.size > 1
62
- anonymous_function(&block_code)+"((VALUE)pframe, (VALUE[]){#{tree[1..-1].map{|subtree| to_c subtree}.join(",")}})"
63
- else
64
- anonymous_function(&block_code)+"((VALUE)pframe, (VALUE[]){})"
65
- end
66
-
85
+
67
86
  protected_block(ret, false)
68
87
  end
69
88
 
70
89
  def to_c_block(tree)
90
+ if tree.size == 1
91
+ return inline_block("return Qnil;")
92
+ end
71
93
 
72
94
  str = ""
73
95
  str = tree[1..-2].map{ |subtree|
@@ -36,10 +36,34 @@ module FastRuby
36
36
  args = tree[3]
37
37
  return _raise(args[1],args[2])
38
38
  end
39
-
39
+
40
40
  recv = tree[1]
41
41
  mname = tree[2]
42
42
  args = tree[3]
43
+
44
+ # search block_pass on arguments
45
+ block_pass_arg = args.find{|arg| if arg == :arglist
46
+ false
47
+ else
48
+ arg[0] == :block_pass
49
+ end}
50
+ if block_pass_arg
51
+
52
+ call_tree = tree.dup
53
+ call_tree[3] = args.select{|arg| if arg == :arglist
54
+ true
55
+ else
56
+ arg[0] != :block_pass
57
+ end
58
+ }
59
+
60
+ block_arguments_tree = s(:masgn, s(:array, s(:splat, s(:lasgn, :__xblock_arguments))))
61
+ block_tree = s(:call, block_pass_arg[1], :call, s(:arglist, s(:splat, s(:lvar, :__xblock_arguments))))
62
+
63
+ replace_iter_tree = s(:iter, call_tree, block_arguments_tree, block_tree).to_fastruby_sexp
64
+
65
+ return to_c(replace_iter_tree)
66
+ end
43
67
 
44
68
  mname = :require_fastruby if mname == :require
45
69
 
@@ -25,8 +25,43 @@ module FastRuby
25
25
  def to_c_defn(tree)
26
26
 
27
27
  method_name = tree[1]
28
- args_tree = tree[2]
28
+ args_tree = tree[2].select{|x| x.to_s[0] != ?&}
29
+
30
+ global_klass_variable = add_global_name("VALUE", "Qnil");
31
+
32
+ hash = Hash.new
33
+ value_cast = ( ["VALUE"]*(args_tree.size+2) ).join(",")
34
+
35
+ strmethodargs = "self,block,(VALUE)&frame"
36
+
37
+ anonymous_method_name = anonymous_dispatcher(global_klass_variable, method_name)
38
+ alt_options = options.dup
29
39
 
40
+ alt_options.delete(:self)
41
+ alt_options.delete(:main)
42
+
43
+ inline_block "
44
+ rb_define_method(plocals->self, #{method_name.to_s.inspect}, #{anonymous_method_name}, -1 );
45
+
46
+ #{global_klass_variable} = plocals->self;
47
+ // set tree
48
+ rb_funcall(#{literal_value FastRuby}, #{intern_num :set_tree}, 5,
49
+ #{global_klass_variable},
50
+ rb_str_new2(#{method_name.to_s.inspect}),
51
+ #{literal_value tree},
52
+ #{literal_value snippet_hash},
53
+ #{literal_value alt_options}
54
+
55
+ );
56
+
57
+ "
58
+
59
+ end
60
+
61
+ def to_c_defs(tree)
62
+ method_name = tree[2]
63
+ args_tree = tree[3].select{|x| x.to_s[0] != ?&}
64
+
30
65
  global_klass_variable = add_global_name("VALUE", "Qnil");
31
66
 
32
67
  hash = Hash.new
@@ -34,9 +69,50 @@ module FastRuby
34
69
 
35
70
  strmethodargs = "self,block,(VALUE)&frame"
36
71
 
37
- anonymous_method_name = anonymous_function{ |anonymous_method_name| "VALUE #{anonymous_method_name}(int argc_, VALUE* argv, VALUE self) {
72
+ anonymous_method_name = anonymous_dispatcher(global_klass_variable, method_name)
73
+
74
+ alt_options = options.dup
75
+
76
+ alt_options.delete(:self)
77
+ alt_options.delete(:main)
78
+
79
+ inline_block "
80
+
81
+ VALUE obj = #{to_c tree[1]};
82
+ rb_define_singleton_method(obj, #{method_name.to_s.inspect}, #{anonymous_method_name}, -1 );
83
+
84
+ #{global_klass_variable} = CLASS_OF(obj);
85
+ // set tree
86
+ rb_funcall(#{literal_value FastRuby}, #{intern_num :set_tree}, 5,
87
+ #{global_klass_variable},
88
+ rb_str_new2(#{method_name.to_s.inspect}),
89
+ #{literal_value tree},
90
+ #{literal_value snippet_hash},
91
+ #{literal_value alt_options}
92
+
93
+ );
94
+
95
+ "
96
+
97
+ end
98
+
99
+ def to_c_scope(tree)
100
+ if tree[1]
101
+ to_c(tree[1])
102
+ else
103
+ "Qnil"
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def anonymous_dispatcher(global_klass_variable, method_name)
110
+
111
+ strmethodargs = "self,block,(VALUE)&frame"
112
+
113
+ anonymous_function{ |anonymous_method_name| "VALUE #{anonymous_method_name}(int argc_, VALUE* argv, VALUE self) {
38
114
  VALUE klass = #{global_klass_variable};
39
- char method_name[0x100];
115
+ char method_name[argc_*40+64];
40
116
 
41
117
  method_name[0] = '_';
42
118
  method_name[1] = 0;
@@ -133,67 +209,19 @@ module FastRuby
133
209
  }
134
210
 
135
211
  if (frame.targetted == 0) {
136
- rb_jump_tag(aux);
212
+ frb_jump_tag(aux);
137
213
  }
138
214
 
139
215
  return Qnil;
140
216
  }
217
+
218
+ VALUE tmp = Qnil;
219
+ if (argv == 0) argv = &tmp;
141
220
 
142
- if (argc_ == 0) return ((VALUE(*)(VALUE,VALUE,VALUE))fptr)(#{strmethodargs});
143
-
144
- #{ (1..15).map{ |i|
145
- value_cast = ( ["VALUE"]*(i+3) ).join(",")
146
- "if (argc_ == #{i}) return ((VALUE(*)(#{value_cast}))fptr)(#{strmethodargs}, #{(0..i-1).map{|x| "argv[#{x}]"}.join(",")});"
147
- }.join("\n");
148
- }
149
-
150
- rb_raise(rb_eArgError, \"too many arguments: %d\", argc_);
221
+ return ((VALUE(*)(VALUE,VALUE,VALUE,int,VALUE*))fptr)(#{strmethodargs}, argc_, argv);
151
222
  }"
152
223
  }
153
-
154
- alt_options = options.dup
155
-
156
- alt_options.delete(:self)
157
- alt_options.delete(:main)
158
-
159
- inline_block "
160
- rb_define_method(plocals->self, #{method_name.to_s.inspect}, #{anonymous_method_name}, -1 );
161
-
162
- #{global_klass_variable} = plocals->self;
163
- // set tree
164
- rb_funcall(#{literal_value FastRuby}, #{intern_num :set_tree}, 5,
165
- #{global_klass_variable},
166
- rb_str_new2(#{method_name.to_s.inspect}),
167
- #{literal_value tree},
168
- #{literal_value snippet_hash},
169
- #{literal_value alt_options}
170
-
171
- );
172
-
173
- "
174
-
175
- end
176
-
177
- def to_c_defs(tree)
178
- args_tree = tree[3];
179
-
180
- tmp = FastRuby.build_defs(tree)
181
-
182
- extra_code << tmp[0]
183
- @init_extra = @init_extra + tmp[2]
184
-
185
- inline_block "
186
- rb_define_singleton_method(#{to_c tree[1]}, \"#{tree[2].to_s}\", (void*)#{tmp[1]}, #{args_tree.size-1});
187
- return Qnil;
188
- "
189
- end
190
-
191
- def to_c_scope(tree)
192
- if tree[1]
193
- to_c(tree[1])
194
- else
195
- "Qnil"
196
- end
224
+
197
225
  end
198
226
  end
199
227
  end