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