ruby2cext 0.2.0

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