ruby2cext 0.2.0
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 +27 -0
- data/README +44 -0
- data/bin/rb2cx +178 -0
- data/doc/eval2c.html +281 -0
- data/doc/index.html +218 -0
- data/doc/limitations.html +581 -0
- data/doc/optimizations.html +222 -0
- data/doc/rb2cx.html +151 -0
- data/doc/style.css +27 -0
- data/lib/ruby2cext/c_function.rb +621 -0
- data/lib/ruby2cext/common_node_comp.rb +1409 -0
- data/lib/ruby2cext/compiler.rb +242 -0
- data/lib/ruby2cext/error.rb +15 -0
- data/lib/ruby2cext/eval2c.rb +129 -0
- data/lib/ruby2cext/parser.rb +36 -0
- data/lib/ruby2cext/plugin.rb +24 -0
- data/lib/ruby2cext/plugins/builtin_methods.rb +820 -0
- data/lib/ruby2cext/plugins/case_optimize.rb +105 -0
- data/lib/ruby2cext/plugins/const_cache.rb +38 -0
- data/lib/ruby2cext/plugins/inline_methods.rb +69 -0
- data/lib/ruby2cext/plugins/require_include.rb +71 -0
- data/lib/ruby2cext/plugins/warnings.rb +123 -0
- data/lib/ruby2cext/scopes.rb +227 -0
- data/lib/ruby2cext/str_to_c_strlit.rb +12 -0
- data/lib/ruby2cext/tools.rb +84 -0
- data/lib/ruby2cext/version.rb +22 -0
- data/testfiles/bench.rb +116 -0
- data/testfiles/eval2c/test_eval2c.rb +37 -0
- data/testfiles/test.rb +615 -0
- data/testfiles/vmode_test.rb +73 -0
- data/testfiles/warn_test.rb +35 -0
- metadata +87 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
require "ruby2cext/plugin"
|
3
|
+
require "ruby2cext/tools"
|
4
|
+
|
5
|
+
module Ruby2CExtension::Plugins
|
6
|
+
|
7
|
+
class CaseOptimize < Ruby2CExtension::Plugin
|
8
|
+
|
9
|
+
include Ruby2CExtension::Tools::EnsureNodeTypeMixin
|
10
|
+
|
11
|
+
def initialize(compiler)
|
12
|
+
super
|
13
|
+
compiler.add_preprocessor(:case) { |cfun, node|
|
14
|
+
handle_case(cfun, node.last) || node
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def fixnum?(node)
|
19
|
+
Array === node && node.first == :lit && Fixnum === node.last[:lit]
|
20
|
+
end
|
21
|
+
|
22
|
+
def fixed_immediate?(node)
|
23
|
+
# checks if the node is optimizable and if yes returns the equivalent C value
|
24
|
+
if fixnum?(node)
|
25
|
+
"LONG2FIX(#{node.last[:lit].inspect})"
|
26
|
+
elsif Array === node
|
27
|
+
case node.first
|
28
|
+
when :nil
|
29
|
+
"Qnil"
|
30
|
+
when :true
|
31
|
+
"Qtrue"
|
32
|
+
when :false
|
33
|
+
"Qfalse"
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle_case(cfun, hash)
|
43
|
+
cur_when = hash[:body]
|
44
|
+
fallback_whens = []
|
45
|
+
opt_cases = []
|
46
|
+
while cur_when.first == :when
|
47
|
+
ensure_node_type(head = cur_when.last[:head], :array)
|
48
|
+
cases = head.last.map { |wn| fixed_immediate?(wn) }
|
49
|
+
break unless cases.all?
|
50
|
+
case_c_code = cases.map { |c| "case #{c}:" }.join("\n")
|
51
|
+
fixnum_whens = head.last.select { |wn| fixnum?(wn) }
|
52
|
+
unless fixnum_whens.empty?
|
53
|
+
goto_label = compiler.un("case_opt_label")
|
54
|
+
case_c_code << "\n#{goto_label}:"
|
55
|
+
fallback_whens << [:when, {
|
56
|
+
:body => "Qnil;\ngoto #{goto_label}", # TODO: evil, depends on impl. details of comp_case/handle_when
|
57
|
+
:head => [:array, fixnum_whens]
|
58
|
+
}]
|
59
|
+
end
|
60
|
+
opt_cases << [case_c_code, cur_when.last[:body]]
|
61
|
+
cur_when = cur_when.last[:next] || [:nil, {}]
|
62
|
+
end
|
63
|
+
return nil if opt_cases.empty? # nothing to optimize
|
64
|
+
rest = cur_when
|
65
|
+
if rest.first == :when # some whens are left construct a complete new case node
|
66
|
+
rest = [:case, {:head => "case_opt_val", :body => rest}]
|
67
|
+
end
|
68
|
+
cfun.instance_eval {
|
69
|
+
c_scope_res {
|
70
|
+
l "VALUE case_opt_val;"
|
71
|
+
l "case_opt_val = #{comp(hash[:head])};"
|
72
|
+
l "switch (case_opt_val) {"
|
73
|
+
opt_cases.each { |(case_code, body_node)|
|
74
|
+
l case_code
|
75
|
+
assign_res(comp(body_node))
|
76
|
+
l "break;"
|
77
|
+
}
|
78
|
+
l "default:"
|
79
|
+
if fallback_whens.empty?
|
80
|
+
assign_res(comp(rest))
|
81
|
+
else
|
82
|
+
# link the fallback_whens
|
83
|
+
fallback_whens.each_with_index { |wn, i|
|
84
|
+
fallback_whens[i - 1].last[:next] = wn if i > 0
|
85
|
+
}
|
86
|
+
fallback_whens.last.last[:next] = "Qundef"
|
87
|
+
c_if("!FIXNUM_P(case_opt_val)") {
|
88
|
+
assign_res(comp_case({:head => "case_opt_val", :body => fallback_whens.first}))
|
89
|
+
}
|
90
|
+
c_else {
|
91
|
+
assign_res("Qundef")
|
92
|
+
}
|
93
|
+
c_if("res == Qundef") {
|
94
|
+
assign_res(comp(rest))
|
95
|
+
}
|
96
|
+
end
|
97
|
+
l "}"
|
98
|
+
"res"
|
99
|
+
}
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
require "ruby2cext/plugin"
|
3
|
+
|
4
|
+
module Ruby2CExtension::Plugins
|
5
|
+
|
6
|
+
class ConstCache < Ruby2CExtension::Plugin
|
7
|
+
def initialize(compiler)
|
8
|
+
super
|
9
|
+
compiler.add_preprocessor(:const) { |cfun, node|
|
10
|
+
cfun.instance_eval {
|
11
|
+
c_static_once {
|
12
|
+
comp_const(node.last)
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
compiler.add_preprocessor(:colon2) { |cfun, node|
|
17
|
+
mid = node.last[:mid]
|
18
|
+
if mid.to_s[0,1].downcase != mid.to_s[0,1] # then it is a constant
|
19
|
+
cfun.instance_eval {
|
20
|
+
c_static_once {
|
21
|
+
comp_colon2(node.last)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
else
|
25
|
+
node
|
26
|
+
end
|
27
|
+
}
|
28
|
+
compiler.add_preprocessor(:colon3) { |cfun, node|
|
29
|
+
cfun.instance_eval {
|
30
|
+
c_static_once {
|
31
|
+
comp_colon3(node.last)
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
require "ruby2cext/plugin"
|
3
|
+
|
4
|
+
module Ruby2CExtension::Plugins
|
5
|
+
|
6
|
+
class InlineMethods < Ruby2CExtension::Plugin
|
7
|
+
|
8
|
+
METHODS = {
|
9
|
+
[:nil?, 0] => proc { |cfun, recv, args|
|
10
|
+
"(Qnil == (#{recv}) ? Qtrue : Qfalse)"
|
11
|
+
},
|
12
|
+
[:equal?, 1] => proc { |cfun, recv, args|
|
13
|
+
cfun.instance_eval {
|
14
|
+
c_scope_res {
|
15
|
+
l "VALUE recv = #{recv};"
|
16
|
+
"(recv == (#{comp(args[0])}) ? Qtrue : Qfalse)"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
},
|
20
|
+
:__send__ => proc { |cfun, recv, args|
|
21
|
+
unless args
|
22
|
+
raise Ruby2CExtension::Ruby2CExtError, "inlining #__send__ without arguments is not allowed"
|
23
|
+
end
|
24
|
+
cfun.instance_eval {
|
25
|
+
add_helper <<-EOC
|
26
|
+
static void inline_method_send_no_method_name() {
|
27
|
+
rb_raise(rb_eArgError, "no method name given");
|
28
|
+
}
|
29
|
+
EOC
|
30
|
+
c_scope_res {
|
31
|
+
l "VALUE recv = #{recv};"
|
32
|
+
build_args(args)
|
33
|
+
l "if (argc == 0) inline_method_send_no_method_name();"
|
34
|
+
"rb_funcall2(recv, rb_to_id(argv[0]), argc - 1, (&(argv[0])) + 1)"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
},
|
38
|
+
}
|
39
|
+
|
40
|
+
def initialize(compiler)
|
41
|
+
super
|
42
|
+
self_node = [:self, {}]
|
43
|
+
compiler.add_preprocessor(:vcall) { |cfun, node|
|
44
|
+
handle(cfun, node.last[:mid], self_node, false) || node
|
45
|
+
}
|
46
|
+
compiler.add_preprocessor(:fcall) { |cfun, node|
|
47
|
+
handle(cfun, node.last[:mid], self_node, node.last[:args]) || node
|
48
|
+
}
|
49
|
+
compiler.add_preprocessor(:call) { |cfun, node|
|
50
|
+
handle(cfun, node.last[:mid], node.last[:recv], node.last[:args]) || node
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def handle(cfun, method, recv, args)
|
55
|
+
if (pr = METHODS[method])
|
56
|
+
pr.call(cfun, cfun.comp(recv), args)
|
57
|
+
else
|
58
|
+
args ||= [:array, []]
|
59
|
+
if args.first == :array && (pr = METHODS[[method, args.last.size]])
|
60
|
+
pr.call(cfun, cfun.comp(recv), args.last)
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
require "ruby2cext/plugin"
|
3
|
+
|
4
|
+
module Ruby2CExtension::Plugins
|
5
|
+
|
6
|
+
class RequireInclude < Ruby2CExtension::Plugin
|
7
|
+
|
8
|
+
attr_reader :include_paths
|
9
|
+
|
10
|
+
def initialize(compiler, include_paths, ignore_files = nil)
|
11
|
+
super(compiler)
|
12
|
+
@include_paths = include_paths
|
13
|
+
done = {}
|
14
|
+
if ignore_files
|
15
|
+
ignore_files.each { |file|
|
16
|
+
done[File.expand_path(file)] = true
|
17
|
+
}
|
18
|
+
end
|
19
|
+
compiler.add_preprocessor(:fcall) { |cfun, node|
|
20
|
+
hash = node.last
|
21
|
+
if hash[:mid] == :require &&
|
22
|
+
(args = hash[:args]) &&
|
23
|
+
args.first == :array &&
|
24
|
+
(args = args.last).size == 1 &&
|
25
|
+
Array === args[0] &&
|
26
|
+
args[0].first == :str &&
|
27
|
+
(file = search_file(args[0].last[:lit]))
|
28
|
+
unless done[File.expand_path(file)]
|
29
|
+
done[File.expand_path(file)] = true
|
30
|
+
cfun.compiler.log "including require'd file: #{file}"
|
31
|
+
cfun.instance_eval {
|
32
|
+
add_helper <<-EOC
|
33
|
+
static NODE * find_top_cref(NODE *cref) {
|
34
|
+
while (cref && cref->nd_next) cref = cref->nd_next;
|
35
|
+
return cref;
|
36
|
+
}
|
37
|
+
EOC
|
38
|
+
c_scope {
|
39
|
+
l "NODE *top_cref = find_top_cref(#{get_cref});"
|
40
|
+
l "static int done = 0;"
|
41
|
+
c_if("!done") {
|
42
|
+
l "done = 1;"
|
43
|
+
compiler.rb_file_to_toplevel_functions(IO.read(file), file).each { |tlfn|
|
44
|
+
l "#{tlfn}(org_ruby_top_self, top_cref);"
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
end
|
50
|
+
"Qtrue"
|
51
|
+
else
|
52
|
+
node
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def search_file(req_str)
|
58
|
+
req_str = req_str.dup
|
59
|
+
unless req_str =~ /\.rb\z/
|
60
|
+
req_str << ".rb"
|
61
|
+
end
|
62
|
+
res = false
|
63
|
+
include_paths.map { |path|
|
64
|
+
File.join(path, req_str)
|
65
|
+
}.find { |file|
|
66
|
+
File.exists? file
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
|
2
|
+
require "ruby2cext/plugin"
|
3
|
+
|
4
|
+
module Ruby2CExtension::Plugins
|
5
|
+
|
6
|
+
# TODO: Module.nesting, Module.constants, Kernel#autoload and Kernel#autoload?
|
7
|
+
|
8
|
+
class Warnings < Ruby2CExtension::Plugin
|
9
|
+
|
10
|
+
CALL_TYPES = [:vcall, :fcall, :call]
|
11
|
+
|
12
|
+
exp = "might not behave as expected"
|
13
|
+
exp2 = "will not behave as expected"
|
14
|
+
vis_exp = "visibility of the defined %s might not be as expected"
|
15
|
+
|
16
|
+
VCALL_WARNINGS = {
|
17
|
+
:binding => exp2,
|
18
|
+
:local_variables => exp2,
|
19
|
+
:callcc => exp,
|
20
|
+
}
|
21
|
+
|
22
|
+
FCALL_WARNINGS = {
|
23
|
+
:set_trace_func => exp,
|
24
|
+
:eval => exp,
|
25
|
+
:define_method => vis_exp % "method",
|
26
|
+
:attr => vis_exp % "attribute",
|
27
|
+
:attr_reader => vis_exp % "attribute(s)",
|
28
|
+
:attr_writer => vis_exp % "attribute(s)",
|
29
|
+
:attr_accessor => vis_exp % "attribute(s)",
|
30
|
+
:instance_eval => exp,
|
31
|
+
:module_eval => exp,
|
32
|
+
:class_eval => exp,
|
33
|
+
}
|
34
|
+
|
35
|
+
CALL_WARNINGS = {
|
36
|
+
:arity => "will return -1 for all methods defined in compiled Ruby code",
|
37
|
+
:instance_eval => exp,
|
38
|
+
:module_eval => exp,
|
39
|
+
:class_eval => exp,
|
40
|
+
}
|
41
|
+
|
42
|
+
BLOCK_PASS_WARNINGS = {
|
43
|
+
:define_method => true,
|
44
|
+
:instance_eval => true,
|
45
|
+
:module_eval => true,
|
46
|
+
:class_eval => true,
|
47
|
+
}
|
48
|
+
|
49
|
+
NO_WARNING_WITH_ITER = {
|
50
|
+
:instance_eval => true,
|
51
|
+
:module_eval => true,
|
52
|
+
:class_eval => true,
|
53
|
+
}
|
54
|
+
|
55
|
+
def initialize(compiler)
|
56
|
+
super
|
57
|
+
CALL_TYPES.each { |ct|
|
58
|
+
ct_sym = "check_#{ct}".to_sym
|
59
|
+
compiler.add_preprocessor(ct) { |cfun, node|
|
60
|
+
send(ct_sym, node.last)
|
61
|
+
node
|
62
|
+
}
|
63
|
+
}
|
64
|
+
[:iter, :block_pass].each { |it|
|
65
|
+
it_sym = "check_#{it}".to_sym
|
66
|
+
compiler.add_preprocessor(it) { |cfun, node|
|
67
|
+
if node.last[:iter] && CALL_TYPES.include?(node.last[:iter].first)
|
68
|
+
send(it_sym, node.last[:iter])
|
69
|
+
end
|
70
|
+
node
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def warn(str, node_hash = {})
|
76
|
+
lstr = ""
|
77
|
+
if (n = node_hash[:node])
|
78
|
+
lstr << "#{n.file}:#{n.line}: "
|
79
|
+
end
|
80
|
+
lstr << "warning: " << str
|
81
|
+
compiler.log(lstr, true)
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_vcall(hash)
|
85
|
+
if (m = VCALL_WARNINGS[hash[:mid]])
|
86
|
+
warn("#{hash[:mid]}: #{m}", hash)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def check_fcall(hash)
|
91
|
+
if (m = FCALL_WARNINGS[hash[:mid]])
|
92
|
+
warn("#{hash[:mid]}: #{m}", hash)
|
93
|
+
else
|
94
|
+
unless hash[:args]
|
95
|
+
check_vcall(hash)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_call(hash)
|
101
|
+
if (m = CALL_WARNINGS[hash[:mid]])
|
102
|
+
warn("#{hash[:mid]}: #{m}", hash)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def check_iter(iter_node)
|
107
|
+
mid = iter_node.last[:mid]
|
108
|
+
unless NO_WARNING_WITH_ITER[mid]
|
109
|
+
send("check_#{iter_node.first}", iter_node.last)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def check_block_pass(iter_node)
|
114
|
+
mid = iter_node.last[:mid]
|
115
|
+
if BLOCK_PASS_WARNINGS[mid]
|
116
|
+
warn("#{mid} with block_pass: might not behave as expected", iter_node.last)
|
117
|
+
else
|
118
|
+
send("check_#{iter_node.first}", iter_node.last)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
|
2
|
+
require "ruby2cext/error"
|
3
|
+
|
4
|
+
module Ruby2CExtension
|
5
|
+
|
6
|
+
module Scopes
|
7
|
+
class Scope
|
8
|
+
VMODES = [:public, :private, :protected, :module_function].freeze
|
9
|
+
|
10
|
+
def initialize(tbl, vmode_methods = VMODES, private_vmode = false)
|
11
|
+
@vmode_methods = vmode_methods
|
12
|
+
@vmode = private_vmode ? :private : :public
|
13
|
+
@tbl = tbl || []
|
14
|
+
@closure_tbl = nil # must be set by user
|
15
|
+
end
|
16
|
+
attr_reader :tbl, :vmode, :vmode_methods
|
17
|
+
attr_accessor :need_heap, :closure_tbl
|
18
|
+
|
19
|
+
def get_lvar_idx(i)
|
20
|
+
raise Ruby2CExtError, "wrong lvar index: #{i}" unless i >= 0 && i < tbl.size
|
21
|
+
"lvar[#{i}]"
|
22
|
+
end
|
23
|
+
def get_lvar(sym)
|
24
|
+
raise Ruby2CExtError, "unknown lvar: #{sym}" unless (i = tbl.index(sym))
|
25
|
+
get_lvar_idx(i)
|
26
|
+
end
|
27
|
+
def get_lvar_ary
|
28
|
+
self.need_heap = true
|
29
|
+
"lvar_ary"
|
30
|
+
end
|
31
|
+
def get_dvar(arg)
|
32
|
+
raise Ruby2CExtError, "dvars not available here"
|
33
|
+
end
|
34
|
+
alias get_dvar_curr get_dvar
|
35
|
+
alias get_dvar_ary get_dvar
|
36
|
+
|
37
|
+
def vmode_method?(method_id)
|
38
|
+
if (res = vmode_methods.include?(method_id))
|
39
|
+
@vmode = method_id
|
40
|
+
end
|
41
|
+
res
|
42
|
+
end
|
43
|
+
def vmode_def_fun
|
44
|
+
case vmode
|
45
|
+
when :protected, :private
|
46
|
+
"rb_define_#{vmode}_method"
|
47
|
+
when :public
|
48
|
+
"rb_define_method"
|
49
|
+
when :module_function
|
50
|
+
"rb_define_module_function"
|
51
|
+
else
|
52
|
+
raise Ruby2CExtError::Bug, "unknown vmode: #{@vmode}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_dyna_scope
|
57
|
+
DynaScope.new(self, nil, 1)
|
58
|
+
end
|
59
|
+
|
60
|
+
def var_ptr_for_wrap
|
61
|
+
tbl.empty? ? nil : "lvar"
|
62
|
+
end
|
63
|
+
|
64
|
+
def init_c_code
|
65
|
+
return nil if tbl.empty?
|
66
|
+
res = []
|
67
|
+
if need_heap
|
68
|
+
res << "VALUE *lvar;"
|
69
|
+
res << "VALUE lvar_ary = rb_ary_new2(#{tbl.size});"
|
70
|
+
res << "rb_mem_clear(RARRAY(lvar_ary)->ptr, #{tbl.size});"
|
71
|
+
res << "RARRAY(lvar_ary)->len = #{tbl.size};"
|
72
|
+
res << "lvar = RARRAY(lvar_ary)->ptr;"
|
73
|
+
else
|
74
|
+
res << "VALUE lvar[#{tbl.size}];\nrb_mem_clear(lvar, #{tbl.size});"
|
75
|
+
end
|
76
|
+
res.join("\n")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class DynaScope
|
81
|
+
def initialize(base_scope, outer_scope, depth)
|
82
|
+
@base_scope = base_scope
|
83
|
+
@outer_scope = outer_scope
|
84
|
+
@depth = depth
|
85
|
+
@closure_tbl = nil # must be set by user
|
86
|
+
@tbl = []
|
87
|
+
end
|
88
|
+
attr_reader :tbl, :base_scope, :outer_scope, :depth
|
89
|
+
attr_accessor :need_heap, :need_closure, :closure_tbl
|
90
|
+
|
91
|
+
def outer_closure_tbl
|
92
|
+
(outer_scope || base_scope).closure_tbl
|
93
|
+
end
|
94
|
+
def add_closure_need(need)
|
95
|
+
# need is in [1, self.depth) or :lvar
|
96
|
+
unless need == :lvar || (1...depth) === need
|
97
|
+
raise Ruby2CExtError::Bug, "unexpected needed closure vars: #{need}"
|
98
|
+
end
|
99
|
+
self.need_closure = true
|
100
|
+
outer_closure_tbl << need unless outer_closure_tbl.include? need
|
101
|
+
end
|
102
|
+
def get_closure_ary(clos_idx)
|
103
|
+
add_closure_need(clos_idx)
|
104
|
+
"closure[#{outer_closure_tbl.index(clos_idx)}]"
|
105
|
+
end
|
106
|
+
def get_closure_var(clos_idx, var_idx)
|
107
|
+
"RARRAY(#{get_closure_ary(clos_idx)})->ptr[#{var_idx}]"
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_lvar_idx(i)
|
111
|
+
raise Ruby2CExtError, "wrong lvar index: #{i}" unless i >= 0 && i < base_scope.tbl.size
|
112
|
+
get_closure_var(:lvar, i)
|
113
|
+
end
|
114
|
+
def get_lvar(sym)
|
115
|
+
raise Ruby2CExtError, "unknown lvar: #{sym}" unless (i = base_scope.tbl.index(sym))
|
116
|
+
get_lvar_idx(i)
|
117
|
+
end
|
118
|
+
def get_lvar_ary
|
119
|
+
get_closure_ary(:lvar)
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_dvar(sym)
|
123
|
+
if tbl.include?(sym)
|
124
|
+
return get_dvar_curr(sym)
|
125
|
+
end
|
126
|
+
cur = self
|
127
|
+
while (cur = cur.outer_scope)
|
128
|
+
if (i = cur.tbl.index(sym))
|
129
|
+
return get_closure_var(cur.depth, i)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
# Ruby versions <= 1.8.5 should/will never reach here
|
133
|
+
# (otherwise it is a bug in the parser).
|
134
|
+
# But starting after 1.8.5 dvars are only initialized, if there
|
135
|
+
# is an assignment in a sub-block. Because of that we can reach
|
136
|
+
# here, in that case we want a new dvar_curr:
|
137
|
+
get_dvar_curr(sym)
|
138
|
+
end
|
139
|
+
def get_dvar_curr(sym)
|
140
|
+
unless (i = tbl.index(sym))
|
141
|
+
i = tbl.size
|
142
|
+
tbl << sym
|
143
|
+
end
|
144
|
+
"dvar[#{i}]"
|
145
|
+
end
|
146
|
+
def get_dvar_ary(idx)
|
147
|
+
if idx == depth
|
148
|
+
self.need_heap = true
|
149
|
+
"dvar_ary"
|
150
|
+
else
|
151
|
+
get_closure_ary(idx)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# blocks always has public vmode
|
156
|
+
def vmode
|
157
|
+
:public
|
158
|
+
end
|
159
|
+
def vmode_method?(method_id)
|
160
|
+
false
|
161
|
+
end
|
162
|
+
def vmode_def_fun
|
163
|
+
"rb_define_method"
|
164
|
+
end
|
165
|
+
|
166
|
+
def new_dyna_scope
|
167
|
+
DynaScope.new(base_scope, self, depth + 1)
|
168
|
+
end
|
169
|
+
|
170
|
+
def var_ptr_for_wrap
|
171
|
+
tbl.empty? ? nil : "dvar"
|
172
|
+
end
|
173
|
+
|
174
|
+
def init_c_code
|
175
|
+
return nil if tbl.empty?
|
176
|
+
res = []
|
177
|
+
if need_heap
|
178
|
+
res << "VALUE *dvar;"
|
179
|
+
res << "VALUE dvar_ary = rb_ary_new2(#{tbl.size});"
|
180
|
+
res << "rb_mem_clear(RARRAY(dvar_ary)->ptr, #{tbl.size});"
|
181
|
+
res << "RARRAY(dvar_ary)->len = #{tbl.size};"
|
182
|
+
res << "dvar = RARRAY(dvar_ary)->ptr;"
|
183
|
+
else
|
184
|
+
res << "VALUE dvar[#{tbl.size}];\nrb_mem_clear(dvar, #{tbl.size});"
|
185
|
+
end
|
186
|
+
res.join("\n")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class WrappedScope
|
191
|
+
def initialize(base_scope)
|
192
|
+
@base_scope = base_scope
|
193
|
+
end
|
194
|
+
|
195
|
+
# redirect to @base_scope
|
196
|
+
def method_missing(meth, *arg)
|
197
|
+
res = @base_scope.send(meth, *arg)
|
198
|
+
if String === res
|
199
|
+
case res
|
200
|
+
when "lvar_ary"
|
201
|
+
raise Ruby2CExtError::Bug, "unexpected need for lvar_ary in WrappedScope"
|
202
|
+
when "dvar_ary"
|
203
|
+
raise Ruby2CExtError::Bug, "unexpected need for dvar_ary in WrappedScope"
|
204
|
+
when /\bclosure\b/
|
205
|
+
res.gsub!(/\bclosure\b/, "(wrap_ptr->closure)")
|
206
|
+
when /\b[ld]var\b/
|
207
|
+
res.gsub!(/\b[ld]var\b/, "(wrap_ptr->var)")
|
208
|
+
when /^rb_define/
|
209
|
+
# nothing
|
210
|
+
else
|
211
|
+
raise Ruby2CExtError::Bug, "unexpected string returned from #{@base_scope.class}##{meth}: #{res}"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
res
|
215
|
+
end
|
216
|
+
|
217
|
+
def var_ptr_for_wrap
|
218
|
+
raise Ruby2CExtError::Bug, "unexpected call of var_ptr_for_wrap"
|
219
|
+
end
|
220
|
+
|
221
|
+
def init_c_code
|
222
|
+
raise Ruby2CExtError::Bug, "unexpected call of init_c_code"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|