fastruby 0.0.14 → 0.0.15

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