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.
@@ -0,0 +1,222 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
+ <html>
3
+ <head>
4
+ <title>Optimizations</title>
5
+ <link href="style.css" media="all" rel="Stylesheet" type="text/css">
6
+ </head>
7
+ <body>
8
+ <h1>Optimizations</h1>
9
+
10
+
11
+ <p>Without any optimizations enabled Ruby2CExtension tries to match Ruby&#8217;s
12
+ semantics as close as possible, but sometimes it might be desired to sacrifice
13
+ some compatibility in exchange for faster execution.</p>
14
+
15
+
16
+ <p>Ruby2CExtension offers different optimizations which can be enabled
17
+ indivdually (see <a href="rb2cx.html">rb2cx</a> and <a href="eval2c.html">Eval2C</a>). All these
18
+ optimizations should be safe, i.e. they won&#8217;t produce segfaults or other C
19
+ level failures, but, as mentioned above, they sacrifice compatibility for
20
+ faster execution and thus can result in &#8220;wrong&#8221; behavior of the compiled Ruby
21
+ code.</p>
22
+
23
+
24
+ <p>Sections: <a href="#section1">Constant Lookup Caching</a>, <a href="#section2">Optimization of Calls to Built-in Methods</a>, <a href="#section3">Inlining of Some Built-in Methods</a>, <a href="#section4">Case Optimization</a>.</p>
25
+
26
+
27
+ <h2 id="section1">Constant Lookup Caching</h2>
28
+
29
+
30
+ <p>This optimization simply assumes that constants really are constant. So the
31
+ constant lookup for each constant is only performed once and then the result
32
+ is cached for later use.</p>
33
+
34
+
35
+ <p>Because in Ruby constants can actually change their value, this optimization
36
+ can lead to wrong results. So if your code depends on changing constants, then
37
+ this optimization should not be enabled. Otherwise it can provide a small
38
+ performance improvement.</p>
39
+
40
+
41
+ <h2 id="section2">Optimization of Calls to Built-in Methods</h2>
42
+
43
+
44
+ <p>This optimization is by far the most important one, because it can produce
45
+ very significant speedups for most Ruby code. It replaces normal calls to many
46
+ (C-implemented) methods of the built-in classes with (almost) direct calls to
47
+ the C functions that implement these methods.</p>
48
+
49
+
50
+ <p>The motivation behind this optimization is that method calls are generally
51
+ considered to be one of the &#8220;slowest&#8221; parts of Ruby and since almost
52
+ everything is method calls in Ruby it helps to improve at least some of them.
53
+ A method call basically consists of two parts: first the method is looked up
54
+ in the receivers class (or its ancestors) and then Ruby sets up the
55
+ environment for the method and executes the code. For many of the
56
+ C-implemented methods of the built-in classes the setup is not necessary and
57
+ if we assume that they are generally not modified/redefined, we can also avoid
58
+ the lookup. So we basically can directly call the C function that implements
59
+ the method and thereby avoid most of the method call overhead in these cases.</p>
60
+
61
+
62
+ <p>Because this can not be done for every C-implemented method, there is a list
63
+ of method names (with arities and built-in types that have this method) for
64
+ which the optimization can be applied. When a call to such a method is
65
+ compiled, instead of doing a normal <code>rb_funcall()</code>, another C function is
66
+ called which checks if the type of the receiver matches one of the built-in
67
+ types that have this method and if yes, then the implementing C function is
68
+ called directly otherwise a normal Ruby call is performed.</p>
69
+
70
+
71
+ <p>To be able to directly call the C functions that implement the methods, the
72
+ pointers to these C functions have to be looked up at least once. So, when the
73
+ C extension is <code>require</code>d, for all methods that are used in this
74
+ extension the pointers to the C functions are looked up. This is also kind of
75
+ a sanity check: if that lookup fails or the lookup returns that the method is
76
+ not implemented by a C function (e.g. because it was modified/overridden by a
77
+ Ruby implementation), then this method will later be called by just using
78
+ <code>rb_funcall()</code>.</p>
79
+
80
+
81
+ <p>The built-in types which are included in this optimization are <code>Array</code>,
82
+ <code>Bignum</code>, <code>FalseClass</code>, <code>Fixnum</code>, <code>Float</code>, <code>Hash</code>, <code>NilClass</code>, <code>Regexp</code>,
83
+ <code>String</code>, <code>Symbol</code> and <code>TrueClass</code>. For the detailed method list please see
84
+ <code>lib/ruby2cext/plugins/builtin_methods.rb</code>.</p>
85
+
86
+
87
+ <p>If the type of the receiver is known at compile time, then it is possibly to
88
+ even further optimize by directly calling the implementing C function, instead
89
+ of doing the indirect call with the runtime type checking. An example for such
90
+ a case is the expression <code>1 == some_var</code>, in this expression the
91
+ receiver is a fixnum. The compile time type detection also works for string
92
+ literals, array literals, hash literals and regexp literals.</p>
93
+
94
+
95
+ <p>As mentioned above, this optimization can speedup most Ruby code, but it also
96
+ has downsides:</p>
97
+
98
+
99
+ <ul>
100
+ <li>If the type of the receiver of such a method call is not one of the
101
+ supported built-in types, then the call will actually be slightly slower
102
+ than it would have been without the optimization (because of the additional
103
+ type check). But the overhead is very small.</li>
104
+ <li>If one of the supported methods is modified after the compiled C extension
105
+ is <code>require</code>d, then this modification will not be used in the
106
+ extension, the extension will just continue to use the C implementation.</li>
107
+ </ul>
108
+
109
+
110
+ But in general this optimization can and should always be used, unless it is
111
+ necessary to modify built-in methods after the extension is
112
+ <code>require</code>d.
113
+
114
+ <h2 id="section3">Inlining of Some Built-in Methods</h2>
115
+
116
+
117
+ <p>This optimization goes one step further than the previous optimization by
118
+ avoiding the method calls completely and instead directly inlining the code
119
+ for the following methods: <code>nil?</code>, <code>equal?</code> and <code>__send__</code> (without a block).</p>
120
+
121
+
122
+ <p>Because these three methods should never be redefined in any class (according
123
+ to their documentation), this optimization can be applied to all calls of
124
+ these methods (with the correct arities) independent of the type of the
125
+ receiver.</p>
126
+
127
+
128
+ <p>Details:</p>
129
+
130
+
131
+ <ul>
132
+ <li>Any call to <code>nil?</code> with no arguments will be translated to a simple check
133
+ that determines if the receiver is <code>nil</code>.</li>
134
+ <li>Any call to <code>equal?</code> with one argument will be translated to a simple check
135
+ that determines if the receiver is equal to the first argument (i.e. they
136
+ have the same id).</li>
137
+ <li>Any call to <code>__send__</code> with one or more arguments and without a block will
138
+ be translated to code that directly performs the method call. As a result
139
+ <code>__send__(:foo, 1, 2, 3)</code> will perform practically as fast as
140
+ <code>foo(1, 2, 3)</code> in compiled code.</li>
141
+ </ul>
142
+
143
+
144
+ <p>In general this optimization can and should always be used, unless one of the
145
+ affected methods is redefined somewhere.</p>
146
+
147
+
148
+ <h2 id="section4">Case Optimization</h2>
149
+
150
+
151
+ <p>This optimization can in some cases transform Ruby <code>case</code>/<code>when</code> statements to
152
+ real C <code>switch</code>/<code>case</code> statements. This works for cases with Ruby immediate
153
+ values, for which the integral value is known at compile time, i.e. <code>nil</code>,
154
+ <code>true</code>, <code>false</code> and fixnums.</p>
155
+
156
+
157
+ <p>These cases have to be put at the beginning, all following non optimizable
158
+ whens will be handled normally. Example:</p>
159
+
160
+
161
+ <pre><code>case x
162
+ when true
163
+ # opt
164
+ when 2, 3, nil
165
+ # opt
166
+ when false, 26
167
+ # opt
168
+ when /foo/
169
+ # normal
170
+ when 23
171
+ # normal because, it is after another normal case
172
+ end
173
+ </code></pre>
174
+
175
+ <p>This basically gets converted to the following C code:</p>
176
+
177
+
178
+ <pre><code>switch (eval(x)) {
179
+ case Qtrue:
180
+ code;
181
+ break;
182
+ case LONG2FIX(2):
183
+ case LONG2FIX(3):
184
+ case Qnil:
185
+ code;
186
+ break;
187
+ case Qfalse:
188
+ case LONG2FIX(26):
189
+ code;
190
+ break;
191
+ default:
192
+ other cases ...
193
+ }
194
+ </code></pre>
195
+
196
+ <p>There also is a fallback that ensures that cases like</p>
197
+
198
+
199
+ <pre><code>case 2.0
200
+ when 2
201
+ ...
202
+ end
203
+ </code></pre>
204
+
205
+ <p>still work.</p>
206
+
207
+
208
+ <p>This optimization can be useful for some kind of opcode dispatch in an
209
+ interpreter for example.</p>
210
+
211
+
212
+ <p>It should always produce the correct results, unless the <code>===</code> method of
213
+ <code>Fixnum</code>, <code>TrueClass</code>, <code>FalseClass</code> or <code>NilClass</code> is modified. So it is
214
+ generally okay to enable this optimization.</p>
215
+
216
+
217
+ <p>When this optimization is combined with the built-in methods optimization,
218
+ then the gained speedup is not really big (because the <code>===</code> calls for the
219
+ <code>when</code> cases are optimized by the built-in methods optimization anyway), but
220
+ it can be useful for larger <code>case</code>/<code>when</code> statements.</p>
221
+ </body>
222
+ </html>
data/doc/rb2cx.html ADDED
@@ -0,0 +1,151 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
+ <html>
3
+ <head>
4
+ <title>rb2cx</title>
5
+ <link href="style.css" media="all" rel="Stylesheet" type="text/css">
6
+ </head>
7
+ <body>
8
+ <h1>rb2cx</h1>
9
+
10
+
11
+ <p><code>rb2cx</code> is the command line interface to the functionality of Ruby2CExtension.
12
+ It takes one or more Ruby files, translates them to equivalent C extensions
13
+ and optionally compiles them with the C compiler.</p>
14
+
15
+
16
+ <p>Sections: <a href="#section1">Overview</a>, <a href="#section2">General Options</a>, <a href="#section3">Include Option</a>, <a href="#section4">Optimization Options</a>, <a href="#section5">Examples</a>.</p>
17
+
18
+
19
+ <h2 id="section1">Overview</h2>
20
+
21
+
22
+ <p>The general usage is very simple, just run <code>rb2cx</code> with the filenames of the
23
+ Ruby files as arguments:</p>
24
+
25
+
26
+ <pre><code>rb2cx file1.rb file2.rb
27
+ </code></pre>
28
+
29
+ <p>This will produce <code>file1.c</code>, <code>file2.c</code> and the compiled extensions <code>file1.so</code>,
30
+ <code>file2.so</code> (the file extension depends on the platform).</p>
31
+
32
+
33
+ <p>Additionally it is possible to specify some options before the filenames.</p>
34
+
35
+
36
+ <h2 id="section2">General Options</h2>
37
+
38
+
39
+ <ul>
40
+ <li><code>-h</code>/<code>--help</code>: a help message is printed and the program exits regardless of
41
+ other arguments.</li>
42
+ <li><code>-c</code>/<code>--only-c</code>: only the translation to C code is performed, the compilation
43
+ to native extensions is omited.</li>
44
+ <li><code>-v</code>/<code>--verbose</code>: some status messages are printed, e.g. which files are
45
+ currently translated or compiled.</li>
46
+ <li><code>-w</code>/<code>--warnings</code>: warnings are printed for some things that might not work
47
+ as expected. The warnings do not cover everything mentioned in the
48
+ <a href="limitations.html">limitations documentation</a>.</li>
49
+ <li><code>-V</code>/<code>--version</code>: the Ruby2CExtension version is printed.</li>
50
+ </ul>
51
+
52
+
53
+ <h2 id="section3">Include Option</h2>
54
+
55
+
56
+ <p>Ruby2CExtension has an experimental feature that allows dependencies of a
57
+ compiled Ruby file to be included in the same extension. This is best
58
+ described with an example. Let&#8217;s say we have 3 files: <code>a.rb</code>, <code>b.rb</code> and
59
+ <code>c.rb</code>:</p>
60
+
61
+
62
+ <pre><code># a.rb
63
+ puts "a"
64
+ class A; end
65
+ </code></pre>
66
+
67
+ <pre><code># b.rb
68
+ puts "b"
69
+ require "a"
70
+ class B; end
71
+ </code></pre>
72
+
73
+ <pre><code># c.rb
74
+ require "a"
75
+ require "b"
76
+ puts "c"
77
+ </code></pre>
78
+
79
+ <p>The <code>require</code>-include feature is enabled if the <code>-I</code>/<code>--include</code> option
80
+ followed by a search path is given (possibly multiple times). The search paths
81
+ can be absolute or relative paths, they are searched for <code>require</code>d
82
+ files. So, if the example is compiled to a C extension with</p>
83
+
84
+
85
+ <pre><code>rb2cx -I . c.rb
86
+ </code></pre>
87
+
88
+ <p>then the following will happen. For each call to <code>require</code> with no explicit
89
+ receiver and one argument that is a simple string (i.e. no interpolation) the
90
+ search paths are searched for a file that matches the argument to the
91
+ <code>require</code> call (with an algorithm similar to Ruby&#8217;s). If no matching file is
92
+ found, then the call to <code>require</code> is compiled as usual. But if a matching file
93
+ is found, then that file is read and it is translated to C and instead of
94
+ compiling the original <code>require</code> call that translated C code will be executed,
95
+ unless a <code>require</code> of that file was encountered before.</p>
96
+
97
+
98
+ <p>So in the example we will get one C extension, that contains the code of all
99
+ three files and the <code>require</code>s are performed at the right moment
100
+ and in correct order. The output will be (notice that the <code>require
101
+ "a"</code> in <code>b.rb</code> does not result in a second execution of <code>a.rb</code>, as
102
+ expected):</p>
103
+
104
+
105
+ <pre><code>a
106
+ b
107
+ c
108
+ </code></pre>
109
+
110
+ As stated above, this feature is experimental, it should work well for many
111
+ cases, e.g. for a library that has one main file which is always
112
+ <code>require</code>d by user code, but is split into multiple files for
113
+ maintenance. Such a library could be compiled into a single C extension. But
114
+ it can break for various reasons, e.g. Ruby will not be aware, that the
115
+ included files are already &#8220;<code>require</code>d&#8221; (so if a file is already
116
+ included in a C extension, but also <code>require</code>d by other normal Ruby
117
+ code, then that file will in effect execute twice).
118
+
119
+ <p>If the verbose mode is enabled (the <code>-v</code>/<code>--verbose</code> option), then each
120
+ inclusion of a file will be logged. This way one can easily check if the
121
+ expected files are actually included.</p>
122
+
123
+
124
+ <h2 id="section4">Optimization Options</h2>
125
+
126
+
127
+ <p>Ruby2CExtension can use various <a href="optimizations.html">optimizations</a> to improve
128
+ the performance of the resulting C extension. These optimizations are not
129
+ enabled by default, because they can all result in wrong behavior. The
130
+ optimizations are enabled by the <code>-O</code>/<code>--optimization</code> option followed by one
131
+ of the following optimization names:</p>
132
+
133
+
134
+ <ul>
135
+ <li><code>const_cache</code>: enables constant lookup caching</li>
136
+ <li><code>builtin_methods</code>: enables optimization of calls to built-in methods</li>
137
+ <li><code>inline_methods</code>: enables inlining of some built-in methods</li>
138
+ <li><code>case_optimize</code>: enables case optimization</li>
139
+ <li><code>all</code>: enables all of the above optimizations</li>
140
+ </ul>
141
+
142
+
143
+ <h2 id="section5">Examples</h2>
144
+
145
+
146
+ <pre><code>rb2cx -wv file.rb
147
+ rb2cx -I . -O all file.rb
148
+ rb2cx -I . -I ../libs -O const_cache -O builtin_methods -w file.rb
149
+ </code></pre>
150
+ </body>
151
+ </html>
data/doc/style.css ADDED
@@ -0,0 +1,27 @@
1
+ body {
2
+ color: #333;
3
+ background-color: white;
4
+ margin: 0 2em;
5
+ min-width: 41em;
6
+ }
7
+ h1 {
8
+ color: black;
9
+ border-bottom: 2px solid red;
10
+ }
11
+ h2 {
12
+ color: black;
13
+ }
14
+ h3 {
15
+ color: black;
16
+ }
17
+ pre {
18
+ color: black;
19
+ background-color: #eee;
20
+ padding: 0.7em 1em;
21
+ margin: 0.8em 0;
22
+ border: 1px dotted #999;
23
+ }
24
+ code {
25
+ color: black;
26
+ background-color: #eee;
27
+ }
@@ -0,0 +1,621 @@
1
+
2
+ require "ruby2cext/str_to_c_strlit"
3
+ require "ruby2cext/error"
4
+ require "ruby2cext/tools"
5
+ require "ruby2cext/common_node_comp"
6
+ require "ruby2cext/scopes"
7
+
8
+ module Ruby2CExtension
9
+
10
+ module CFunction
11
+ # contains all different Types of C functions that are compiled from ruby nodes
12
+
13
+ class Base
14
+ include CommonNodeComp
15
+ extend Tools::EnsureNodeTypeMixin
16
+ attr_reader :scope, :compiler, :closure_tbl
17
+ attr_accessor :need_res, :need_self, :need_cref, :need_class, :need_wrap
18
+ def initialize(compiler, scope)
19
+ @compiler = compiler
20
+ @scope = scope
21
+ @closure_tbl = []
22
+ @scope.closure_tbl = @closure_tbl
23
+ @lines = []
24
+ @while_stack = []
25
+ end
26
+
27
+ # some redirects to compiler
28
+ def un(str); compiler.un(str); end
29
+ def sym(sym); compiler.sym(sym); end
30
+ def global_const(str, register_gc = true)
31
+ compiler.global_const(str, register_gc)
32
+ end
33
+ def global_var(str)
34
+ compiler.global_var(str)
35
+ end
36
+ def add_helper(str); compiler.add_helper(str); end
37
+
38
+ def get_lines
39
+ @lines.join("\n")
40
+ end
41
+ def l(line) # add_line
42
+ # ignore lines with only whitespace or only alnum chars (variable name)
43
+ unless line =~ /\A\s*\z/ || (line =~ /\A(\w*);?\z/ && !(%w[break continue].include? $1))
44
+ @lines << line
45
+ end
46
+ end
47
+
48
+ def push_while(redo_lbl, next_lbl, break_lbl)
49
+ @while_stack << [redo_lbl, next_lbl, break_lbl]
50
+ end
51
+ def pop_while
52
+ raise Ruby2CExtError::Bug, "popped from empty while stack" if @while_stack.empty?
53
+ @while_stack.pop
54
+ end
55
+ def in_while?(lbl_type = nil)
56
+ return false if @while_stack.empty?
57
+ case lbl_type
58
+ when nil
59
+ true
60
+ when :redo
61
+ @while_stack.last[0]
62
+ when :next
63
+ @while_stack.last[1]
64
+ when :break
65
+ @while_stack.last[2]
66
+ else
67
+ false
68
+ end
69
+ end
70
+
71
+ def break_allowed?(with_value)
72
+ in_while?(:break)
73
+ end
74
+ def comp_break(hash)
75
+ if (lbl = in_while?(:break))
76
+ l "while_res = #{comp(hash[:stts])};"
77
+ l "goto #{lbl};"
78
+ "Qnil"
79
+ else
80
+ raise Ruby2CExtError::NotSupported, "break is not supported here"
81
+ end
82
+ end
83
+
84
+ def next_allowed?
85
+ in_while?(:next)
86
+ end
87
+ def comp_next(hash)
88
+ if (lbl = in_while?(:next))
89
+ # hash[:stts] is silently ignored (as ruby does)
90
+ l "goto #{lbl};"
91
+ "Qnil"
92
+ else
93
+ raise Ruby2CExtError::NotSupported, "next is not supported here"
94
+ end
95
+ end
96
+
97
+ def redo_allowed?
98
+ in_while?(:redo)
99
+ end
100
+ def comp_redo(hash)
101
+ if (lbl = in_while?(:redo))
102
+ l "goto #{lbl};"
103
+ "Qnil"
104
+ else
105
+ raise Ruby2CExtError::NotSupported, "redo is not supported here"
106
+ end
107
+ end
108
+
109
+ def return_allowed?
110
+ false
111
+ end
112
+ def comp_return(hash)
113
+ raise Ruby2CExtError::NotSupported, "return is not supported here"
114
+ end
115
+
116
+ def comp_retry(hash)
117
+ l "rb_jump_tag(0x4 /* TAG_RETRY */);"
118
+ "Qnil"
119
+ end
120
+
121
+ def need_closure_ptr
122
+ false # only needed in Block
123
+ end
124
+
125
+ def assign_res(str)
126
+ self.need_res = true
127
+ unless str.strip == "res"
128
+ l "res = #{str};"
129
+ end
130
+ end
131
+ def get_self
132
+ self.need_self = true
133
+ "self"
134
+ end
135
+ def get_cref
136
+ self.need_cref = true
137
+ get_cref_impl # subclass
138
+ end
139
+ def get_class
140
+ self.need_class = true
141
+ "s_class"
142
+ end
143
+ def get_cbase
144
+ "(#{get_cref}->nd_clss)"
145
+ end
146
+ def get_cvar_cbase
147
+ # there is always at least one real class in the cref chain
148
+ add_helper <<-EOC
149
+ static VALUE cvar_cbase(NODE *cref) {
150
+ while (FL_TEST(cref->nd_clss, FL_SINGLETON)) { cref = cref->nd_next; }
151
+ return cref->nd_clss;
152
+ }
153
+ EOC
154
+ "cvar_cbase(#{get_cref})"
155
+ end
156
+
157
+ def get_closure_ary_var
158
+ "my_closure_ary"
159
+ end
160
+
161
+ def get_wrap_ptr
162
+ "(&the_wrap)"
163
+ end
164
+
165
+ def add_closure_need(sym)
166
+ closure_tbl << sym unless closure_tbl.include? sym
167
+ end
168
+
169
+ def closure_buid_c_code
170
+ if closure_tbl.empty?
171
+ nil
172
+ else
173
+ res = ["#{get_closure_ary_var} = rb_ary_new2(#{closure_tbl.size});"]
174
+ closure_tbl.each_with_index { |entry, idx|
175
+ case entry
176
+ when Integer
177
+ res << "RARRAY(#{get_closure_ary_var})->ptr[#{idx}] = #{scope.get_dvar_ary(entry)};"
178
+ when :lvar
179
+ res << "RARRAY(#{get_closure_ary_var})->ptr[#{idx}] = #{scope.get_lvar_ary};"
180
+ when :self
181
+ res << "RARRAY(#{get_closure_ary_var})->ptr[#{idx}] = #{get_self};"
182
+ when :class
183
+ res << "RARRAY(#{get_closure_ary_var})->ptr[#{idx}] = #{get_class};"
184
+ when :cref
185
+ add_helper <<-EOC
186
+ static void cref_data_mark(NODE *n) {
187
+ rb_gc_mark((VALUE)n);
188
+ }
189
+ EOC
190
+ res << "RARRAY(#{get_closure_ary_var})->ptr[#{idx}] = " +
191
+ "Data_Wrap_Struct(rb_cObject, cref_data_mark, 0, #{get_cref});"
192
+ else
193
+ raise Ruby2CExtError::Bug, "unexpected closure_tbl entry: #{entry.inspect}"
194
+ end
195
+ }
196
+ res << "RARRAY(#{get_closure_ary_var})->len = #{closure_tbl.size};"
197
+ res.join("\n")
198
+ end
199
+ end
200
+
201
+ def wrap_buid_c_code
202
+ add_helper <<-EOC
203
+ struct wrap {
204
+ VALUE self;
205
+ VALUE s_class;
206
+ NODE *cref;
207
+ VALUE my_closure_ary;
208
+ VALUE *closure;
209
+ VALUE *var;
210
+ long state;
211
+ };
212
+ EOC
213
+ res = []
214
+ res << "the_wrap.self = #{get_self};" if need_self
215
+ res << "the_wrap.s_class = #{get_class};" if need_class
216
+ res << "the_wrap.cref = #{get_cref};" if need_cref
217
+ res << "the_wrap.my_closure_ary = #{get_closure_ary_var};" unless closure_tbl.empty?
218
+ res << "the_wrap.closure = closure;" if need_closure_ptr
219
+ res << "the_wrap.var = #{scope.var_ptr_for_wrap};" if scope.var_ptr_for_wrap
220
+ res.compact.join("\n")
221
+ end
222
+
223
+ def init_c_code
224
+ cb_c_code = closure_buid_c_code # must be called before the rest because it might change self or scope
225
+ res = []
226
+ res << "VALUE res;" if need_res
227
+ res << "VALUE s_class = (#{get_cref})->nd_clss;" if need_class
228
+ res << "VALUE #{get_closure_ary_var};" if cb_c_code
229
+ res << "struct wrap the_wrap;" if need_wrap
230
+ res << scope.init_c_code
231
+ res << cb_c_code if cb_c_code
232
+ res << wrap_buid_c_code if need_wrap
233
+ res.compact.join("\n")
234
+ end
235
+ end
236
+
237
+ class ClassModuleScope < Base
238
+ def self.compile(outer, scope_node, class_mod_var, is_class)
239
+ ensure_node_type(scope_node, :scope)
240
+ vmode_methods = Scopes::Scope::VMODES.dup
241
+ vmode_methods.delete :module_function if is_class
242
+ cf = self.new(outer.compiler, Scopes::Scope.new(scope_node.last[:tbl], vmode_methods))
243
+ cf.instance_eval {
244
+ block = make_block(scope_node.last[:next])
245
+ l "return #{comp(block)};"
246
+ }
247
+ body = "#{cf.init_c_code}\n#{cf.get_lines}"
248
+ args = []
249
+ args << "VALUE self" if cf.need_self
250
+ args << "NODE *cref" if cf.need_cref
251
+ sig = "static VALUE FUNNAME(#{args.join(", ")}) {"
252
+ fname = cf.compiler.add_fun("#{sig}\n#{body}\n}", "class_module_scope")
253
+ outer.instance_eval {
254
+ args = []
255
+ args << class_mod_var if cf.need_self
256
+ args << "NEW_NODE(NODE_CREF, #{class_mod_var}, 0, #{get_cref})" if cf.need_cref
257
+ assign_res("#{fname}(#{args.join(", ")})")
258
+ }
259
+ "res"
260
+ end
261
+
262
+ def get_cref_impl
263
+ "cref"
264
+ end
265
+
266
+ end
267
+
268
+ class ToplevelScope < ClassModuleScope
269
+ def self.compile(compiler, scope_node, private_vmode = true)
270
+ ensure_node_type(scope_node, :scope)
271
+ cf = self.new(compiler, Scopes::Scope.new(scope_node.last[:tbl], [:public, :private], private_vmode))
272
+ cf.instance_eval {
273
+ block = make_block(scope_node.last[:next])
274
+ l "#{comp(block)};"
275
+ }
276
+ body = "#{cf.init_c_code}\n#{cf.get_lines}"
277
+ sig = "static void FUNNAME(VALUE self, NODE *cref) {"
278
+ cf.compiler.add_fun("#{sig}\n#{body}\n}", "toplevel_scope") # and return the function name
279
+ end
280
+ end
281
+
282
+ class Method < Base
283
+ def self.compile(outer, scope_node, def_fun, class_var, mid)
284
+ ensure_node_type(scope_node, :scope)
285
+ cf = self.new(outer.compiler, Scopes::Scope.new(scope_node.last[:tbl], []))
286
+ cf.instance_eval {
287
+ block_array = make_block(scope_node.last[:next]).last.dup # dup the block_array to allow modification
288
+ arg = block_array.shift
289
+ ba = nil
290
+ unless block_array.empty? || block_array.first.first != :block_arg
291
+ ba = block_array.shift
292
+ end
293
+ handle_method_args(arg, ba)
294
+ l "return #{comp([:block, block_array])};"
295
+ }
296
+ body = "#{cf.init_c_code}\n#{cf.get_lines}"
297
+ sig = "static VALUE FUNNAME(int meth_argc, VALUE *meth_argv, VALUE self) {"
298
+ fname = cf.compiler.add_fun("#{sig}\n#{body}\n}", "method")
299
+ if cf.need_cref
300
+ outer.instance_eval {
301
+ add_helper <<-EOC
302
+ static void def_only_once(ID mid) {
303
+ rb_raise(rb_eTypeError, "def for \\"%s\\" can only be used once", rb_id2name(mid));
304
+ }
305
+ EOC
306
+ l "if (#{cf.cref_global_var}) def_only_once(#{sym(mid)});"
307
+ l "#{cf.cref_global_var} = (VALUE)(#{get_cref});"
308
+ }
309
+ end
310
+ outer.l "#{def_fun}(#{class_var}, #{mid.to_s.to_c_strlit}, #{fname}, -1);"
311
+ "Qnil"
312
+ end
313
+
314
+ def return_allowed?
315
+ true
316
+ end
317
+ def comp_return(hash)
318
+ l "return #{comp(hash[:stts])};"
319
+ "Qnil"
320
+ end
321
+
322
+ def get_cref_impl
323
+ @cref_global_var ||= global_var("Qfalse")
324
+ "(RNODE(#{@cref_global_var}))"
325
+ end
326
+ attr_reader :cref_global_var
327
+ end
328
+
329
+ class Block < Base
330
+ def self.compile(outer, block_node, var_node)
331
+ ensure_node_type(block_node, :block)
332
+ cf = self.new(outer.compiler, outer.scope.new_dyna_scope)
333
+ cf.instance_eval {
334
+ if Array === var_node
335
+ if var_node.first == :masgn
336
+ dup_hash = var_node.last.dup
337
+ c_if("ruby_current_node->nd_state != 1") { # 1 is YIELD_FUNC_AVALUE
338
+ # do "svalue_to_mrhs"
339
+ c_if("bl_val == Qundef") {
340
+ l "bl_val = rb_ary_new2(0);"
341
+ }
342
+ c_else {
343
+ #if dup_hash[:head] # TODO
344
+ l "VALUE tmp = rb_check_array_type(bl_val);"
345
+ l "bl_val = (NIL_P(tmp) ? rb_ary_new3(1, bl_val) : tmp);"
346
+ #else
347
+ # l "bl_val = rb_ary_new3(1, bl_val);"
348
+ #end
349
+ }
350
+ }
351
+ dup_hash[:value] = "bl_val"
352
+ comp_masgn(dup_hash)
353
+ else
354
+ c_if("ruby_current_node->nd_state == 1") { # 1 is YIELD_FUNC_AVALUE
355
+ # do "avalue_to_svalue"
356
+ l "if (RARRAY(bl_val)->len == 0) bl_val = Qnil;"
357
+ l "else if (RARRAY(bl_val)->len == 1) bl_val = RARRAY(bl_val)->ptr[0];"
358
+ }
359
+ handle_assign(var_node, "bl_val")
360
+ end
361
+ end
362
+ l "block_redo:"
363
+ l "return #{comp_block(block_node.last)};"
364
+ }
365
+ body = "#{cf.init_c_code(outer)}\n#{cf.get_lines}"
366
+ sig = "static VALUE FUNNAME(VALUE bl_val, VALUE closure_ary, VALUE bl_self) {"
367
+ fname = cf.compiler.add_fun("#{sig}\n#{body}\n}", "block")
368
+ [fname, cf.need_closure_ptr]
369
+ end
370
+
371
+ def init_c_code(outer)
372
+ cb_c_code = closure_buid_c_code # must be called before the rest because it might change self or scope
373
+ outer.add_closure_need(:self) if need_self
374
+ outer.add_closure_need(:class) if need_class
375
+ outer.add_closure_need(:cref) if need_cref
376
+ res = []
377
+ res << "VALUE res;" if need_res
378
+ if need_closure_ptr
379
+ res << "VALUE *closure = RARRAY(closure_ary)->ptr;"
380
+ end
381
+ res << "VALUE self = (bl_self == Qundef ? closure[#{outer.closure_tbl.index(:self)}] : bl_self);" if need_self
382
+ res << "VALUE s_class = (bl_self == Qundef ? closure[#{outer.closure_tbl.index(:class)}] : ruby_class);" if need_class
383
+ if need_cref
384
+ # see #define Data_Get_Struct
385
+ res << "NODE *cref = (Check_Type(closure[#{outer.closure_tbl.index(:cref)}]," +
386
+ " T_DATA), (NODE*)DATA_PTR(closure[#{outer.closure_tbl.index(:cref)}]));"
387
+ end
388
+ res << "VALUE #{get_closure_ary_var};" if cb_c_code
389
+ res << "struct wrap the_wrap;" if need_wrap
390
+ res << scope.init_c_code
391
+ res << cb_c_code if cb_c_code
392
+ res << wrap_buid_c_code if need_wrap
393
+ res.compact.join("\n")
394
+ end
395
+
396
+ def break_allowed?(with_value)
397
+ super || !with_value
398
+ end
399
+ def comp_break(hash)
400
+ if in_while?(:break)
401
+ super
402
+ else
403
+ raise Ruby2CExtError::NotSupported, "break with a value is not supported in a block" if hash[:stts]
404
+ l "rb_iter_break();"
405
+ "Qnil"
406
+ end
407
+ end
408
+
409
+ def next_allowed?
410
+ true
411
+ end
412
+ def comp_next(hash)
413
+ if in_while?(:next)
414
+ super
415
+ else
416
+ l "return #{comp(hash[:stts])};"
417
+ "Qnil"
418
+ end
419
+ end
420
+
421
+ def redo_allowed?
422
+ true
423
+ end
424
+ def comp_redo(hash)
425
+ if in_while?(:redo)
426
+ super
427
+ else
428
+ l "goto block_redo;"
429
+ "Qnil"
430
+ end
431
+ end
432
+
433
+ def need_closure_ptr
434
+ scope.need_closure || need_self || need_class || need_cref
435
+ end
436
+
437
+ def get_cref_impl
438
+ "cref"
439
+ end
440
+ end
441
+
442
+ class Wrap < Base
443
+
444
+ def self.compile(outer, base_name, cflow_hash = nil)
445
+ cf = self.new(outer, cflow_hash)
446
+ cf.instance_eval {
447
+ if cf.cflow_hash
448
+ l "#{get_wrap_ptr}->state &= (1<<20)-1;" # set bits 20+ to 0
449
+ end
450
+ l "return #{yield cf};"
451
+ }
452
+ body = "#{cf.init_c_code}\n#{cf.get_lines}"
453
+ sig = "static VALUE FUNNAME(struct wrap *wrap_ptr) {"
454
+ outer.need_wrap = true
455
+ cf.compiler.add_fun("#{sig}\n#{body}\n}", base_name) # and return the function name
456
+ end
457
+
458
+ attr_reader :base_cfun, :outer_cfun, :cflow_hash
459
+
460
+ # the following attr_accessors from Base are redefined, to redirect to base_cfun
461
+ [:need_self, :need_cref, :need_class, :need_wrap].each { |a|
462
+ define_method(a) { base_cfun.send(a) }
463
+ asgn_sym = :"#{a}="
464
+ define_method(asgn_sym) { |arg| base_cfun.send(asgn_sym, arg) }
465
+ }
466
+
467
+ # cflow_hash can either be nil or a hash, if it is nil then break,
468
+ # next, redo and return are not possible from the wrap to
469
+ # outer_cfun.
470
+ #
471
+ # If cflow_hash is a hash then those are possible if outer_cfun
472
+ # allows them. For this to work only the bits 0-19 of
473
+ # wrap_ptr->state may be modified by code inside this wrap and
474
+ # after the call the code generated by Wrap.handle_wrap_cflow has
475
+ # to be inserted.
476
+ def initialize(outer_cfun, cflow_hash = nil)
477
+ @outer_cfun = outer_cfun
478
+ if Wrap === outer_cfun
479
+ @base_cfun = outer_cfun.base_cfun
480
+ else
481
+ @base_cfun = outer_cfun
482
+ end
483
+ @cflow_hash = cflow_hash
484
+ @compiler = base_cfun.compiler
485
+ @scope = Scopes::WrappedScope.new(base_cfun.scope)
486
+ @closure_tbl = base_cfun.closure_tbl
487
+ @lines = []
488
+ @while_stack = []
489
+ end
490
+
491
+ BREAK_FLAG = 1 << 20
492
+ NEXT_FLAG = 1 << 21
493
+ REDO_FLAG = 1 << 22
494
+ RETURN_FLAG = 1 << 23
495
+
496
+ def break_allowed?(with_value)
497
+ super || (cflow_hash && outer_cfun.break_allowed?(with_value))
498
+ end
499
+ def comp_break(hash)
500
+ if in_while?(:break)
501
+ super
502
+ elsif break_allowed?(hash[:stts])
503
+ @cflow_hash[:break] ||= hash[:stts]
504
+ l "#{get_wrap_ptr}->state |= #{BREAK_FLAG};"
505
+ l "return #{comp(hash[:stts])};"
506
+ "Qnil"
507
+ else
508
+ raise Ruby2CExtError::NotSupported, "break #{hash[:stts] ? "with a value " : ""}is not supported here"
509
+ end
510
+ end
511
+
512
+ def next_allowed?
513
+ super || (cflow_hash && outer_cfun.next_allowed?)
514
+ end
515
+ def comp_next(hash)
516
+ if in_while?(:next)
517
+ super
518
+ elsif next_allowed?
519
+ @cflow_hash[:next] = true
520
+ l "#{get_wrap_ptr}->state |= #{NEXT_FLAG};"
521
+ # hash[:stts] might be evaluated unnecessarily (because it
522
+ # is ignored in while loops), but oh well ...
523
+ l "return #{comp(hash[:stts])};"
524
+ "Qnil"
525
+ else
526
+ raise Ruby2CExtError::NotSupported, "next is not supported here"
527
+ end
528
+ end
529
+
530
+ def redo_allowed?
531
+ super || (cflow_hash && outer_cfun.redo_allowed?)
532
+ end
533
+ def comp_redo(hash)
534
+ if in_while?(:redo)
535
+ super
536
+ elsif redo_allowed?
537
+ @cflow_hash[:redo] = true
538
+ l "#{get_wrap_ptr}->state |= #{REDO_FLAG};"
539
+ l "return Qnil;"
540
+ "Qnil"
541
+ else
542
+ raise Ruby2CExtError::NotSupported, "redo is not supported here"
543
+ end
544
+ end
545
+
546
+ def return_allowed?
547
+ cflow_hash && outer_cfun.return_allowed?
548
+ end
549
+ def comp_return(hash)
550
+ if return_allowed?
551
+ @cflow_hash[:return] = true
552
+ l "#{get_wrap_ptr}->state |= #{RETURN_FLAG};"
553
+ l "return #{comp(hash[:stts])};"
554
+ "Qnil"
555
+ else
556
+ raise Ruby2CExtError::NotSupported, "return is not supported here"
557
+ end
558
+ end
559
+
560
+ # the result of the call to the wrap cfun is expected in "res"
561
+ def self.handle_wrap_cflow(cfun, cflow_hash)
562
+ cfun.instance_eval {
563
+ if cflow_hash.has_key?(:break)
564
+ c_if("#{get_wrap_ptr}->state & #{BREAK_FLAG}") {
565
+ l comp_break(:stts => (cflow_hash[:break] ? "res" : false))
566
+ }
567
+ end
568
+ if cflow_hash.has_key?(:next)
569
+ c_if("#{get_wrap_ptr}->state & #{NEXT_FLAG}") {
570
+ l comp_next(:stts => "res")
571
+ }
572
+ end
573
+ if cflow_hash.has_key?(:redo)
574
+ c_if("#{get_wrap_ptr}->state & #{REDO_FLAG}") {
575
+ l comp_redo({})
576
+ }
577
+ end
578
+ if cflow_hash.has_key?(:return)
579
+ c_if("#{get_wrap_ptr}->state & #{RETURN_FLAG}") {
580
+ l comp_return(:stts => "res")
581
+ }
582
+ end
583
+ }
584
+ end
585
+
586
+ def get_self
587
+ self.need_self = true
588
+ "(wrap_ptr->self)"
589
+ end
590
+ def get_cref_impl
591
+ "(wrap_ptr->cref)"
592
+ end
593
+ def get_class
594
+ self.need_class = true
595
+ "(wrap_ptr->s_class)"
596
+ end
597
+
598
+ def get_closure_ary_var
599
+ "(wrap_ptr->my_closure_ary)"
600
+ end
601
+
602
+ def get_wrap_ptr
603
+ "wrap_ptr"
604
+ end
605
+
606
+ [:closure_buid_c_code, :wrap_buid_c_code, :need_closure_ptr].each { |m|
607
+ define_method(m) {
608
+ raise Ruby2CExtError::Bug, "the method #{m} may not be called for an instance of Wrap"
609
+ }
610
+ }
611
+
612
+ def init_c_code
613
+ res = []
614
+ res << "VALUE res;" if need_res
615
+ res.compact.join("\n")
616
+ end
617
+ end
618
+
619
+ end
620
+
621
+ end