fastruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +2 -0
- data/LICENSE +674 -0
- data/README +142 -0
- data/Rakefile +47 -0
- data/TODO +31 -0
- data/benchmarks/benchmark.rb +68 -0
- data/benchmarks/benchmark2.rb +70 -0
- data/benchmarks/benchmark3.rb +70 -0
- data/benchmarks/benchmark4.rb +94 -0
- data/benchmarks/benchmark5.rb +80 -0
- data/benchmarks/benchmark6.rb +64 -0
- data/examples/example1.rb +12 -0
- data/examples/example2.rb +14 -0
- data/examples/example3.rb +12 -0
- data/examples/example4.rb +17 -0
- data/lib/fastruby/builder.rb +97 -0
- data/lib/fastruby/exceptions.rb +24 -0
- data/lib/fastruby/getlocals.rb +47 -0
- data/lib/fastruby/inline_extension.rb +87 -0
- data/lib/fastruby/logging.rb +37 -0
- data/lib/fastruby/object.rb +168 -0
- data/lib/fastruby/translator.rb +903 -0
- data/lib/fastruby.rb +24 -0
- data/spec/base_spec.rb +357 -0
- data/spec/block_spec.rb +418 -0
- data/spec/control_spec.rb +71 -0
- data/spec/expression_spec.rb +59 -0
- data/spec/integrity_spec.rb +74 -0
- data/spec/literal_spec.rb +95 -0
- data/spec/variable_spec.rb +63 -0
- metadata +127 -0
@@ -0,0 +1,903 @@
|
|
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 "rubygems"
|
22
|
+
require "inline"
|
23
|
+
require "set"
|
24
|
+
|
25
|
+
module FastRuby
|
26
|
+
class Context
|
27
|
+
|
28
|
+
attr_accessor :infer_lvar_map
|
29
|
+
attr_accessor :alt_method_name
|
30
|
+
attr_accessor :locals
|
31
|
+
attr_accessor :options
|
32
|
+
attr_accessor :infer_self
|
33
|
+
attr_reader :extra_code
|
34
|
+
attr_reader :yield_signature
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@infer_lvar_map = Hash.new
|
38
|
+
@extra_code = ""
|
39
|
+
@options = {}
|
40
|
+
|
41
|
+
extra_code << '#include "node.h"
|
42
|
+
'
|
43
|
+
|
44
|
+
extra_code << "static VALUE _rb_gvar_set(void* ge,VALUE value) {
|
45
|
+
rb_gvar_set((struct global_entry*)ge,value);
|
46
|
+
return value;
|
47
|
+
}
|
48
|
+
"
|
49
|
+
|
50
|
+
extra_code << "static VALUE _rb_ivar_set(VALUE recv,ID idvar, VALUE value) {
|
51
|
+
rb_ivar_set(recv,idvar,value);
|
52
|
+
return value;
|
53
|
+
}
|
54
|
+
"
|
55
|
+
|
56
|
+
extra_code << "static VALUE _lvar_assing(VALUE* destination,VALUE value) {
|
57
|
+
*destination = value;
|
58
|
+
return value;
|
59
|
+
}
|
60
|
+
"
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_block
|
65
|
+
yield
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_c(tree)
|
69
|
+
return "Qnil" unless tree
|
70
|
+
send("to_c_" + tree[0].to_s, tree);
|
71
|
+
end
|
72
|
+
|
73
|
+
def anonymous_function(method)
|
74
|
+
|
75
|
+
name = "anonymous" + rand(10000000).to_s
|
76
|
+
extra_code << method.call(name)
|
77
|
+
|
78
|
+
name
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_c_dot2(tree)
|
82
|
+
"rb_range_new(#{to_c tree[1]}, #{to_c tree[2]},0)"
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_c_attrasgn(tree)
|
86
|
+
to_c_call(tree)
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_c_iter(tree)
|
90
|
+
|
91
|
+
call_tree = tree[1]
|
92
|
+
args_tree = tree[2]
|
93
|
+
recv_tree = call_tree[1]
|
94
|
+
|
95
|
+
directive_code = directive(call_tree)
|
96
|
+
if directive_code
|
97
|
+
return directive_code
|
98
|
+
end
|
99
|
+
|
100
|
+
other_call_tree = call_tree.dup
|
101
|
+
other_call_tree[1] = s(:lvar, :arg)
|
102
|
+
|
103
|
+
call_args_tree = call_tree[3]
|
104
|
+
|
105
|
+
caller_code = nil
|
106
|
+
|
107
|
+
str_lvar_initialization = @locals_struct + " *plocals;
|
108
|
+
plocals = (void*)param;"
|
109
|
+
|
110
|
+
recvtype = infer_type(recv_tree || s(:self))
|
111
|
+
|
112
|
+
address = nil
|
113
|
+
mobject = nil
|
114
|
+
len = nil
|
115
|
+
|
116
|
+
convention = :ruby
|
117
|
+
|
118
|
+
extra_inference = {}
|
119
|
+
|
120
|
+
if recvtype
|
121
|
+
|
122
|
+
inference_complete = true
|
123
|
+
signature = [recvtype]
|
124
|
+
|
125
|
+
call_args_tree[1..-1].each do |arg|
|
126
|
+
argtype = infer_type(arg)
|
127
|
+
if argtype
|
128
|
+
signature << argtype
|
129
|
+
else
|
130
|
+
inference_complete = false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
convention = nil
|
135
|
+
|
136
|
+
if recvtype.respond_to? :method_tree and inference_complete
|
137
|
+
|
138
|
+
if recvtype.method_tree[call_tree[2]]
|
139
|
+
mobject = recvtype.build(signature, call_tree[2])
|
140
|
+
yield_signature = mobject.yield_signature
|
141
|
+
|
142
|
+
if not args_tree
|
143
|
+
elsif args_tree.first == :lasgn
|
144
|
+
if yield_signature[0]
|
145
|
+
extra_inference[args_tree.last] = yield_signature[0]
|
146
|
+
end
|
147
|
+
elsif args_tree.first == :masgn
|
148
|
+
yield_args = args_tree[1][1..-1].map(&:last)
|
149
|
+
(0...yield_signature.size-1).each do |i|
|
150
|
+
extra_inference[yield_args[i]] = yield_signature[i]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
convention = :fastruby
|
155
|
+
else
|
156
|
+
mobject = recvtype.instance_method(call_tree[2])
|
157
|
+
convention = :cruby
|
158
|
+
end
|
159
|
+
else
|
160
|
+
mobject = recvtype.instance_method(call_tree[2])
|
161
|
+
convention = :cruby
|
162
|
+
end
|
163
|
+
|
164
|
+
address = getaddress(mobject)
|
165
|
+
len = getlen(mobject)
|
166
|
+
|
167
|
+
unless address
|
168
|
+
convention = :ruby
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
anonymous_impl = tree[3]
|
174
|
+
|
175
|
+
str_lvar_initialization = @locals_struct + " *plocals;
|
176
|
+
plocals = (void*)param;"
|
177
|
+
|
178
|
+
str_arg_initialization = ""
|
179
|
+
|
180
|
+
str_impl = ""
|
181
|
+
|
182
|
+
with_extra_inference(extra_inference) do
|
183
|
+
# if impl_tree is a block, implement the last node with a return
|
184
|
+
if anonymous_impl
|
185
|
+
if anonymous_impl[0] == :block
|
186
|
+
str_impl = anonymous_impl[1..-2].map{ |subtree|
|
187
|
+
to_c(subtree)
|
188
|
+
}.join(";")
|
189
|
+
|
190
|
+
if anonymous_impl[-1][0] != :return
|
191
|
+
str_impl = str_impl + ";last_expression = (#{to_c(anonymous_impl[-1])});"
|
192
|
+
else
|
193
|
+
str_impl = str_impl + ";#{to_c(anonymous_impl[-1])};"
|
194
|
+
end
|
195
|
+
else
|
196
|
+
if anonymous_impl[0] != :return
|
197
|
+
str_impl = str_impl + ";last_expression = (#{to_c(anonymous_impl)});"
|
198
|
+
else
|
199
|
+
str_impl = str_impl + ";#{to_c(anonymous_impl)};"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
else
|
203
|
+
str_impl = "last_expression = Qnil;"
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
if convention == :ruby or convention == :cruby
|
209
|
+
|
210
|
+
if call_args_tree.size > 1
|
211
|
+
|
212
|
+
str_called_code_args = call_args_tree[1..-1].map{ |subtree| to_c subtree }.join(",")
|
213
|
+
str_recv = to_c recv_tree
|
214
|
+
|
215
|
+
str_recv = "plocals->self" unless recv_tree
|
216
|
+
|
217
|
+
caller_code = proc { |name| "
|
218
|
+
static VALUE #{name}(VALUE param) {
|
219
|
+
// call to #{call_tree[2]}
|
220
|
+
|
221
|
+
#{str_lvar_initialization}
|
222
|
+
return rb_funcall(#{str_recv}, #{call_tree[2].to_i}, #{call_args_tree.size-1}, #{str_called_code_args});
|
223
|
+
}
|
224
|
+
"
|
225
|
+
}
|
226
|
+
|
227
|
+
else
|
228
|
+
str_recv = to_c recv_tree
|
229
|
+
str_recv = "plocals->self" unless recv_tree
|
230
|
+
|
231
|
+
caller_code = proc { |name| "
|
232
|
+
static VALUE #{name}(VALUE param) {
|
233
|
+
// call to #{call_tree[2]}
|
234
|
+
#{str_lvar_initialization}
|
235
|
+
return rb_funcall(#{str_recv}, #{call_tree[2].to_i}, 0);
|
236
|
+
}
|
237
|
+
"
|
238
|
+
}
|
239
|
+
end
|
240
|
+
|
241
|
+
if not args_tree
|
242
|
+
str_arg_initialization = ""
|
243
|
+
elsif args_tree.first == :lasgn
|
244
|
+
str_arg_initialization = "plocals->#{args_tree[1]} = arg;"
|
245
|
+
elsif args_tree.first == :masgn
|
246
|
+
arguments = args_tree[1][1..-1].map(&:last)
|
247
|
+
|
248
|
+
(0..arguments.size-1).each do |i|
|
249
|
+
str_arg_initialization << "plocals->#{arguments[i]} = rb_ary_entry(arg,#{i});\n"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
str_arg_initialization
|
254
|
+
|
255
|
+
block_code = proc { |name| "
|
256
|
+
static VALUE #{name}(VALUE arg, VALUE param) {
|
257
|
+
// block for call to #{call_tree[2]}
|
258
|
+
VALUE last_expression = Qnil;
|
259
|
+
|
260
|
+
#{str_lvar_initialization};
|
261
|
+
#{str_arg_initialization}
|
262
|
+
#{str_impl}
|
263
|
+
|
264
|
+
return last_expression;
|
265
|
+
}
|
266
|
+
"
|
267
|
+
}
|
268
|
+
|
269
|
+
"rb_iterate(#{anonymous_function(caller_code)}, (VALUE)#{locals_pointer}, #{anonymous_function(block_code)}, (VALUE)#{locals_pointer})"
|
270
|
+
elsif convention == :fastruby
|
271
|
+
|
272
|
+
str_arg_initialization = ""
|
273
|
+
|
274
|
+
if not args_tree
|
275
|
+
str_arg_initialization = ""
|
276
|
+
elsif args_tree.first == :lasgn
|
277
|
+
str_arg_initialization = "plocals->#{args_tree[1]} = argv[0];"
|
278
|
+
elsif args_tree.first == :masgn
|
279
|
+
arguments = args_tree[1][1..-1].map(&:last)
|
280
|
+
|
281
|
+
(0..arguments.size-1).each do |i|
|
282
|
+
str_arg_initialization << "plocals->#{arguments[i]} = #{i} < argc ? argv[#{i}] : Qnil;\n"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
block_code = proc { |name| "
|
287
|
+
static VALUE #{name}(int argc, VALUE* argv, VALUE param) {
|
288
|
+
// block for call to #{call_tree[2]}
|
289
|
+
VALUE last_expression = Qnil;
|
290
|
+
|
291
|
+
#{str_lvar_initialization};
|
292
|
+
#{str_arg_initialization}
|
293
|
+
#{str_impl}
|
294
|
+
|
295
|
+
return last_expression;
|
296
|
+
}
|
297
|
+
"
|
298
|
+
}
|
299
|
+
|
300
|
+
|
301
|
+
str_recv = "plocals->self"
|
302
|
+
|
303
|
+
if recv_tree
|
304
|
+
str_recv = to_c recv_tree
|
305
|
+
end
|
306
|
+
|
307
|
+
if call_args_tree.size > 1
|
308
|
+
value_cast = ( ["VALUE"]*(call_tree[3].size) ).join(",")
|
309
|
+
value_cast = value_cast + ", VALUE" if convention == :fastruby
|
310
|
+
|
311
|
+
str_called_code_args = call_tree[3][1..-1].map{|subtree| to_c subtree}.join(",")
|
312
|
+
|
313
|
+
caller_code = proc { |name| "
|
314
|
+
static VALUE #{name}(VALUE param) {
|
315
|
+
#{@block_struct} block;
|
316
|
+
|
317
|
+
block.block_function_address = (void*)#{anonymous_function(block_code)};
|
318
|
+
block.block_function_param = (void*)param;
|
319
|
+
|
320
|
+
// call to #{call_tree[2]}
|
321
|
+
|
322
|
+
#{str_lvar_initialization}
|
323
|
+
|
324
|
+
return ((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{str_recv}, (VALUE)&block, #{str_called_code_args});
|
325
|
+
}
|
326
|
+
"
|
327
|
+
}
|
328
|
+
|
329
|
+
else
|
330
|
+
caller_code = proc { |name| "
|
331
|
+
static VALUE #{name}(VALUE param) {
|
332
|
+
#{@block_struct} block;
|
333
|
+
|
334
|
+
block.block_function_address = (void*)#{anonymous_function(block_code)};
|
335
|
+
block.block_function_param = (void*)param;
|
336
|
+
|
337
|
+
// call to #{call_tree[2]}
|
338
|
+
#{str_lvar_initialization}
|
339
|
+
|
340
|
+
return ((VALUE(*)(VALUE,VALUE))0x#{address.to_s(16)})(#{str_recv}, (VALUE)&block);
|
341
|
+
}
|
342
|
+
"
|
343
|
+
}
|
344
|
+
end
|
345
|
+
|
346
|
+
"#{anonymous_function(caller_code)}((VALUE)#{locals_pointer})"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def to_c_yield(tree)
|
351
|
+
|
352
|
+
block_code = proc { |name| "
|
353
|
+
static VALUE #{name}(VALUE locals_param, VALUE* block_args) {
|
354
|
+
|
355
|
+
#{@locals_struct} *plocals;
|
356
|
+
plocals = (void*)locals_param;
|
357
|
+
|
358
|
+
if (plocals->block_function_address == 0) {
|
359
|
+
rb_raise(rb_eLocalJumpError, \"no block given\");
|
360
|
+
} else {
|
361
|
+
return ((VALUE(*)(int,VALUE*,VALUE))plocals->block_function_address)(#{tree.size-1}, block_args, plocals->block_function_param);
|
362
|
+
}
|
363
|
+
}
|
364
|
+
"
|
365
|
+
}
|
366
|
+
|
367
|
+
new_yield_signature = tree[1..-1].map{|subtree| infer_type subtree}
|
368
|
+
# merge the new_yield_signature with the new
|
369
|
+
if @yield_signature
|
370
|
+
if new_yield_signature.size == @yield_signature.size
|
371
|
+
(0..new_yield_signature.size-1).each do |i|
|
372
|
+
if @yield_signature[i] != new_yield_signature[i]
|
373
|
+
@yield_signature[i] = nil
|
374
|
+
end
|
375
|
+
end
|
376
|
+
else
|
377
|
+
@yield_signature = new_yield_signature.map{|x| nil}
|
378
|
+
end
|
379
|
+
else
|
380
|
+
@yield_signature = new_yield_signature
|
381
|
+
end
|
382
|
+
|
383
|
+
if tree.size > 1
|
384
|
+
anonymous_function(block_code)+"((VALUE)#{locals_pointer}, (VALUE[]){#{tree[1..-1].map{|subtree| to_c subtree}.join(",")}})"
|
385
|
+
else
|
386
|
+
anonymous_function(block_code)+"((VALUE)#{locals_pointer}, (VALUE[]){})"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def to_c_block(tree)
|
391
|
+
|
392
|
+
str = ""
|
393
|
+
str = tree[1..-2].map{ |subtree|
|
394
|
+
to_c(subtree)
|
395
|
+
}.join(";")
|
396
|
+
|
397
|
+
if tree[-1][0] != :return
|
398
|
+
str = str + ";last_expression = #{to_c(tree[-1])};"
|
399
|
+
else
|
400
|
+
str = str + ";#{to_c(tree[-1])};"
|
401
|
+
end
|
402
|
+
|
403
|
+
caller_code = proc { |name| "
|
404
|
+
static VALUE #{name}(VALUE param) {
|
405
|
+
#{@locals_struct} *plocals = (void*)param;
|
406
|
+
VALUE last_expression;
|
407
|
+
|
408
|
+
#{str}
|
409
|
+
|
410
|
+
return last_expression;
|
411
|
+
}
|
412
|
+
"
|
413
|
+
}
|
414
|
+
|
415
|
+
anonymous_function(caller_code) + "((VALUE)#{locals_pointer})"
|
416
|
+
end
|
417
|
+
|
418
|
+
def to_c_return(tree)
|
419
|
+
"return #{to_c(tree[1])};\n"
|
420
|
+
end
|
421
|
+
|
422
|
+
def to_c_lit(tree)
|
423
|
+
"(VALUE)#{tree[1].internal_value}"
|
424
|
+
end
|
425
|
+
|
426
|
+
def to_c_nil(tree)
|
427
|
+
"Qnil"
|
428
|
+
end
|
429
|
+
|
430
|
+
def to_c_str(tree)
|
431
|
+
"(VALUE)#{tree[1].internal_value}"
|
432
|
+
end
|
433
|
+
|
434
|
+
def to_c_hash(tree)
|
435
|
+
|
436
|
+
hash_aset_code = ""
|
437
|
+
(0..(tree.size-3)/2).each do |i|
|
438
|
+
strkey = to_c tree[1 + i * 2]
|
439
|
+
strvalue = to_c tree[2 + i * 2]
|
440
|
+
hash_aset_code << "rb_hash_aset(hash, #{strkey}, #{strvalue});"
|
441
|
+
end
|
442
|
+
|
443
|
+
wrapper_func = proc { |name| "
|
444
|
+
static VALUE #{name}(VALUE value_params) {
|
445
|
+
#{@locals_struct} *plocals = (void*)value_params;
|
446
|
+
VALUE hash = rb_hash_new();
|
447
|
+
#{hash_aset_code}
|
448
|
+
return hash;
|
449
|
+
}
|
450
|
+
" }
|
451
|
+
|
452
|
+
anonymous_function(wrapper_func) + "((VALUE)#{locals_pointer})"
|
453
|
+
end
|
454
|
+
|
455
|
+
def to_c_array(tree)
|
456
|
+
if tree.size > 1
|
457
|
+
strargs = tree[1..-1].map{|subtree| to_c subtree}.join(",")
|
458
|
+
"rb_ary_new3(#{tree.size-1}, #{strargs})"
|
459
|
+
else
|
460
|
+
"rb_ary_new3(0)"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def to_c_defn(tree)
|
465
|
+
method_name = tree[1]
|
466
|
+
args_tree = tree[2]
|
467
|
+
|
468
|
+
impl_tree = tree[3][1]
|
469
|
+
|
470
|
+
@locals_struct = "struct {
|
471
|
+
#{@locals.map{|l| "VALUE #{l};\n"}.join}
|
472
|
+
#{args_tree[1..-1].map{|arg| "VALUE #{arg};\n"}.join};
|
473
|
+
void* block_function_address;
|
474
|
+
VALUE block_function_param;
|
475
|
+
}"
|
476
|
+
|
477
|
+
@block_struct = "struct {
|
478
|
+
void* block_function_address;
|
479
|
+
void* block_function_param;
|
480
|
+
}"
|
481
|
+
|
482
|
+
str_impl = ""
|
483
|
+
# if impl_tree is a block, implement the last node with a return
|
484
|
+
if impl_tree[0] == :block
|
485
|
+
str_impl = to_c impl_tree
|
486
|
+
else
|
487
|
+
if impl_tree[0] != :return
|
488
|
+
str_impl = str_impl + ";last_expression = #{to_c(impl_tree)};"
|
489
|
+
else
|
490
|
+
str_impl = str_impl + ";#{to_c(impl_tree)};"
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
strargs = if args_tree.size > 1
|
495
|
+
"VALUE block, #{args_tree[1..-1].map{|arg| "VALUE #{arg}" }.join(",") }"
|
496
|
+
else
|
497
|
+
"VALUE block"
|
498
|
+
end
|
499
|
+
|
500
|
+
"VALUE #{@alt_method_name || method_name}(#{strargs}) {
|
501
|
+
#{@locals_struct} locals;
|
502
|
+
#{@locals_struct} *plocals = (void*)&locals;
|
503
|
+
#{@block_struct} *pblock;
|
504
|
+
VALUE last_expression;
|
505
|
+
|
506
|
+
#{args_tree[1..-1].map { |arg|
|
507
|
+
"locals.#{arg} = #{arg};\n"
|
508
|
+
}.join("") }
|
509
|
+
|
510
|
+
locals.self = self;
|
511
|
+
|
512
|
+
pblock = (void*)block;
|
513
|
+
if (pblock) {
|
514
|
+
locals.block_function_address = pblock->block_function_address;
|
515
|
+
locals.block_function_param = (VALUE)pblock->block_function_param;
|
516
|
+
} else {
|
517
|
+
locals.block_function_address = 0;
|
518
|
+
locals.block_function_param = Qnil;
|
519
|
+
}
|
520
|
+
|
521
|
+
return #{str_impl};
|
522
|
+
}"
|
523
|
+
end
|
524
|
+
|
525
|
+
def locals_accessor
|
526
|
+
"plocals->"
|
527
|
+
end
|
528
|
+
|
529
|
+
def locals_pointer
|
530
|
+
"plocals"
|
531
|
+
end
|
532
|
+
|
533
|
+
def to_c_gvar(tree)
|
534
|
+
"rb_gvar_get((struct global_entry*)0x#{global_entry(tree[1]).to_s(16)})"
|
535
|
+
end
|
536
|
+
|
537
|
+
def to_c_gasgn(tree)
|
538
|
+
"_rb_gvar_set((void*)0x#{global_entry(tree[1]).to_s(16)}, #{to_c tree[2]})"
|
539
|
+
end
|
540
|
+
|
541
|
+
def to_c_ivar(tree)
|
542
|
+
"rb_ivar_get(#{locals_accessor}self,#{tree[1].to_i})"
|
543
|
+
end
|
544
|
+
|
545
|
+
def to_c_iasgn(tree)
|
546
|
+
"_rb_ivar_set(#{locals_accessor}self,#{tree[1].to_i},#{to_c tree[2]})"
|
547
|
+
end
|
548
|
+
|
549
|
+
def to_c_lasgn(tree)
|
550
|
+
if options[:validate_lvar_types]
|
551
|
+
klass = @infer_lvar_map[tree[1]]
|
552
|
+
if klass
|
553
|
+
|
554
|
+
verify_type_function = proc { |name| "
|
555
|
+
static VALUE #{name}(VALUE arg) {
|
556
|
+
if (CLASS_OF(arg)!=#{klass.internal_value}) rb_raise(#{TypeMismatchAssignmentException.internal_value}, \"Illegal assignment at runtime (type mismatch)\");
|
557
|
+
return arg;
|
558
|
+
}
|
559
|
+
"
|
560
|
+
}
|
561
|
+
|
562
|
+
|
563
|
+
"_lvar_assing(&#{locals_accessor}#{tree[1]}, #{anonymous_function(verify_type_function)}(#{to_c tree[2]}))"
|
564
|
+
else
|
565
|
+
"_lvar_assing(&#{locals_accessor}#{tree[1]},#{to_c tree[2]})"
|
566
|
+
end
|
567
|
+
else
|
568
|
+
"_lvar_assing(&#{locals_accessor}#{tree[1]},#{to_c tree[2]})"
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def to_c_lvar(tree)
|
573
|
+
locals_accessor + tree[1].to_s
|
574
|
+
end
|
575
|
+
|
576
|
+
def to_c_self(tree)
|
577
|
+
locals_accessor + "self"
|
578
|
+
end
|
579
|
+
|
580
|
+
def to_c_false(tree)
|
581
|
+
"Qfalse"
|
582
|
+
end
|
583
|
+
|
584
|
+
def to_c_true(tree)
|
585
|
+
"Qtrue"
|
586
|
+
end
|
587
|
+
|
588
|
+
def to_c_and(tree)
|
589
|
+
"(RTEST(#{to_c tree[1]}) && RTEST(#{to_c tree[2]})) ? Qtrue : Qfalse"
|
590
|
+
end
|
591
|
+
|
592
|
+
def to_c_or(tree)
|
593
|
+
"(RTEST(#{to_c tree[1]}) || RTEST(#{to_c tree[2]})) ? Qtrue : Qfalse"
|
594
|
+
end
|
595
|
+
|
596
|
+
def to_c_not(tree)
|
597
|
+
"RTEST(#{to_c tree[1]}) ? Qfalse : Qtrue"
|
598
|
+
end
|
599
|
+
|
600
|
+
def to_c_if(tree)
|
601
|
+
condition_tree = tree[1]
|
602
|
+
impl_tree = tree[2]
|
603
|
+
else_tree = tree[3]
|
604
|
+
code = "if (RTEST(#{to_c condition_tree})) {
|
605
|
+
last_expression = #{to_c impl_tree};
|
606
|
+
}
|
607
|
+
"
|
608
|
+
|
609
|
+
if (else_tree)
|
610
|
+
code = code + " else {
|
611
|
+
last_expression = #{to_c else_tree};
|
612
|
+
}
|
613
|
+
"
|
614
|
+
end
|
615
|
+
|
616
|
+
caller_code = proc { |name| "
|
617
|
+
static VALUE #{name}(VALUE param) {
|
618
|
+
#{@locals_struct} *plocals = (void*)param;
|
619
|
+
VALUE last_expression = Qnil;
|
620
|
+
|
621
|
+
#{code};
|
622
|
+
|
623
|
+
return last_expression;
|
624
|
+
}
|
625
|
+
"
|
626
|
+
}
|
627
|
+
|
628
|
+
anonymous_function(caller_code) + "((VALUE)#{locals_pointer})"
|
629
|
+
end
|
630
|
+
|
631
|
+
def to_c_call(tree)
|
632
|
+
recv = tree[1]
|
633
|
+
mname = tree[2]
|
634
|
+
args = tree[3]
|
635
|
+
|
636
|
+
directive_code = directive(tree)
|
637
|
+
if directive_code
|
638
|
+
return directive_code
|
639
|
+
end
|
640
|
+
|
641
|
+
strargs = args[1..-1].map{|arg| to_c arg}.join(",")
|
642
|
+
|
643
|
+
argnum = args.size - 1
|
644
|
+
|
645
|
+
recv = recv || s(:self)
|
646
|
+
|
647
|
+
recvtype = infer_type(recv)
|
648
|
+
|
649
|
+
if recvtype
|
650
|
+
|
651
|
+
address = nil
|
652
|
+
mobject = nil
|
653
|
+
|
654
|
+
inference_complete = true
|
655
|
+
signature = [recvtype]
|
656
|
+
|
657
|
+
args[1..-1].each do |arg|
|
658
|
+
argtype = infer_type(arg)
|
659
|
+
if argtype
|
660
|
+
signature << argtype
|
661
|
+
else
|
662
|
+
inference_complete = false
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
convention = nil
|
667
|
+
|
668
|
+
if recvtype.respond_to? :method_tree and inference_complete
|
669
|
+
|
670
|
+
if recvtype.method_tree[tree[2]]
|
671
|
+
mobject = recvtype.build(signature, tree[2])
|
672
|
+
convention = :fastruby
|
673
|
+
else
|
674
|
+
mobject = recvtype.instance_method(tree[2])
|
675
|
+
convention = :cruby
|
676
|
+
end
|
677
|
+
else
|
678
|
+
mobject = recvtype.instance_method(tree[2])
|
679
|
+
convention = :cruby
|
680
|
+
end
|
681
|
+
|
682
|
+
address = getaddress(mobject)
|
683
|
+
len = getlen(mobject)
|
684
|
+
|
685
|
+
extraargs = ""
|
686
|
+
extraargs = ", Qfalse" if convention == :fastruby
|
687
|
+
|
688
|
+
if address then
|
689
|
+
if argnum == 0
|
690
|
+
value_cast = "VALUE"
|
691
|
+
value_cast = value_cast + ", VALUE" if convention == :fastruby
|
692
|
+
|
693
|
+
if convention == :fastruby
|
694
|
+
"((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{to_c recv}, Qfalse)"
|
695
|
+
else
|
696
|
+
|
697
|
+
str_incall_args = nil
|
698
|
+
if len == -1
|
699
|
+
str_incall_args = "0, (VALUE[]){}, recv"
|
700
|
+
value_cast = "int,VALUE*,VALUE"
|
701
|
+
elsif len == -2
|
702
|
+
str_incall_args = "recv, rb_ary_new4(#{})"
|
703
|
+
value_cast = "VALUE,VALUE"
|
704
|
+
else
|
705
|
+
str_incall_args = "recv"
|
706
|
+
end
|
707
|
+
|
708
|
+
wrapper_func = proc { |name| "
|
709
|
+
static VALUE #{name}(VALUE recv) {
|
710
|
+
// call to #{recvtype}##{mname}
|
711
|
+
if (rb_block_given_p()) {
|
712
|
+
// no passing block, recall
|
713
|
+
return rb_funcall(recv, #{tree[2].to_i}, 0);
|
714
|
+
} else {
|
715
|
+
return ((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{str_incall_args});
|
716
|
+
}
|
717
|
+
}
|
718
|
+
" }
|
719
|
+
|
720
|
+
anonymous_function(wrapper_func) + "(#{to_c(recv)})"
|
721
|
+
|
722
|
+
end
|
723
|
+
else
|
724
|
+
value_cast = ( ["VALUE"]*(args.size) ).join(",")
|
725
|
+
value_cast = value_cast + ", VALUE" if convention == :fastruby
|
726
|
+
|
727
|
+
wrapper_func = nil
|
728
|
+
if convention == :fastruby
|
729
|
+
"((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{to_c recv}, Qfalse, #{strargs})"
|
730
|
+
else
|
731
|
+
|
732
|
+
str_incall_args = nil
|
733
|
+
if len == -1
|
734
|
+
str_incall_args = "#{argnum}, (VALUE[]){#{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")}}, recv"
|
735
|
+
value_cast = "int,VALUE*,VALUE"
|
736
|
+
elsif len == -2
|
737
|
+
str_incall_args = "recv, rb_ary_new4(#{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")})"
|
738
|
+
value_cast = "VALUE,VALUE"
|
739
|
+
else
|
740
|
+
str_incall_args = "recv, #{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")}"
|
741
|
+
end
|
742
|
+
|
743
|
+
wrapper_func = proc { |name| "
|
744
|
+
static VALUE #{name}(VALUE recv, #{ (1..argnum).map{|x| "VALUE _arg"+x.to_s }.join(",")} ) {
|
745
|
+
// call to #{recvtype}##{mname}
|
746
|
+
if (rb_block_given_p()) {
|
747
|
+
// no passing block, recall
|
748
|
+
return rb_funcall(recv, #{tree[2].to_i}, #{argnum}, #{ (1..argnum).map{|x| "_arg"+x.to_s }.join(",")});
|
749
|
+
} else {
|
750
|
+
return ((VALUE(*)(#{value_cast}))0x#{address.to_s(16)})(#{str_incall_args});
|
751
|
+
}
|
752
|
+
}
|
753
|
+
" }
|
754
|
+
|
755
|
+
anonymous_function(wrapper_func) + "(#{to_c(recv)}, #{strargs})"
|
756
|
+
end
|
757
|
+
end
|
758
|
+
else
|
759
|
+
|
760
|
+
if argnum == 0
|
761
|
+
"rb_funcall(#{to_c recv}, #{tree[2].to_i}, 0)"
|
762
|
+
else
|
763
|
+
"rb_funcall(#{to_c recv}, #{tree[2].to_i}, #{argnum}, #{strargs} )"
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
else
|
768
|
+
if argnum == 0
|
769
|
+
"rb_funcall(#{to_c recv}, #{tree[2].to_i}, 0)"
|
770
|
+
else
|
771
|
+
"rb_funcall(#{to_c recv}, #{tree[2].to_i}, #{argnum}, #{strargs} )"
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
def to_c_while(tree)
|
777
|
+
caller_code = proc { |name| "
|
778
|
+
static VALUE #{name}(VALUE param) {
|
779
|
+
#{@locals_struct} *plocals = (void*)param;
|
780
|
+
VALUE last_expression;
|
781
|
+
|
782
|
+
while (#{to_c tree[1]}) {
|
783
|
+
#{to_c tree[2]};
|
784
|
+
}
|
785
|
+
|
786
|
+
return Qnil;
|
787
|
+
}
|
788
|
+
"
|
789
|
+
}
|
790
|
+
|
791
|
+
anonymous_function(caller_code) + "((VALUE)#{locals_pointer})"
|
792
|
+
end
|
793
|
+
|
794
|
+
def infer_type(recv)
|
795
|
+
if recv[0] == :call
|
796
|
+
if recv[2] == :infer
|
797
|
+
eval(recv[3].last.last.to_s)
|
798
|
+
end
|
799
|
+
elsif recv[0] == :lvar
|
800
|
+
@infer_lvar_map[recv[1]]
|
801
|
+
elsif recv[0] == :self
|
802
|
+
@infer_self
|
803
|
+
elsif recv[0] == :str or recv[0] == :lit
|
804
|
+
recv[1].class
|
805
|
+
else
|
806
|
+
nil
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
def with_extra_inference(extra_inference)
|
811
|
+
previous_infer_lvar_map = @infer_lvar_map
|
812
|
+
begin
|
813
|
+
@infer_lvar_map = @infer_lvar_map.merge(extra_inference)
|
814
|
+
yield
|
815
|
+
ensure
|
816
|
+
@infer_lvar_map = previous_infer_lvar_map
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
def directive(tree)
|
821
|
+
recv = tree[1]
|
822
|
+
mname = tree[2]
|
823
|
+
args = tree[3]
|
824
|
+
|
825
|
+
if mname == :infer
|
826
|
+
return to_c(recv)
|
827
|
+
elsif mname == :lvar_type
|
828
|
+
lvar_name = args[1][1] || args[1][2]
|
829
|
+
lvar_type = eval(args[2][1].to_s)
|
830
|
+
|
831
|
+
@infer_lvar_map[lvar_name] = lvar_type
|
832
|
+
return ""
|
833
|
+
elsif mname == :block_given?
|
834
|
+
return "#{locals_accessor}block_function_address == 0 ? Qfalse : Qtrue"
|
835
|
+
elsif mname == :inline_c
|
836
|
+
code = args[1][1]
|
837
|
+
|
838
|
+
caller_code = proc { |name| "
|
839
|
+
static VALUE #{name}(VALUE param) {
|
840
|
+
#{@locals_struct} *plocals = (void*)param;
|
841
|
+
#{code};
|
842
|
+
return Qnil;
|
843
|
+
}
|
844
|
+
"
|
845
|
+
}
|
846
|
+
|
847
|
+
return anonymous_function(caller_code)+"((VALUE)plocals)"
|
848
|
+
else
|
849
|
+
nil
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
inline :C do |builder|
|
854
|
+
builder.include "<node.h>"
|
855
|
+
builder.c "VALUE getaddress(VALUE method) {
|
856
|
+
struct METHOD {
|
857
|
+
VALUE klass, rklass;
|
858
|
+
VALUE recv;
|
859
|
+
ID id, oid;
|
860
|
+
int safe_level;
|
861
|
+
NODE *body;
|
862
|
+
};
|
863
|
+
|
864
|
+
struct METHOD *data;
|
865
|
+
Data_Get_Struct(method, struct METHOD, data);
|
866
|
+
|
867
|
+
if (nd_type(data->body) == NODE_CFUNC) {
|
868
|
+
return INT2FIX(data->body->nd_cfnc);
|
869
|
+
}
|
870
|
+
|
871
|
+
return Qnil;
|
872
|
+
}"
|
873
|
+
|
874
|
+
builder.c "VALUE getlen(VALUE method) {
|
875
|
+
struct METHOD {
|
876
|
+
VALUE klass, rklass;
|
877
|
+
VALUE recv;
|
878
|
+
ID id, oid;
|
879
|
+
int safe_level;
|
880
|
+
NODE *body;
|
881
|
+
};
|
882
|
+
|
883
|
+
struct METHOD *data;
|
884
|
+
Data_Get_Struct(method, struct METHOD, data);
|
885
|
+
|
886
|
+
if (nd_type(data->body) == NODE_CFUNC) {
|
887
|
+
return INT2FIX(data->body->nd_argc);
|
888
|
+
}
|
889
|
+
|
890
|
+
return Qnil;
|
891
|
+
}"
|
892
|
+
|
893
|
+
builder.c "VALUE global_entry(VALUE global_id) {
|
894
|
+
ID id = SYM2ID(global_id);
|
895
|
+
struct global_entry* entry;
|
896
|
+
|
897
|
+
entry = rb_global_entry(id);
|
898
|
+
return INT2FIX(entry);
|
899
|
+
}
|
900
|
+
"
|
901
|
+
end
|
902
|
+
end
|
903
|
+
end
|